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/source/
|
||||
/dist/gdb/staging/
|
||||
/doc/qbs/
|
||||
/doc/qtcreator/
|
||||
/doc/qtcreator-dev/
|
||||
/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}
|
||||
{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
|
||||
|
||||
\li \l{http://ant.apache.org/bindownload.cgi}{Apache Ant} 1.8.0,
|
||||
or later
|
||||
|
||||
\li \l{http://www.gradle.org}{Gradle}
|
||||
|
||||
\endlist
|
||||
\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
|
||||
earlier, but Ant support has been deprecated since \QC 4.3 and will
|
||||
be removed in a future version.
|
||||
|
||||
\li A tool chain for building applications for Android devices provided
|
||||
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}
|
||||
|
||||
After installing the Tools, update the Android SDK to get the API
|
||||
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
|
||||
After installing the Tools, you can use the
|
||||
\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:
|
||||
|
||||
@@ -108,33 +96,17 @@
|
||||
|
||||
\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
|
||||
|
||||
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,
|
||||
you must install Qt for Android as part of Qt 5.2, or later.
|
||||
SDK to get the build and platform tools needed for development. Since
|
||||
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}.
|
||||
|
||||
@@ -165,6 +137,9 @@
|
||||
\li To use Ant, add the path to the Ant executable in the
|
||||
\uicontrol {Ant executable} field.
|
||||
|
||||
\note Ant support has been deprecated since \QC 4.3. We
|
||||
recommend that you use Gradle instead.
|
||||
|
||||
\endlist
|
||||
|
||||
\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"
|
||||
|
||||
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}.
|
||||
|
||||
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/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
@@ -55,6 +55,9 @@
|
||||
To specify settings for application packages, select \uicontrol Projects >
|
||||
\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
|
||||
|
||||
To copy Qt libraries and files to the project directory and to bundle them
|
||||
@@ -128,8 +131,9 @@
|
||||
|
||||
\endlist
|
||||
|
||||
The Gradle wrappers and scripts are bundled only if you use
|
||||
\l{Selecting the APK Build Tool}{Gradle} to build the application packages.
|
||||
The Gradle wrappers and scripts are bundled only if you use Gradle to build
|
||||
the application packages. For more information, see
|
||||
\l{Connecting Android Devices}.
|
||||
|
||||
To view the packages that the \c androiddeployqt tool created, select the
|
||||
\uicontrol {Open package location after build} check box.
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
\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
|
||||
\l{Debugging a Qt Quick Example Application}.
|
||||
@@ -55,17 +55,14 @@
|
||||
|
||||
\list 1
|
||||
|
||||
\li Debugging is enabled by default for Qt 4.8, or later. For Qt 4.7,
|
||||
select \uicontrol Projects, and then select the
|
||||
\uicontrol {Enable QML debugging and profiling} check box in the \uicontrol qmake
|
||||
section in \uicontrol {Build Steps}.
|
||||
\li Debugging is enabled by default for Qt 5.0, or later.
|
||||
|
||||
You might have to compile the library first, by selecting the
|
||||
\uicontrol Compile link.
|
||||
|
||||
\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
|
||||
to the application that you are debugging and execute any JavaScript
|
||||
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
|
||||
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
|
||||
\l{Running on Multiple Platforms}{select the corresponding kit for the device}
|
||||
before you start debugging.
|
||||
@@ -107,9 +104,8 @@
|
||||
\li Execute JavaScript expressions to get information about the state of
|
||||
the application
|
||||
|
||||
\li Change QML code and immediately see the changes at runtime
|
||||
|
||||
\li Inspect QML code and change it temporarily at runtime
|
||||
\li Inspect QML properties and JavaScript variables and change them
|
||||
temporarily at runtime
|
||||
|
||||
\endlist
|
||||
|
||||
@@ -121,13 +117,8 @@
|
||||
parameters (if you build the application with \QC, it automatically
|
||||
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:
|
||||
|
||||
@@ -210,7 +201,7 @@
|
||||
\section1 Inspecting User Interfaces
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
\list
|
||||
|
||||
\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
|
||||
Double-click an item in the running application to cycle through the item
|
||||
stack at the cursor position.
|
||||
|
||||
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
|
||||
above folder.
|
||||
|
||||
\section1 Checking JavaScript and QML Syntax
|
||||
\section2 Checking JavaScript and QML Syntax
|
||||
|
||||
To run the checks, select \uicontrol Tools > \uicontrol {QML/JS} >
|
||||
\uicontrol {Run Checks} or press \key {Ctrl+Shift+C}. The results are shown
|
||||
@@ -241,7 +241,7 @@
|
||||
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
|
||||
\l{http://www.jslint.com}{JSLint} tool.
|
||||
@@ -811,6 +811,12 @@
|
||||
To inspect QML and JavaScript properties, methods, and enums, move the
|
||||
cursor over them and select \uicontrol Tools > \uicontrol {QML/JS} >
|
||||
\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}.
|
||||
|
||||
\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,
|
||||
you can test applications on
|
||||
\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.
|
||||
|
||||
You can change the simulated hardware and software version in the run
|
||||
settings for the project. Select \uicontrol Projects > \uicontrol Run, and then select
|
||||
the device to simulate in the \uicontrol {Device type} field.
|
||||
|
||||
\QC uses the Xcode version set as current on the Mac computer.
|
||||
To check the version, enter the following command:
|
||||
\image qtcreator-ios-simulator-deploy.png
|
||||
|
||||
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}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/*!
|
||||
//! [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
|
||||
must create connections from the development host to the device and add the
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/*!
|
||||
//! [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,
|
||||
you must create connections from the development
|
||||
|
||||
@@ -41,29 +41,16 @@
|
||||
{JSON-Based wizards} instead. XML wizards are deprecated and support for
|
||||
them will be removed in future versions of \QC.
|
||||
|
||||
To display the XML-based example wizards in \QC, rename
|
||||
\c {wizard_sample.xml} as \c {wizard.xml} in the
|
||||
\c {\share\qtcreator\templates\wizards\helloworld} and
|
||||
\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.
|
||||
To see examples of XML-based wizards, select \uicontrol File >
|
||||
\uicontrol {New File or Project} > \uicontrol Library. For each wizard, an
|
||||
icon (1), a display name (2), and a description (3) 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}
|
||||
{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.
|
||||
Files can be generated by using \l{Processing Template Files}{templates}.
|
||||
|
||||
XML-based wizard template directories contain an XML configuration file
|
||||
called wizard.xml, the template source files, and optionally, the generator
|
||||
script.
|
||||
called wizard.xml and the template source files.
|
||||
|
||||
\section1 Creating XML-Based Project Wizards
|
||||
|
||||
@@ -71,10 +58,11 @@
|
||||
|
||||
\list 1
|
||||
|
||||
\li Make a copy of the \c {share/qtcreator/templates/wizards/helloworld}
|
||||
or \c {share/qtcreator/templates/wizards/listmodel} folder.
|
||||
\li Make a copy of a folder in the \c share/qtcreator/templates/wizards/
|
||||
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
|
||||
in the \uicontrol New dialog:
|
||||
@@ -89,8 +77,7 @@
|
||||
|
||||
\list
|
||||
|
||||
\li \c version is the version of the file contents. Do not modify
|
||||
this value.
|
||||
\li \c version is the version of the file contents.
|
||||
|
||||
\li \c kind specifies the type of the wizard: \c project or
|
||||
\c class.
|
||||
@@ -146,12 +133,7 @@
|
||||
|
||||
\endlist
|
||||
|
||||
\li Files to be added to the project:
|
||||
|
||||
\list
|
||||
|
||||
\li Template-based: The following code specifies the files to add to
|
||||
the project:
|
||||
\li The following code specifies the files to add to the project:
|
||||
\code
|
||||
<files>
|
||||
<file source="main.cpp" openeditor="true" />
|
||||
@@ -183,41 +165,6 @@
|
||||
|
||||
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:
|
||||
|
||||
\code
|
||||
@@ -600,50 +547,4 @@
|
||||
of the base class. If the validation fails, a red label displaying the
|
||||
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
|
||||
|
||||
\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
|
||||
and click \uicontrol Copy.
|
||||
|
||||
@@ -51,9 +51,8 @@
|
||||
|
||||
\list 1
|
||||
|
||||
\li In \uicontrol Projects, select a project.
|
||||
|
||||
\li Click \uicontrol Dependencies.
|
||||
\li Select \uicontrol Projects > \uicontrol {Project Settings} >
|
||||
\uicontrol Dependencies.
|
||||
|
||||
\li Select projects that must be built before the current project is
|
||||
built.
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
|
||||
\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}.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/*!
|
||||
//! [settings valgrind]
|
||||
|
||||
\section2 Specifying Valgrind Settings
|
||||
\section1 Specifying Valgrind Settings
|
||||
|
||||
\QC integrates \l{Analyzing Code}{Valgrind code analysis tools} for
|
||||
detecting memory leaks and profiling function execution. You can configure
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/*!
|
||||
//! [run settings debugger]
|
||||
|
||||
\section2 Specifying Debugger Settings
|
||||
\section1 Specifying Debugger Settings
|
||||
|
||||
\image qtquick-debugging-settings.png "Debugger Settings"
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/*!
|
||||
//! [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
|
||||
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
|
||||
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
|
||||
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-analyze.qdocinc settings valgrind
|
||||
\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 qnx/creator-projects-settings-run-qnx.qdocinc run settings qnx
|
||||
\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/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
@@ -107,6 +107,9 @@
|
||||
add debuggers to the list. For more information, see
|
||||
\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
|
||||
building the project. You can add Qt versions to the list if they
|
||||
are installed on the development PC, but were not detected
|
||||
@@ -137,6 +140,10 @@
|
||||
\uicontrol Change to edit the variables of the CMake configuration
|
||||
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
|
||||
|
||||
\QC uses the \e {default kit} if it does not have enough information to
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/*!
|
||||
//! [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
|
||||
create connections from the development PC to the device. Click
|
||||
|
||||
@@ -42,14 +42,13 @@
|
||||
. Select the icon to open the toolbar.
|
||||
|
||||
To open toolbars immediately when you select a QML type, select
|
||||
\uicontrol{Tools > Options > Qt Quick > Qt Quick Toolbar > Always show Qt Quick
|
||||
Toolbar}.
|
||||
\uicontrol Tools > \uicontrol Options > \uicontrol {Qt Quick} >
|
||||
\uicontrol {QML/JS Editing} > \uicontrol {Always show Qt Quick Toolbar}.
|
||||
|
||||
Drag the toolbar to pin it to another location. Select
|
||||
\inlineimage qml-toolbar-pin.png
|
||||
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
|
||||
> Pin Quick Toolbar}.
|
||||
by default, select \uicontrol {Pin Quick Toolbar}.
|
||||
|
||||
\section1 Previewing Images
|
||||
|
||||
|
||||
@@ -741,7 +741,7 @@ class Dumper(DumperBase):
|
||||
typeName = "'" + typeName + "'"
|
||||
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
||||
#exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg)
|
||||
addr = value.laddress
|
||||
addr = value.address()
|
||||
if addr is None:
|
||||
addr = self.pokeValue(value)
|
||||
#warn('PTR: %s -> %s(%s)' % (value, function, addr))
|
||||
@@ -750,7 +750,7 @@ class Dumper(DumperBase):
|
||||
result = gdb.parse_and_eval(exp)
|
||||
#warn(' -> %s' % result)
|
||||
res = self.fromNativeValue(result)
|
||||
if value.laddress is None:
|
||||
if value.address() is None:
|
||||
self.releaseValue(addr)
|
||||
return res
|
||||
|
||||
@@ -1056,7 +1056,7 @@ class Dumper(DumperBase):
|
||||
typeName = typeName[0:pos]
|
||||
if typeName in self.qqEditable and not simpleType:
|
||||
#self.qqEditable[typeName](self, expr, value)
|
||||
expr = gdb.parse_and_eval(expr)
|
||||
expr = self.parseAndEvaluate(expr)
|
||||
self.qqEditable[typeName](self, expr, value)
|
||||
else:
|
||||
cmd = 'set variable (%s)=%s' % (expr, value)
|
||||
|
||||
@@ -1558,7 +1558,8 @@ class Dumper(DumperBase):
|
||||
self.target.BreakpointDelete(bp.GetID())
|
||||
res = frame.SetPC(loc.GetLoadAddress())
|
||||
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):
|
||||
result = lldb.SBCommandReturnObject()
|
||||
|
||||
@@ -46,6 +46,11 @@ def qform__QByteArray():
|
||||
return [Latin1StringFormat, SeparateLatin1StringFormat,
|
||||
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):
|
||||
data, size, alloc = d.byteArrayData(value)
|
||||
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):
|
||||
values = data.split(',')
|
||||
size = len(values)
|
||||
d.call('void', value, 'resize', str(size))
|
||||
innerType = value.type[0]
|
||||
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)
|
||||
d.call('void', value, 'resize', str(len(values)))
|
||||
base, vsize, valloc = d.vectorDataHelper(d.extractPointer(value))
|
||||
d.setValues(base, value.type[0].name, values)
|
||||
|
||||
|
||||
def qform__QVector():
|
||||
|
||||
@@ -46,6 +46,7 @@ def qdump__std__complex(d, value):
|
||||
innerType = value.type[0]
|
||||
(real, imag) = value.split('{%s}{%s}' % (innerType.name, innerType.name))
|
||||
d.putValue("(%s, %s)" % (real.display(), imag.display()))
|
||||
d.putNumChild(2)
|
||||
if d.isExpanded():
|
||||
with Children(d, 2, childType=innerType):
|
||||
d.putSubItem("real", real)
|
||||
@@ -887,10 +888,10 @@ def qedit__std__vector(d, value, data):
|
||||
import gdb
|
||||
values = data.split(',')
|
||||
n = len(values)
|
||||
innerType = value.type[0]
|
||||
innerType = value.type[0].name
|
||||
cmd = "set $d = (%s*)calloc(sizeof(%s)*%s,1)" % (innerType, innerType, n)
|
||||
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)
|
||||
cmd = "set (%s[%d])*$d={%s}" % (innerType, n, data)
|
||||
gdb.execute(cmd)
|
||||
@@ -975,11 +976,23 @@ def qdump__std____debug__vector(d, value):
|
||||
|
||||
|
||||
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):
|
||||
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):
|
||||
qdump__std__string(d, value)
|
||||
|
||||
|
||||
@@ -130,8 +130,6 @@ Item {
|
||||
exportMenuItem.enabled = !backendValue.isAttachedProperty()
|
||||
}
|
||||
|
||||
onAboutToHide: menuLoader.active = false
|
||||
|
||||
Controls.MenuItem {
|
||||
text: qsTr("Reset")
|
||||
onTriggered: {
|
||||
|
||||
@@ -20,9 +20,10 @@ QtcProduct {
|
||||
targetName: qtc.ide_app_target
|
||||
version: qtc.qtcreator_version
|
||||
|
||||
installDir: bundle.isBundle ? qtc.ide_app_path : qtc.ide_bin_path
|
||||
installTags: bundle.isBundle ? ["bundle.content"] : base
|
||||
installSourceBase: bundle.isBundle ? buildDirectory : base
|
||||
property bool isBundle: qbs.targetOS.contains("darwin") && bundle.isBundle
|
||||
installDir: isBundle ? qtc.ide_app_path : qtc.ide_bin_path
|
||||
installTags: isBundle ? ["bundle.content"] : base
|
||||
installSourceBase: isBundle ? buildDirectory : base
|
||||
property bool qtcRunnable: true
|
||||
|
||||
cpp.rpaths: qbs.targetOS.contains("macos") ? ["@executable_path/../Frameworks"]
|
||||
|
||||
@@ -1387,6 +1387,8 @@ void ClassOrNamespace::instantiateNestedClasses(ClassOrNamespace *enclosingTempl
|
||||
void ClassOrNamespace::NestedClassInstantiator::instantiate(ClassOrNamespace *enclosingTemplateClass,
|
||||
ClassOrNamespace *enclosingTemplateClassInstantiation)
|
||||
{
|
||||
if (_alreadyConsideredNestedClassInstantiations.size() >= 3)
|
||||
return;
|
||||
if (_alreadyConsideredNestedClassInstantiations.contains(enclosingTemplateClass))
|
||||
return;
|
||||
_alreadyConsideredNestedClassInstantiations.insert(enclosingTemplateClass);
|
||||
|
||||
@@ -326,6 +326,11 @@ static bool allowAutoClosingBrace(const QTextCursor &cursor,
|
||||
|
||||
int prevState;
|
||||
const Tokens tokens = getTokens(cursor, prevState);
|
||||
|
||||
const Token token = tokenAtPosition(tokens, cursor.positionInBlock());
|
||||
if (token.isStringLiteral())
|
||||
return false;
|
||||
|
||||
if (isAfterNamespaceDefinition(tokens, cursor.positionInBlock()))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1781,6 +1781,9 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
|
||||
addMessage(ErrInvalidMember, idPart->identifierToken, propertyName, objectValue->className());
|
||||
return 0;
|
||||
}
|
||||
// resolve references
|
||||
if (const Reference *ref = value->asReference())
|
||||
value = _context->lookupReference(ref);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -104,30 +104,17 @@ namespace Utils {
|
||||
|
||||
struct Context // Basic context containing element name string constants.
|
||||
{
|
||||
Context();
|
||||
|
||||
const QString qtCreatorElement;
|
||||
const QString dataElement;
|
||||
const QString variableElement;
|
||||
const QString typeAttribute;
|
||||
const QString valueElement;
|
||||
const QString valueListElement;
|
||||
const QString valueMapElement;
|
||||
const QString keyAttribute;
|
||||
Context() {}
|
||||
const QString qtCreatorElement = QString("qtcreator");
|
||||
const QString dataElement = QString("data");
|
||||
const QString variableElement = QString("variable");
|
||||
const QString typeAttribute = QString("type");
|
||||
const QString valueElement = QString("value");
|
||||
const QString valueListElement = QString("valuelist");
|
||||
const QString valueMapElement = QString("valuemap");
|
||||
const QString keyAttribute = QString("key");
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
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()),
|
||||
int(r.lineNumber()), qPrintable(r.errorString()));
|
||||
return QVariantMap();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // 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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
QTC_ASSERT(m_model, return);
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
void prependChild(TreeItem *item);
|
||||
void appendChild(TreeItem *item);
|
||||
void insertChild(int pos, TreeItem *item);
|
||||
void removeChildAt(int pos);
|
||||
void removeChildren();
|
||||
void sortChildren(const std::function<bool(const TreeItem *, const TreeItem *)> &cmp);
|
||||
void update();
|
||||
@@ -90,6 +91,7 @@ private:
|
||||
void operator=(const TreeItem &) = delete;
|
||||
|
||||
void clear();
|
||||
void removeItemAt(int pos);
|
||||
void propagateModel(BaseTreeModel *m);
|
||||
|
||||
TreeItem *m_parent; // Not owned.
|
||||
|
||||
@@ -432,6 +432,7 @@ void Wizard::showVariables()
|
||||
label->setWordWrap(true);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
||||
scrollArea->setWidget(label);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
|
||||
layout->addWidget(scrollArea);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
@@ -37,10 +37,10 @@ AndroidQmlProfilerSupport::AndroidQmlProfilerSupport(RunControl *runControl)
|
||||
setDisplayName("AndroidQmlProfilerSupport");
|
||||
|
||||
auto runner = new AndroidRunner(runControl);
|
||||
addDependency(runner);
|
||||
addStartDependency(runner);
|
||||
|
||||
auto profiler = runControl->createWorker(runControl->runMode());
|
||||
profiler->addDependency(this);
|
||||
profiler->addStartDependency(this);
|
||||
|
||||
connect(runner, &AndroidRunner::qmlServerReady, [this, runner, profiler](const QUrl &server) {
|
||||
profiler->recordData("QmlServerUrl", server);
|
||||
|
||||
@@ -142,11 +142,12 @@ bool AndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps)
|
||||
if (!version)
|
||||
return false;
|
||||
|
||||
if (AndroidConfigurations::currentConfig().sdkToolsVersion() >= gradleScriptRevokedSdkVersion &&
|
||||
const QVersionNumber sdkToolsVersion = AndroidConfigurations::currentConfig().sdkToolsVersion();
|
||||
if (sdkToolsVersion >= gradleScriptRevokedSdkVersion &&
|
||||
QVersionNumber::fromString(version->qtVersionString()) < gradleScriptsContainedQtVersion) {
|
||||
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")
|
||||
.arg(gradleScriptRevokedSdkVersion.toString())
|
||||
.arg(sdkToolsVersion.toString())
|
||||
.arg(gradleScriptsContainedQtVersion.toString()), OutputFormat::Stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl)
|
||||
{
|
||||
setDisplayName("AndroidDebugger");
|
||||
m_runner = new AndroidRunner(runControl);
|
||||
addDependency(m_runner);
|
||||
addStartDependency(m_runner);
|
||||
}
|
||||
|
||||
void AndroidDebugSupport::start()
|
||||
|
||||
@@ -295,7 +295,7 @@ QSet<QString> GTestTreeItem::internalTargets() const
|
||||
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
|
||||
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
|
||||
if (projectPart->projectFile == proFile())
|
||||
result.insert(projectPart->buildSystemTarget);
|
||||
result.insert(projectPart->buildSystemTarget + '|' + projectPart->projectFile);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -193,31 +193,10 @@ QList<TestConfiguration *> QuickTestTreeItem::getSelectedTestConfigurations() co
|
||||
|
||||
QuickTestConfiguration *tc = nullptr;
|
||||
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) {
|
||||
const TestTreeItem *child = childItem(row);
|
||||
// unnamed Quick Tests have been handled separately already
|
||||
// unnamed Quick Tests cannot get selected explicitly
|
||||
if (child->name().isEmpty())
|
||||
continue;
|
||||
|
||||
@@ -239,15 +218,8 @@ QList<TestConfiguration *> QuickTestTreeItem::getSelectedTestConfigurations() co
|
||||
if (foundProFiles.contains(child->proFile())) {
|
||||
tc = foundProFiles[child->proFile()];
|
||||
QStringList oldFunctions(tc->testCases());
|
||||
// if oldFunctions.size() is 0 this test configuration is used for at least one
|
||||
// unnamed test case
|
||||
if (oldFunctions.size() == 0) {
|
||||
tc->setTestCaseCount(tc->testCaseCount() + testFunctions.size());
|
||||
tc->setUnnamedOnly(false);
|
||||
} else {
|
||||
oldFunctions << testFunctions;
|
||||
tc->setTestCases(oldFunctions);
|
||||
}
|
||||
oldFunctions << testFunctions;
|
||||
tc->setTestCases(oldFunctions);
|
||||
} else {
|
||||
tc = new QuickTestConfiguration;
|
||||
tc->setTestCases(testFunctions);
|
||||
@@ -321,7 +293,7 @@ QSet<QString> QuickTestTreeItem::internalTargets() const
|
||||
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
|
||||
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
|
||||
if (projectPart->projectFile == proFile()) {
|
||||
result.insert(projectPart->buildSystemTarget);
|
||||
result.insert(projectPart->buildSystemTarget + '|' + projectPart->projectFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/buildtargetinfo.h>
|
||||
#include <projectexplorer/deploymentdata.h>
|
||||
#include <projectexplorer/environmentaspect.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/runnables.h>
|
||||
@@ -40,6 +41,10 @@
|
||||
#include <projectexplorer/session.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.testconfiguration")
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace Autotest {
|
||||
@@ -61,6 +66,13 @@ static bool isLocal(RunConfiguration *runConfiguration)
|
||||
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)
|
||||
{
|
||||
QTC_ASSERT(!m_projectFile.isEmpty(), return);
|
||||
@@ -73,8 +85,12 @@ void TestConfiguration::completeTestInformation(int runMode)
|
||||
Target *target = project->activeTarget();
|
||||
if (!target)
|
||||
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;
|
||||
qCDebug(LOG) << "BuildSystemTargets\n " << buildSystemTargets;
|
||||
const BuildTargetInfo targetInfo
|
||||
= Utils::findOrDefault(target->applicationTargets().list,
|
||||
[&buildSystemTargets] (const BuildTargetInfo &bti) {
|
||||
@@ -82,44 +98,81 @@ void TestConfiguration::completeTestInformation(int runMode)
|
||||
const QStringList targWithProjectFile = b.split('|');
|
||||
if (targWithProjectFile.size() != 2) // some build targets might miss the project file
|
||||
return false;
|
||||
return targWithProjectFile.at(0) == bti.targetName
|
||||
return !bti.targetFilePath.isEmpty() && targWithProjectFile.at(0) == bti.targetName
|
||||
&& targWithProjectFile.at(1).startsWith(bti.projectFilePath.toString());
|
||||
});
|
||||
});
|
||||
QString executable = targetInfo.targetFilePath.toString(); // empty if BTI default created
|
||||
if (Utils::HostOsInfo::isWindowsHost() && !executable.isEmpty()
|
||||
&& !executable.toLower().endsWith(".exe")) {
|
||||
executable = Utils::HostOsInfo::withExecutableSuffix(executable);
|
||||
if (!QTC_GUARD(!targetInfo.targetFilePath.isEmpty())) { // empty if BTI default created
|
||||
qCDebug(LOG) << "BuildTargetInfos";
|
||||
for (const BuildTargetInfo &bti : target->applicationTargets().list)
|
||||
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()) {
|
||||
if (!isLocal(runConfig)) // TODO add device support
|
||||
continue;
|
||||
// deployment information should get taken into account, but it pretty much seems as if
|
||||
// each build system uses it differently
|
||||
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;
|
||||
if (buildSystemTargets.contains(bst)) {
|
||||
Runnable runnable = runConfig->runnable();
|
||||
if (!runnable.is<StandardRunnable>())
|
||||
continue;
|
||||
StandardRunnable stdRunnable = runnable.as<StandardRunnable>();
|
||||
// TODO this might pick up the wrong executable
|
||||
m_executableFile = stdRunnable.executable;
|
||||
if (Utils::HostOsInfo::isWindowsHost() && !m_executableFile.isEmpty()
|
||||
&& !m_executableFile.toLower().endsWith(".exe")) {
|
||||
m_executableFile = Utils::HostOsInfo::withExecutableSuffix(m_executableFile);
|
||||
}
|
||||
qCDebug(LOG) << " LocalExecutable" << localExecutable;
|
||||
qCDebug(LOG) << " DeployedExecutable" << deployedExecutable;
|
||||
qCDebug(LOG) << "Iterating run configurations";
|
||||
for (RunConfiguration *runConfig : target->runConfigurations()) {
|
||||
qCDebug(LOG) << "RunConfiguration" << runConfig->id();
|
||||
if (!isLocal(runConfig)) { // TODO add device support
|
||||
qCDebug(LOG) << " Skipped as not being local";
|
||||
continue;
|
||||
}
|
||||
|
||||
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_workingDir = Utils::FileUtils::normalizePathName(stdRunnable.workingDirectory);
|
||||
m_environment = stdRunnable.environment;
|
||||
m_project = project;
|
||||
if (runMode == TestRunner::Debug)
|
||||
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
|
||||
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
|
||||
if (auto rc = target->activeRunConfiguration()) {
|
||||
if (isLocal(rc)) { // FIXME for now only Desktop support
|
||||
@@ -127,26 +180,21 @@ void TestConfiguration::completeTestInformation(int runMode)
|
||||
if (runnable.is<StandardRunnable>()) {
|
||||
StandardRunnable stdRunnable = runnable.as<StandardRunnable>();
|
||||
m_environment = stdRunnable.environment;
|
||||
m_executableFile = executable;
|
||||
m_project = project;
|
||||
m_guessedConfiguration = true;
|
||||
m_guessedFrom = rc->displayName();
|
||||
if (runMode == TestRunner::Debug)
|
||||
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
|
||||
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());
|
||||
QSet<QString> targets;
|
||||
for (const CppTools::ProjectPart::Ptr part : projectParts)
|
||||
targets.insert(part->buildSystemTarget);
|
||||
targets.insert(part->buildSystemTarget + '|' + part->projectFile);
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ BareMetalDebugSupport::BareMetalDebugSupport(RunControl *runControl)
|
||||
r.commandLineArguments = Utils::QtcProcess::joinArgs(p->arguments(), Utils::HostOsInfo::hostOs());
|
||||
m_gdbServer = new SimpleTargetRunner(runControl);
|
||||
m_gdbServer->setRunnable(r);
|
||||
addDependency(m_gdbServer);
|
||||
addStartDependency(m_gdbServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
#include <cplusplus/Token.h>
|
||||
|
||||
#include <texteditor/completionsettings.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
|
||||
#include <QTextCursor>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
using namespace ClangBackEnd;
|
||||
|
||||
@@ -66,6 +66,14 @@ bool ClangAssistProposalItem::implicitlyApplies() const
|
||||
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,
|
||||
int basePosition) const
|
||||
{
|
||||
@@ -78,12 +86,11 @@ void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface
|
||||
bool setAutoCompleteSkipPos = false;
|
||||
int currentPosition = manipulator.currentPosition();
|
||||
|
||||
bool autoParenthesesEnabled = true;
|
||||
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
|
||||
extraCharacters += QLatin1Char(')');
|
||||
if (m_typedCharacter == QLatin1Char('(')) // Eat the opening parenthesis
|
||||
m_typedCharacter = QChar();
|
||||
} else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) {
|
||||
} else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) {
|
||||
CompletionChunksToTextConverter converter;
|
||||
converter.setupForKeywords();
|
||||
|
||||
@@ -116,7 +123,17 @@ void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface
|
||||
cursor.movePosition(QTextCursor::PreviousWord);
|
||||
while (manipulator.characterAt(cursor.position()) == ':')
|
||||
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)
|
||||
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 (!isOverloaded() && !ccr.hasParameters() && skipClosingParenthesis) {
|
||||
if (!hasOverloadsWithParameters() && !ccr.hasParameters() && skipClosingParenthesis) {
|
||||
extraCharacters += QLatin1Char(')');
|
||||
if (endWithSemicolon) {
|
||||
extraCharacters += semicolon;
|
||||
m_typedCharacter = QChar();
|
||||
}
|
||||
} else if (autoParenthesesEnabled) {
|
||||
} else {
|
||||
const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
|
||||
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
|
||||
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
|
||||
@@ -315,21 +319,21 @@ quint64 ClangAssistProposalItem::hash() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ClangAssistProposalItem::hasOverloadsWithParameters() const
|
||||
{
|
||||
return m_hasOverloadsWithParameters;
|
||||
}
|
||||
|
||||
void ClangAssistProposalItem::setHasOverloadsWithParameters(bool hasOverloadsWithParameters)
|
||||
{
|
||||
m_hasOverloadsWithParameters = hasOverloadsWithParameters;
|
||||
}
|
||||
|
||||
void ClangAssistProposalItem::keepCompletionOperator(unsigned 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)
|
||||
{
|
||||
m_codeCompletion = codeCompletion;
|
||||
@@ -342,4 +346,3 @@ const ClangBackEnd::CodeCompletion &ClangAssistProposalItem::codeCompletion() co
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ public:
|
||||
|
||||
void keepCompletionOperator(unsigned compOp);
|
||||
|
||||
bool isOverloaded() const;
|
||||
void addOverload(const ClangBackEnd::CodeCompletion &ccr);
|
||||
bool hasOverloadsWithParameters() const;
|
||||
void setHasOverloadsWithParameters(bool hasOverloadsWithParameters);
|
||||
|
||||
void setCodeCompletion(const ClangBackEnd::CodeCompletion &codeCompletion);
|
||||
const ClangBackEnd::CodeCompletion &codeCompletion() const;
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
private:
|
||||
ClangBackEnd::CodeCompletion m_codeCompletion;
|
||||
QList<ClangBackEnd::CodeCompletion> m_overloads;
|
||||
bool m_hasOverloadsWithParameters = false;
|
||||
QString m_text;
|
||||
unsigned m_completionOperator;
|
||||
mutable QChar m_typedCharacter;
|
||||
|
||||
@@ -89,7 +89,8 @@ QList<AssistProposalItemInterface *> toAssistProposalItems(const CodeCompletions
|
||||
|
||||
ClangAssistProposalItem *item = items.value(name, 0);
|
||||
if (item) {
|
||||
item->addOverload(codeCompletion);
|
||||
if (codeCompletion.hasParameters())
|
||||
item->setHasOverloadsWithParameters(true);
|
||||
} else {
|
||||
item = new ClangAssistProposalItem;
|
||||
items.insert(name, item);
|
||||
|
||||
@@ -799,7 +799,7 @@ void ClangCodeCompletionTest::testCompleteGlobals()
|
||||
ProjectLessCompletionTest t("globalCompletion.cpp");
|
||||
|
||||
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, "class", "class")); // Keyword
|
||||
QVERIFY(hasSnippet(t.proposal, "class")); // Snippet
|
||||
|
||||
@@ -80,7 +80,11 @@ ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runCont
|
||||
RunConfiguration *runConfiguration = runControl->runConfiguration();
|
||||
auto tool = ClangStaticAnalyzerTool::instance();
|
||||
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);
|
||||
|
||||
ProjectInfo projectInfoBeforeBuild = tool->projectInfoBeforeBuild();
|
||||
@@ -605,8 +609,6 @@ void ClangStaticAnalyzerToolRunner::stop()
|
||||
}
|
||||
m_runners.clear();
|
||||
m_unitsToProcess.clear();
|
||||
appendMessage(tr("Clang Static Analyzer stopped by user."),
|
||||
Utils::NormalMessageFormat);
|
||||
m_progress.reportFinished();
|
||||
ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
|
||||
reportStopped();
|
||||
|
||||
@@ -37,18 +37,16 @@ using namespace CMakeProjectManager;
|
||||
using namespace CMakeProjectManager::Internal;
|
||||
|
||||
CMakeInputsNode::CMakeInputsNode(const Utils::FileName &cmakeLists) :
|
||||
ProjectExplorer::ProjectNode(CMakeInputsNode::inputsPathFromCMakeListsPath(cmakeLists))
|
||||
ProjectExplorer::ProjectNode(cmakeLists, generateId(cmakeLists))
|
||||
{
|
||||
setPriority(Node::DefaultPriority - 10); // Bottom most!
|
||||
setDisplayName(QCoreApplication::translate("CMakeFilesProjectNode", "CMake Modules"));
|
||||
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;
|
||||
result.appendPath("cmakeInputs"); // cmakeLists is a file, so this can not exist on disk
|
||||
return result;
|
||||
return inputFile.toString().toUtf8() + "/cmakeInputs";
|
||||
}
|
||||
|
||||
bool CMakeInputsNode::showInSimpleTree() const
|
||||
@@ -91,13 +89,18 @@ QString CMakeProjectNode::tooltip() const
|
||||
return QString();
|
||||
}
|
||||
|
||||
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory) :
|
||||
ProjectExplorer::ProjectNode(directory)
|
||||
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory, const QString &target) :
|
||||
ProjectExplorer::ProjectNode(directory, generateId(directory, target))
|
||||
{
|
||||
setPriority(Node::DefaultProjectPriority + 900);
|
||||
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
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -35,7 +35,7 @@ class CMakeInputsNode : public ProjectExplorer::ProjectNode
|
||||
public:
|
||||
CMakeInputsNode(const Utils::FileName &cmakeLists);
|
||||
|
||||
static Utils::FileName inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists);
|
||||
static QByteArray generateId(const Utils::FileName &inputFile);
|
||||
|
||||
bool showInSimpleTree() const final;
|
||||
};
|
||||
@@ -60,7 +60,9 @@ public:
|
||||
class CMakeTargetNode : public ProjectExplorer::ProjectNode
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
@@ -250,9 +250,12 @@ static void addCMakeVFolder(FolderNode *base, const Utils::FileName &basePath, i
|
||||
{
|
||||
if (files.isEmpty())
|
||||
return;
|
||||
auto folder = new VirtualFolderNode(basePath, priority);
|
||||
folder->setDisplayName(displayName);
|
||||
base->addNode(folder);
|
||||
FolderNode *folder = base;
|
||||
if (!displayName.isEmpty()) {
|
||||
folder = new VirtualFolderNode(basePath, priority);
|
||||
folder->setDisplayName(displayName);
|
||||
base->addNode(folder);
|
||||
}
|
||||
folder->addNestedNodes(files);
|
||||
for (FolderNode *fn : folder->folderNodes())
|
||||
fn->compress();
|
||||
@@ -268,9 +271,7 @@ static void addCMakeInputs(FolderNode *root,
|
||||
ProjectNode *cmakeVFolder = new CMakeInputsNode(root->filePath());
|
||||
root->addNode(cmakeVFolder);
|
||||
|
||||
addCMakeVFolder(cmakeVFolder, sourceDir, 1000,
|
||||
QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", "<Source Directory>"),
|
||||
sourceInputs);
|
||||
addCMakeVFolder(cmakeVFolder, sourceDir, 1000, QString(), sourceInputs);
|
||||
addCMakeVFolder(cmakeVFolder, buildDir, 100,
|
||||
QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", "<Build Directory>"),
|
||||
buildInputs);
|
||||
@@ -336,7 +337,7 @@ void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps)
|
||||
|
||||
CppTools::RawProjectPart rpp;
|
||||
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.setDefines(defineArg.toUtf8());
|
||||
rpp.setIncludePaths(includes);
|
||||
@@ -757,12 +758,13 @@ static CMakeTargetNode *createTargetNode(const QHash<Utils::FileName, ProjectNod
|
||||
ProjectNode *cmln = cmakeListsNodes.value(dir);
|
||||
QTC_ASSERT(cmln, return nullptr);
|
||||
|
||||
Utils::FileName targetName = dir;
|
||||
targetName.appendPath(".target::" + displayName);
|
||||
QByteArray targetId = CMakeTargetNode::generateId(dir, 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) {
|
||||
tn = new CMakeTargetNode(targetName);
|
||||
tn = new CMakeTargetNode(dir, displayName);
|
||||
cmln->addNode(tn);
|
||||
}
|
||||
tn->setDisplayName(displayName);
|
||||
@@ -849,7 +851,7 @@ void ServerModeReader::addFileGroups(ProjectNode *targetRoot,
|
||||
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, Utils::FileName(), 10, tr("<Other Locations>"), otherFileNodes);
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ void TeaLeafReader::updateCodeModel(CppTools::RawProjectParts &rpps)
|
||||
includePaths += m_parameters.buildDirectory.toString();
|
||||
CppTools::RawProjectPart rpp;
|
||||
rpp.setProjectFileLocation(cbt.sourceDirectory.toString() + "/CMakeLists.txt");
|
||||
rpp.setBuildSystemTarget(cbt.title + '|' + rpp.projectFile);
|
||||
rpp.setBuildSystemTarget(cbt.title);
|
||||
rpp.setIncludePaths(includePaths);
|
||||
|
||||
CppTools::RawProjectPartFlags cProjectFlags;
|
||||
|
||||
@@ -210,6 +210,8 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
|
||||
this, &SearchResultWidget::handleReplaceButton);
|
||||
connect(m_replaceButton, &QAbstractButton::clicked,
|
||||
this, &SearchResultWidget::handleReplaceButton);
|
||||
connect(m_replaceTextEdit, &QLineEdit::textChanged,
|
||||
this, &SearchResultWidget::handleReplaceEditTextChanged);
|
||||
}
|
||||
|
||||
SearchResultWidget::~SearchResultWidget()
|
||||
@@ -304,6 +306,7 @@ void SearchResultWidget::setSupportsReplace(bool replaceSupported, const QString
|
||||
|
||||
void SearchResultWidget::setTextToReplace(const QString &textToReplace)
|
||||
{
|
||||
m_replaceText = textToReplace;
|
||||
m_replaceTextEdit->setText(textToReplace);
|
||||
}
|
||||
|
||||
@@ -406,7 +409,6 @@ void SearchResultWidget::goToPrevious()
|
||||
void SearchResultWidget::restart()
|
||||
{
|
||||
m_replaceTextEdit->setEnabled(false);
|
||||
m_replaceButton->setEnabled(false);
|
||||
m_searchResultTreeView->clear();
|
||||
m_count = 0;
|
||||
Id sizeWarningId(SIZE_WARNING_ID);
|
||||
@@ -416,6 +418,7 @@ void SearchResultWidget::restart()
|
||||
m_searchAgainButton->setVisible(false);
|
||||
m_messageWidget->setVisible(false);
|
||||
updateMatchesFoundLabel();
|
||||
handleReplaceEditTextChanged();
|
||||
emit restarted();
|
||||
}
|
||||
|
||||
@@ -436,7 +439,6 @@ void SearchResultWidget::finishSearch(bool canceled)
|
||||
m_infoBar.removeInfo(sizeWarningId);
|
||||
m_infoBar.enableInfo(sizeWarningId);
|
||||
m_replaceTextEdit->setEnabled(m_count > 0);
|
||||
m_replaceButton->setEnabled(m_count > 0);
|
||||
m_preserveCaseCheck->setEnabled(m_count > 0);
|
||||
m_cancelButton->setVisible(false);
|
||||
m_messageWidget->setVisible(canceled);
|
||||
@@ -461,6 +463,15 @@ void SearchResultWidget::cancelAfterSizeWarning()
|
||||
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)
|
||||
{
|
||||
emit activated(item);
|
||||
|
||||
@@ -115,6 +115,7 @@ private:
|
||||
void setShowReplaceUI(bool visible);
|
||||
void continueAfterSizeWarning();
|
||||
void cancelAfterSizeWarning();
|
||||
void handleReplaceEditTextChanged();
|
||||
|
||||
QList<SearchResultItem> checkedItems() const;
|
||||
void updateMatchesFoundLabel();
|
||||
@@ -141,6 +142,7 @@ private:
|
||||
bool m_isShowingReplaceUI = false;
|
||||
bool m_searchAgainSupported = false;
|
||||
bool m_replaceSupported = false;
|
||||
QString m_replaceText;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
|
||||
@@ -249,8 +249,7 @@ void CppEditorPlugin::test_autoComplete_data()
|
||||
QString expectedText;
|
||||
int skippedChar = 0;
|
||||
|
||||
// We always expect to get a closing char in an empty file
|
||||
if (fc == EmptyFile && isOpeningChar(c))
|
||||
if (fc == EmptyFile && isOpeningChar(c) && c != QLatin1Char('{'))
|
||||
expectedText = closingChar(c);
|
||||
|
||||
if (fc == InBetween) {
|
||||
|
||||
@@ -457,8 +457,6 @@ void CppEditorWidget::renameSymbolUnderCursor()
|
||||
|
||||
void CppEditorWidget::renameSymbolUnderCursorBuiltin()
|
||||
{
|
||||
d->m_useSelectionsUpdater.abortSchedule();
|
||||
|
||||
updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
|
||||
/*updateUseSelectionSynchronously=*/ true);
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ void CppUseSelectionsUpdater::update(CallType callType)
|
||||
|
||||
m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params));
|
||||
} else { // synchronous case
|
||||
abortSchedule();
|
||||
|
||||
const int startRevision = cppEditorDocument->document()->revision();
|
||||
QFuture<CursorInfo> future = cppEditorDocument->cursorInfo(params);
|
||||
|
||||
|
||||
@@ -520,14 +520,14 @@ bool CdbEngine::launchCDB(const DebuggerRunParameters &sp, QString *errorMessage
|
||||
const QFileInfo extensionFi(CdbEngine::extensionLibraryName(cdbIs64Bit));
|
||||
if (!extensionFi.isFile()) {
|
||||
*errorMessage = tr("Internal error: The extension %1 cannot be found.\n"
|
||||
"If you have updated Qt Creator via Maintenance Tool you may "
|
||||
"need to rerun the Tool and select \"Add or remove components\""
|
||||
"If you have updated Qt Creator via Maintenance Tool, you may "
|
||||
"need to rerun the Tool and select \"Add or remove components\" "
|
||||
"and then select the\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"
|
||||
"you will need to build a separate cdbextension with the "
|
||||
"same bitness as the cdb you want to use.").
|
||||
"you will need to build a separate CDB extension with the "
|
||||
"same bitness as the CDB you want to use.").
|
||||
arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1030,39 +1030,37 @@ RunControl *DebuggerEnginePrivate::runControl() const
|
||||
|
||||
void DebuggerEngine::notifyEngineIll()
|
||||
{
|
||||
runControl()->initiateStop();
|
||||
return;
|
||||
//#ifdef WITH_BENCHMARK
|
||||
// CALLGRIND_STOP_INSTRUMENTATION;
|
||||
// CALLGRIND_DUMP_STATS;
|
||||
//#endif
|
||||
// showMessage("NOTE: ENGINE ILL ******");
|
||||
// runTool()->startDying();
|
||||
// d->m_lastGoodState = d->m_state;
|
||||
// switch (state()) {
|
||||
// case InferiorRunRequested:
|
||||
// case InferiorRunOk:
|
||||
// // The engine does not look overly ill right now, so attempt to
|
||||
// // properly interrupt at least once. If that fails, we are on the
|
||||
// // shutdown path due to d->m_targetState anyways.
|
||||
// setState(InferiorStopRequested, true);
|
||||
// showMessage("ATTEMPT TO INTERRUPT INFERIOR");
|
||||
// interruptInferior();
|
||||
// break;
|
||||
// case InferiorStopRequested:
|
||||
// notifyInferiorStopFailed();
|
||||
// break;
|
||||
// case InferiorStopOk:
|
||||
// showMessage("FORWARDING STATE TO InferiorShutdownFailed");
|
||||
// setState(InferiorShutdownFailed, true);
|
||||
// if (isMasterEngine())
|
||||
// d->queueShutdownEngine();
|
||||
// break;
|
||||
// default:
|
||||
// if (isMasterEngine())
|
||||
// d->queueShutdownEngine();
|
||||
// break;
|
||||
// }
|
||||
showMessage("NOTE: ENGINE ILL ******");
|
||||
runTool()->startDying();
|
||||
d->m_lastGoodState = d->m_state;
|
||||
switch (state()) {
|
||||
case InferiorRunRequested:
|
||||
case InferiorRunOk:
|
||||
// The engine does not look overly ill right now, so attempt to
|
||||
// properly interrupt at least once. If that fails, we are on the
|
||||
// shutdown path due to d->m_targetState anyways.
|
||||
setState(InferiorStopRequested, true);
|
||||
showMessage("ATTEMPT TO INTERRUPT INFERIOR");
|
||||
interruptInferior();
|
||||
break;
|
||||
case InferiorStopRequested:
|
||||
notifyInferiorStopFailed();
|
||||
break;
|
||||
case InferiorStopOk:
|
||||
showMessage("FORWARDING STATE TO InferiorShutdownFailed");
|
||||
setState(InferiorShutdownFailed, true);
|
||||
if (isMasterEngine())
|
||||
d->queueShutdownEngine();
|
||||
break;
|
||||
default:
|
||||
if (isMasterEngine())
|
||||
d->queueShutdownEngine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerEngine::notifyEngineSpontaneousShutdown()
|
||||
|
||||
@@ -1351,7 +1351,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
||||
connect(m_registerView, &BaseTreeView::aboutToShow,
|
||||
this, &DebuggerPluginPrivate::reloadRegisters,
|
||||
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->setSettings(settings, "Debugger.StackView");
|
||||
@@ -3561,8 +3561,10 @@ void setPerspectiveEnabled(const QByteArray &perspectiveId, bool enabled)
|
||||
|
||||
void selectPerspective(const QByteArray &perspectiveId)
|
||||
{
|
||||
if (dd->m_mainWindow->currentPerspective() == perspectiveId)
|
||||
if (ModeManager::currentMode() == MODE_DEBUG
|
||||
&& dd->m_mainWindow->currentPerspective() == perspectiveId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Work-around aslong as the GammaRay integration does not use the same setup,
|
||||
if (perspectiveId.isEmpty())
|
||||
|
||||
@@ -256,8 +256,8 @@ DebuggerEngine *createEngine(DebuggerEngineType cppEngineType,
|
||||
if (cppEngine) {
|
||||
engine = createQmlCppEngine(cppEngine, useTerminal);
|
||||
} else {
|
||||
errors->append(DebuggerPlugin::tr("The slave debugging engine required for combined "
|
||||
"QML/C++-Debugging could not be created: %1"));
|
||||
errors->append(DebuggerPlugin::tr("The debugging engine required for combined "
|
||||
"QML/C++ debugging could not be created: %1"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -266,7 +266,7 @@ DebuggerEngine *createEngine(DebuggerEngineType cppEngineType,
|
||||
.arg(engineTypeName(et)));
|
||||
}
|
||||
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)));
|
||||
return engine;
|
||||
}
|
||||
@@ -282,7 +282,7 @@ static bool fixupParameters(DebuggerRunParameters &rp, RunControl *runControl, Q
|
||||
|
||||
// Extract as much as possible from available RunConfiguration.
|
||||
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
|
||||
// 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.
|
||||
if (rp.inferior.executable.isEmpty() && rp.attachPID.isValid()) {
|
||||
if (rp.needFixup && rp.inferior.executable.isEmpty() && rp.attachPID.isValid()) {
|
||||
foreach (const DeviceProcessItem &p, DeviceProcessList::localProcesses()) {
|
||||
if (p.pid == rp.attachPID.pid()) {
|
||||
rp.inferior.executable = p.exe;
|
||||
@@ -313,14 +313,16 @@ static bool fixupParameters(DebuggerRunParameters &rp, RunControl *runControl, Q
|
||||
if (!envBinary.isEmpty())
|
||||
rp.debugger.executable = QString::fromLocal8Bit(envBinary);
|
||||
|
||||
if (auto envAspect = runConfig->extraAspect<EnvironmentAspect>()) {
|
||||
rp.inferior.environment = envAspect->environment(); // Correct.
|
||||
rp.stubEnvironment = rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
|
||||
if (rp.needFixup) {
|
||||
if (auto envAspect = runConfig->extraAspect<EnvironmentAspect>()) {
|
||||
rp.inferior.environment = envAspect->environment(); // Correct.
|
||||
rp.stubEnvironment = rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
|
||||
|
||||
// Copy over DYLD_IMAGE_SUFFIX etc
|
||||
for (auto var : QStringList({"DYLD_IMAGE_SUFFIX", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"}))
|
||||
if (rp.inferior.environment.hasKey(var))
|
||||
rp.debugger.environment.set(var, rp.inferior.environment.value(var));
|
||||
// Copy over DYLD_IMAGE_SUFFIX etc
|
||||
for (auto var : QStringList({"DYLD_IMAGE_SUFFIX", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"}))
|
||||
if (rp.inferior.environment.hasKey(var))
|
||||
rp.debugger.environment.set(var, rp.inferior.environment.value(var));
|
||||
}
|
||||
}
|
||||
if (Project *project = runConfig->target()->project()) {
|
||||
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.
|
||||
Project *project = SessionManager::startupProject();
|
||||
Target *target = project ? project->target(kit) : nullptr;
|
||||
if (!target) {
|
||||
if (!target || !target->activeRunConfiguration()) {
|
||||
project = new DummyProject;
|
||||
target = project->createTarget(kit);
|
||||
}
|
||||
@@ -685,7 +687,7 @@ void GdbServerPortsGatherer::handlePortListReady()
|
||||
return;
|
||||
}
|
||||
}
|
||||
reportStarted();
|
||||
reportDone();
|
||||
}
|
||||
|
||||
// GdbServerRunner
|
||||
@@ -731,7 +733,7 @@ void GdbServerRunner::start()
|
||||
|
||||
setRunnable(r);
|
||||
|
||||
appendMessage(tr("Starting GDBserver..."), NormalMessageFormat);
|
||||
appendMessage(tr("Starting gdbserver..."), NormalMessageFormat);
|
||||
|
||||
SimpleTargetRunner::start();
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
Utils::ProcessHandle attachPID;
|
||||
QStringList solibSearchPath;
|
||||
bool useTerminal = false;
|
||||
bool needFixup = true; // FIXME: Make false the default...
|
||||
|
||||
// Used by Qml debugging.
|
||||
TcpServerConnection qmlServer;
|
||||
|
||||
@@ -430,7 +430,7 @@ LogWindow::LogWindow(QWidget *parent)
|
||||
setMinimumHeight(60);
|
||||
|
||||
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. "
|
||||
"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 "
|
||||
|
||||
@@ -856,7 +856,7 @@ void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
|
||||
QFETCH(bool, lastChunk);
|
||||
QFETCH(QString, patchText);
|
||||
|
||||
QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);
|
||||
const QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);
|
||||
|
||||
QCOMPARE(result, patchText);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "differ.h"
|
||||
|
||||
#include "texteditor/fontsettings.h"
|
||||
#include "utils/asconst.h"
|
||||
|
||||
#include <QFutureInterfaceBase>
|
||||
#include <QRegularExpression>
|
||||
@@ -485,6 +486,34 @@ QString DiffUtils::makePatch(const ChunkData &chunkData,
|
||||
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 diffText;
|
||||
@@ -496,26 +525,36 @@ QString DiffUtils::makePatch(const QList<FileData> &fileDataList, unsigned forma
|
||||
str << "diff --git a/" << fileData.leftFileInfo.fileName
|
||||
<< " 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) {
|
||||
str << "Binary files ";
|
||||
if (formatFlags & AddLevel)
|
||||
str << "a/";
|
||||
str << fileData.leftFileInfo.fileName << " and ";
|
||||
if (formatFlags & AddLevel)
|
||||
str << "b/";
|
||||
str << fileData.rightFileInfo.fileName << " differ\n";
|
||||
str << leftFileName(fileData, formatFlags);
|
||||
str << " and ";
|
||||
str << rightFileName(fileData, formatFlags);
|
||||
str << " differ\n";
|
||||
} else {
|
||||
str << "--- ";
|
||||
if (formatFlags & AddLevel)
|
||||
str << "a/";
|
||||
str << fileData.leftFileInfo.fileName << "\n+++ ";
|
||||
if (formatFlags & AddLevel)
|
||||
str << "b/";
|
||||
str << fileData.rightFileInfo.fileName << '\n';
|
||||
for (int j = 0; j < fileData.chunks.count(); j++) {
|
||||
str << makePatch(fileData.chunks.at(j),
|
||||
(j == fileData.chunks.count() - 1)
|
||||
&& fileData.lastChunkAtTheEndOfFile);
|
||||
if (!fileData.chunks.isEmpty()) {
|
||||
str << "--- ";
|
||||
str << leftFileName(fileData, formatFlags) << "\n";
|
||||
str << "+++ ";
|
||||
str << rightFileName(fileData, formatFlags) << "\n";
|
||||
for (int j = 0; j < fileData.chunks.count(); j++) {
|
||||
str << makePatch(fileData.chunks.at(j),
|
||||
(j == fileData.chunks.count() - 1)
|
||||
&& fileData.lastChunkAtTheEndOfFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -695,58 +734,128 @@ static QList<RowData> readLines(QStringRef patch,
|
||||
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,
|
||||
bool *lastChunkAtTheEndOfFile,
|
||||
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;
|
||||
int position = -1;
|
||||
|
||||
QRegularExpressionMatch match = chunkRegExp.match(patch);
|
||||
if (match.hasMatch() && match.capturedStart() == 0) {
|
||||
int endOfLastChunk = 0;
|
||||
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());
|
||||
QVector<int> startingPositions; // store starting positions of @@
|
||||
if (patch.startsWith(QStringLiteral("@@ -")))
|
||||
startingPositions.append(position + 1);
|
||||
|
||||
if (endOfLastChunk > 0) {
|
||||
QStringRef lines = patch.mid(endOfLastChunk);
|
||||
chunkDataList.last().rows = readLines(lines,
|
||||
true,
|
||||
lastChunkAtTheEndOfFile,
|
||||
&readOk);
|
||||
}
|
||||
while ((position = patch.indexOf(QStringLiteral("\n@@ -"), position + 1)) >= 0)
|
||||
startingPositions.append(position + 1);
|
||||
|
||||
const QChar newLine('\n');
|
||||
bool readOk = true;
|
||||
|
||||
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)
|
||||
@@ -881,293 +990,315 @@ static QList<FileData> readDiffPatch(QStringRef patch,
|
||||
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,
|
||||
const QString &fileName,
|
||||
bool *ok)
|
||||
static bool extractCommonFileName(QStringRef fileNames, QStringRef *fileName)
|
||||
{
|
||||
FileData fileData;
|
||||
fileData.leftFileInfo.fileName = fileName;
|
||||
fileData.rightFileInfo.fileName = fileName;
|
||||
// we should have 1 space between filenames
|
||||
if (fileNames.size() % 2 == 0)
|
||||
return false;
|
||||
|
||||
QStringRef patch = headerAndChunks;
|
||||
bool readOk = false;
|
||||
if (!fileNames.startsWith(QStringLiteral("a/")))
|
||||
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 @@
|
||||
// new file mode octal
|
||||
const QRegularExpression newFileMode("^new file mode \\d+\\n");
|
||||
// drop "a/"
|
||||
const QStringRef leftFileName = fileNames.mid(2, fileNameSize - 2);
|
||||
|
||||
// will be followed by: index shasha..0000000, file "b" replaced by "/dev/null", @@ -m,n +0,0 @@
|
||||
// deleted file mode octal
|
||||
const QRegularExpression deletedFileMode("^deleted file mode \\d+\\n");
|
||||
// drop the first filename + " b/"
|
||||
const QStringRef rightFileName = fileNames.mid(fileNameSize + 3, fileNameSize - 2);
|
||||
|
||||
const QRegularExpression modeChangeRegExp("^old mode \\d+\\nnew mode \\d+\\n");
|
||||
if (leftFileName != rightFileName)
|
||||
return false;
|
||||
|
||||
// index cap1..cap2(optionally: octal)
|
||||
const QRegularExpression indexRegExp("^index (\\w+)\\.{2}(\\w+)(?: \\d+)?(\\n|$)");
|
||||
*fileName = leftFileName;
|
||||
return true;
|
||||
}
|
||||
|
||||
QString leftFileName = QLatin1String("a/") + fileName;
|
||||
QString rightFileName = QLatin1String("b/") + fileName;
|
||||
static bool detectFileData(QStringRef patch,
|
||||
FileData *fileData,
|
||||
QStringRef *remainingPatch) {
|
||||
bool hasNewLine;
|
||||
|
||||
const QRegularExpressionMatch newFileMatch = newFileMode.match(patch);
|
||||
if (newFileMatch.hasMatch() && newFileMatch.capturedStart() == 0) {
|
||||
fileData.fileOperation = FileData::NewFile;
|
||||
leftFileName = devNull;
|
||||
patch = patch.mid(newFileMatch.capturedEnd());
|
||||
} else {
|
||||
const QRegularExpressionMatch deletedFileMatch = deletedFileMode.match(patch);
|
||||
if (deletedFileMatch.hasMatch() && deletedFileMatch.capturedStart() == 0) {
|
||||
fileData.fileOperation = FileData::DeleteFile;
|
||||
rightFileName = devNull;
|
||||
patch = patch.mid(deletedFileMatch.capturedEnd());
|
||||
QStringRef afterDiffGit;
|
||||
// diff --git a/leftFileName b/rightFileName
|
||||
const QStringRef diffGit = readLine(patch, &afterDiffGit, &hasNewLine);
|
||||
if (!hasNewLine)
|
||||
return false; // we need to have at least one more line
|
||||
|
||||
const QLatin1String gitHeader("diff --git ");
|
||||
const QStringRef fileNames = diffGit.mid(gitHeader.size());
|
||||
QStringRef commonFileName;
|
||||
if (extractCommonFileName(fileNames, &commonFileName)) {
|
||||
// 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 {
|
||||
const QRegularExpressionMatch modeChangeMatch = modeChangeRegExp.match(patch);
|
||||
if (modeChangeMatch.hasMatch() && modeChangeMatch.capturedStart() == 0) {
|
||||
patch = patch.mid(modeChangeMatch.capturedEnd());
|
||||
}
|
||||
*remainingPatch = afterDiffGit;
|
||||
}
|
||||
}
|
||||
|
||||
const QRegularExpressionMatch indexMatch = indexRegExp.match(patch);
|
||||
if (indexMatch.hasMatch() && indexMatch.capturedStart() == 0) {
|
||||
fileData.leftFileInfo.typeInfo = indexMatch.captured(1);
|
||||
fileData.rightFileInfo.typeInfo = indexMatch.captured(2);
|
||||
} else {
|
||||
// copy / rename
|
||||
|
||||
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
|
||||
+ QLatin1String(" and ") + rightFileName + QLatin1String(" differ");
|
||||
const QString leftStart = QString::fromLatin1("--- ") + leftFileName;
|
||||
QChar leftFollow = patch.count() > leftStart.count() ? patch.at(leftStart.count()) : QLatin1Char('\n');
|
||||
// TODO: validate similarity line
|
||||
|
||||
// empty or followed either by leftFileRegExp or by binaryRegExp
|
||||
if (patch.isEmpty() && (fileData.fileOperation == FileData::NewFile
|
||||
|| fileData.fileOperation == FileData::DeleteFile)) {
|
||||
readOk = true;
|
||||
} else if (patch.startsWith(leftStart) && fileNameEnd(leftFollow)) {
|
||||
patch = patch.mid(patch.indexOf(QLatin1Char('\n'), leftStart.count()) + 1);
|
||||
QStringRef afterCopyRenameFrom;
|
||||
// [copy / rename] from leftFileName
|
||||
const QStringRef copyRenameFrom = readLine(afterSimilarity, &afterCopyRenameFrom, &hasNewLine);
|
||||
if (!hasNewLine)
|
||||
return false; // we need to have at least one more line
|
||||
|
||||
const QString rightStart = QString::fromLatin1("+++ ") + 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);
|
||||
const QLatin1String copyFrom("copy from ");
|
||||
const QLatin1String renameFrom("rename from ");
|
||||
if (copyRenameFrom.startsWith(copyFrom)) {
|
||||
fileData->fileOperation = FileData::CopyFile;
|
||||
fileData->leftFileInfo.fileName = copyRenameFrom.mid(copyFrom.size()).toString();
|
||||
} else if (copyRenameFrom.startsWith(renameFrom)) {
|
||||
fileData->fileOperation = FileData::RenameFile;
|
||||
fileData->leftFileInfo.fileName = copyRenameFrom.mid(renameFrom.size()).toString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (patch == binaryLine) {
|
||||
readOk = true;
|
||||
fileData.binaryFiles = true;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
QStringRef afterCopyRenameTo;
|
||||
// [copy / rename] to rightFileName
|
||||
const QStringRef copyRenameTo = readLine(afterCopyRenameFrom, &afterCopyRenameTo, &hasNewLine);
|
||||
|
||||
if (!readOk)
|
||||
return FileData();
|
||||
// if (dis)similarity index is 100% we don't have more lines
|
||||
|
||||
return fileData;
|
||||
}
|
||||
|
||||
static FileData readCopyRenameChunks(QStringRef copyRenameChunks,
|
||||
FileData::FileOperation fileOperation,
|
||||
const QString &leftFileName,
|
||||
const QString &rightFileName,
|
||||
bool *ok)
|
||||
{
|
||||
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;
|
||||
const QLatin1String copyTo("copy to ");
|
||||
const QLatin1String renameTo("rename to ");
|
||||
if (fileData->fileOperation == FileData::CopyFile && copyRenameTo.startsWith(copyTo)) {
|
||||
fileData->rightFileInfo.fileName = copyRenameTo.mid(copyTo.size()).toString();
|
||||
} else if (fileData->fileOperation == FileData::RenameFile && copyRenameTo.startsWith(renameTo)) {
|
||||
fileData->rightFileInfo.fileName = copyRenameTo.mid(renameTo.size()).toString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
*remainingPatch = afterCopyRenameTo;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
if (!readOk)
|
||||
return FileData();
|
||||
|
||||
return fileData;
|
||||
return detectIndexAndBinary(*remainingPatch, fileData, remainingPatch);
|
||||
}
|
||||
|
||||
static QList<FileData> readGitPatch(QStringRef patch, bool *ok,
|
||||
QFutureInterfaceBase *jobController)
|
||||
{
|
||||
int position = -1;
|
||||
|
||||
const QRegularExpression simpleGitRegExp(
|
||||
"^diff --git a/([^\\n]+) b/\\1\\n" // diff --git a/cap1 b/cap1
|
||||
, QRegularExpression::MultilineOption);
|
||||
QVector<int> startingPositions; // store starting positions of git headers
|
||||
if (patch.startsWith(QStringLiteral("diff --git ")))
|
||||
startingPositions.append(position + 1);
|
||||
|
||||
const QRegularExpression similarityRegExp(
|
||||
"^diff --git a/([^\\n]+) b/([^\\n]+)\\n" // diff --git a/cap1 b/cap2
|
||||
"(?: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);
|
||||
while ((position = patch.indexOf(QStringLiteral("\ndiff --git "), position + 1)) >= 0)
|
||||
startingPositions.append(position + 1);
|
||||
|
||||
bool readOk = false;
|
||||
|
||||
QList<FileData> fileDataList;
|
||||
|
||||
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;
|
||||
class PatchInfo {
|
||||
public:
|
||||
QStringRef patch;
|
||||
FileData fileData;
|
||||
};
|
||||
|
||||
// Set both pos and simpleGitMatched according to the first match:
|
||||
calculateGitMatchAndPosition();
|
||||
const QChar newLine('\n');
|
||||
bool readOk = true;
|
||||
|
||||
if (pos >= 0) { // git style patch
|
||||
readOk = true;
|
||||
int endOfLastHeader = 0;
|
||||
QString lastLeftFileName;
|
||||
QString lastRightFileName;
|
||||
FileData::FileOperation lastOperation = FileData::ChangeFile;
|
||||
QVector<PatchInfo> patches;
|
||||
const int count = startingPositions.count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (jobController && jobController->isCanceled())
|
||||
return QList<FileData>();
|
||||
|
||||
auto collectFileData = [&]() {
|
||||
if (endOfLastHeader > 0 && readOk) {
|
||||
const int end = pos < 0 ? patch.count() : pos;
|
||||
QStringRef headerAndChunks = patch.mid(endOfLastHeader,
|
||||
qMax(end - endOfLastHeader - 1, 0));
|
||||
const int diffStart = startingPositions.at(i);
|
||||
const int diffEnd = (i < count - 1)
|
||||
// drop the newline before the next header start
|
||||
? startingPositions.at(i + 1) - 1
|
||||
// drop the possible newline by the end of file
|
||||
: (patch.at(patch.count() - 1) == newLine ? patch.count() - 1 : patch.count());
|
||||
|
||||
FileData fileData;
|
||||
if (lastOperation == FileData::ChangeFile) {
|
||||
fileData = readGitHeaderAndChunks(headerAndChunks,
|
||||
lastLeftFileName,
|
||||
&readOk);
|
||||
} else {
|
||||
fileData = readCopyRenameChunks(headerAndChunks,
|
||||
lastOperation,
|
||||
lastLeftFileName,
|
||||
lastRightFileName,
|
||||
&readOk);
|
||||
}
|
||||
if (readOk)
|
||||
fileDataList.append(fileData);
|
||||
}
|
||||
};
|
||||
// extract the patch for just one file
|
||||
const QStringRef fileDiff = patch.mid(diffStart, diffEnd - diffStart);
|
||||
|
||||
do {
|
||||
if (jobController && jobController->isCanceled())
|
||||
FileData fileData;
|
||||
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>();
|
||||
jobController->setProgressValue(i++);
|
||||
}
|
||||
|
||||
collectFileData();
|
||||
if (!readOk)
|
||||
break;
|
||||
FileData fileData = patchInfo.fileData;
|
||||
if (!patchInfo.patch.isEmpty() || fileData.fileOperation == FileData::ChangeFile)
|
||||
fileData.chunks = readChunks(patchInfo.patch, &fileData.lastChunkAtTheEndOfFile, &readOk);
|
||||
else
|
||||
readOk = true;
|
||||
|
||||
if (simpleGitMatched) {
|
||||
const QString fileName = simpleGitMatch.captured(1);
|
||||
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;
|
||||
if (!readOk)
|
||||
break;
|
||||
|
||||
// give both pos and simpleGitMatched a new value for the next match
|
||||
calculateGitMatchAndPosition();
|
||||
} while (pos != -1);
|
||||
|
||||
if (readOk)
|
||||
collectFileData();
|
||||
fileDataList.append(fileData);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
|
||||
@@ -112,6 +112,7 @@ protected:
|
||||
QColor replacementPenColor(int blockNumber) const override;
|
||||
QString plainTextFromSelection(const QTextCursor &cursor) const override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void scrollContentsBy(int dx, int dy) override;
|
||||
@@ -393,6 +394,16 @@ void SideDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *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)
|
||||
{
|
||||
QPointer<QMenu> menu = createStandardContextMenu();
|
||||
|
||||
@@ -161,6 +161,16 @@ void UnifiedDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *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)
|
||||
{
|
||||
QPointer<QMenu> menu = createStandardContextMenu();
|
||||
|
||||
@@ -74,6 +74,7 @@ signals:
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
QString lineNumber(int blockNumber) const override;
|
||||
int lineNumberDigits() const override;
|
||||
|
||||
@@ -394,10 +394,10 @@ IosQmlProfilerSupport::IosQmlProfilerSupport(RunControl *runControl)
|
||||
|
||||
m_runner = new IosRunner(runControl);
|
||||
m_runner->setQmlDebugging(QmlDebug::QmlProfilerServices);
|
||||
addDependency(m_runner);
|
||||
addStartDependency(m_runner);
|
||||
|
||||
m_profiler = runControl->createWorker(runControl->runMode());
|
||||
m_profiler->addDependency(this);
|
||||
m_profiler->addStartDependency(this);
|
||||
}
|
||||
|
||||
void IosQmlProfilerSupport::start()
|
||||
@@ -428,7 +428,7 @@ IosDebugSupport::IosDebugSupport(RunControl *runControl)
|
||||
m_runner->setCppDebugging(isCppDebugging());
|
||||
m_runner->setQmlDebugging(isQmlDebugging() ? QmlDebug::QmlDebuggerServices : QmlDebug::NoQmlDebugServices);
|
||||
|
||||
addDependency(m_runner);
|
||||
addStartDependency(m_runner);
|
||||
}
|
||||
|
||||
void IosDebugSupport::start()
|
||||
|
||||
@@ -185,7 +185,7 @@ void IosSettingsWidget::onReset()
|
||||
|
||||
const int userInput = QMessageBox::question(this, tr("Reset"),
|
||||
tr("Do you really want to reset the contents and settings"
|
||||
" of the selected devices", "",
|
||||
" of the selected devices?", "",
|
||||
simulatorInfoList.count()));
|
||||
if (userInput == QMessageBox::No)
|
||||
return;
|
||||
@@ -243,7 +243,7 @@ void IosSettingsWidget::onDelete()
|
||||
|
||||
const int userInput = QMessageBox::question(this, tr("Delete Device"),
|
||||
tr("Do you really want to delete the selected "
|
||||
"devices", "", simulatorInfoList.count()));
|
||||
"devices?", "", simulatorInfoList.count()));
|
||||
if (userInput == QMessageBox::No)
|
||||
return;
|
||||
|
||||
|
||||
@@ -37,11 +37,13 @@
|
||||
#include <ssh/sshconnection.h>
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/portlist.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
using namespace ProjectExplorer::Constants;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ProjectExplorer {
|
||||
|
||||
@@ -137,6 +139,104 @@ DeviceEnvironmentFetcher::Ptr DesktopDevice::environmentFetcher() const
|
||||
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 url;
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
bool canCreateProcessModel() const override;
|
||||
DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||
bool canCreateProcess() const override { return true; }
|
||||
ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override;
|
||||
DeviceProcess *createProcess(QObject *parent) const override;
|
||||
DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "deviceprocess.h"
|
||||
#include "deviceusedportsgatherer.h"
|
||||
|
||||
#include <projectexplorer/runnables.h>
|
||||
|
||||
#include <utils/port.h>
|
||||
#include <utils/portlist.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <ssh/sshconnection.h>
|
||||
#include <ssh/sshconnectionmanager.h>
|
||||
#include <ssh/sshremoteprocess.h>
|
||||
|
||||
using namespace QSsh;
|
||||
using namespace Utils;
|
||||
@@ -41,12 +41,12 @@ namespace Internal {
|
||||
class DeviceUsedPortsGathererPrivate
|
||||
{
|
||||
public:
|
||||
SshConnection *connection;
|
||||
SshRemoteProcess::Ptr process;
|
||||
QPointer<DeviceProcess> process;
|
||||
QList<Port> usedPorts;
|
||||
QByteArray remoteStdout;
|
||||
QByteArray remoteStderr;
|
||||
IDevice::ConstPtr device;
|
||||
PortsGatheringMethod::Ptr portsGatheringMethod;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
@@ -54,7 +54,6 @@ class DeviceUsedPortsGathererPrivate
|
||||
DeviceUsedPortsGatherer::DeviceUsedPortsGatherer(QObject *parent) :
|
||||
QObject(parent), d(new Internal::DeviceUsedPortsGathererPrivate)
|
||||
{
|
||||
d->connection = 0;
|
||||
}
|
||||
|
||||
DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
|
||||
@@ -65,50 +64,36 @@ DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
|
||||
|
||||
void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device)
|
||||
{
|
||||
QTC_ASSERT(!d->connection, return);
|
||||
QTC_ASSERT(device && device->portsGatheringMethod(), return);
|
||||
|
||||
d->device = device;
|
||||
d->connection = QSsh::acquireConnection(device->sshParameters());
|
||||
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();
|
||||
}
|
||||
QTC_ASSERT(d->device, emit error("No device given"); return);
|
||||
|
||||
void DeviceUsedPortsGatherer::handleConnectionEstablished()
|
||||
{
|
||||
const QAbstractSocket::NetworkLayerProtocol protocol
|
||||
= d->connection->connectionInfo().localAddress.protocol();
|
||||
const QByteArray commandLine = d->device->portsGatheringMethod()->commandLine(protocol);
|
||||
d->process = d->connection->createRemoteProcess(commandLine);
|
||||
d->portsGatheringMethod = d->device->portsGatheringMethod();
|
||||
QTC_ASSERT(d->portsGatheringMethod, emit error("Not implemented"); return);
|
||||
|
||||
connect(d->process.data(), &SshRemoteProcess::closed, this, &DeviceUsedPortsGatherer::handleProcessClosed);
|
||||
connect(d->process.data(), &SshRemoteProcess::readyReadStandardOutput, this, &DeviceUsedPortsGatherer::handleRemoteStdOut);
|
||||
connect(d->process.data(), &SshRemoteProcess::readyReadStandardError, this, &DeviceUsedPortsGatherer::handleRemoteStdErr);
|
||||
const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol;
|
||||
d->process = d->device->createProcess(this);
|
||||
|
||||
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()
|
||||
{
|
||||
if (!d->connection)
|
||||
return;
|
||||
d->usedPorts.clear();
|
||||
d->remoteStdout.clear();
|
||||
d->remoteStderr.clear();
|
||||
if (d->process)
|
||||
disconnect(d->process.data(), 0, this, 0);
|
||||
d->process.clear();
|
||||
disconnect(d->connection, 0, this, 0);
|
||||
QSsh::releaseConnection(d->connection);
|
||||
d->connection = 0;
|
||||
}
|
||||
|
||||
Port DeviceUsedPortsGatherer::getNextFreePort(PortList *freePorts) const
|
||||
@@ -129,7 +114,7 @@ QList<Port> DeviceUsedPortsGatherer::usedPorts() const
|
||||
void DeviceUsedPortsGatherer::setupUsedPorts()
|
||||
{
|
||||
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) {
|
||||
if (d->device->freePorts().contains(port))
|
||||
d->usedPorts << port;
|
||||
@@ -137,27 +122,23 @@ void DeviceUsedPortsGatherer::setupUsedPorts()
|
||||
emit portListReady();
|
||||
}
|
||||
|
||||
void DeviceUsedPortsGatherer::handleConnectionError()
|
||||
void DeviceUsedPortsGatherer::handleProcessError()
|
||||
{
|
||||
if (!d->connection)
|
||||
return;
|
||||
emit error(tr("Connection error: %1").arg(d->connection->errorString()));
|
||||
emit error(tr("Connection error: %1").arg(d->process->errorString()));
|
||||
stop();
|
||||
}
|
||||
|
||||
void DeviceUsedPortsGatherer::handleProcessClosed(int exitStatus)
|
||||
void DeviceUsedPortsGatherer::handleProcessFinished()
|
||||
{
|
||||
if (!d->connection)
|
||||
if (!d->process)
|
||||
return;
|
||||
QString errMsg;
|
||||
QProcess::ExitStatus exitStatus = d->process->exitStatus();
|
||||
switch (exitStatus) {
|
||||
case SshRemoteProcess::FailedToStart:
|
||||
errMsg = tr("Could not start remote process: %1").arg(d->process->errorString());
|
||||
break;
|
||||
case SshRemoteProcess::CrashExit:
|
||||
case QProcess::CrashExit:
|
||||
errMsg = tr("Remote process crashed: %1").arg(d->process->errorString());
|
||||
break;
|
||||
case SshRemoteProcess::NormalExit:
|
||||
case QProcess::NormalExit:
|
||||
if (d->process->exitCode() == 0)
|
||||
setupUsedPorts();
|
||||
else
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
namespace ProjectExplorer {
|
||||
namespace Internal { class DeviceUsedPortsGathererPrivate; }
|
||||
class StandardRunnable;
|
||||
|
||||
class PROJECTEXPLORER_EXPORT DeviceUsedPortsGatherer : public QObject
|
||||
{
|
||||
@@ -42,7 +43,7 @@ public:
|
||||
DeviceUsedPortsGatherer(QObject *parent = 0);
|
||||
~DeviceUsedPortsGatherer() override;
|
||||
|
||||
void start(const ProjectExplorer::IDevice::ConstPtr &device);
|
||||
void start(const IDevice::ConstPtr &device);
|
||||
void stop();
|
||||
Utils::Port getNextFreePort(Utils::PortList *freePorts) const; // returns -1 if no more are left
|
||||
QList<Utils::Port> usedPorts() const;
|
||||
@@ -52,11 +53,10 @@ signals:
|
||||
void portListReady();
|
||||
|
||||
private:
|
||||
void handleConnectionEstablished();
|
||||
void handleConnectionError();
|
||||
void handleProcessClosed(int exitStatus);
|
||||
void handleRemoteStdOut();
|
||||
void handleRemoteStdErr();
|
||||
void handleProcessError();
|
||||
void handleProcessFinished();
|
||||
|
||||
void setupUsedPorts();
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ class Connection;
|
||||
class DeviceProcess;
|
||||
class DeviceProcessList;
|
||||
class Kit;
|
||||
class Runnable;
|
||||
class RunControl;
|
||||
class RunWorker;
|
||||
|
||||
@@ -110,7 +111,7 @@ public:
|
||||
typedef QSharedPointer<const PortsGatheringMethod> Ptr;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -410,6 +410,54 @@ void ToolChainKitInformation::setToolChain(Kit *k, ToolChain *tc)
|
||||
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)
|
||||
{
|
||||
QTC_ASSERT(language.isValid(), return);
|
||||
|
||||
@@ -98,6 +98,7 @@ public:
|
||||
static ToolChain *toolChain(const Kit *k, Core::Id language);
|
||||
static QList<ToolChain *> toolChains(const Kit *k);
|
||||
static void setToolChain(Kit *k, ToolChain *tc);
|
||||
static void setAllToolChainsToMatch(Kit *k, ToolChain *tc);
|
||||
static void clearToolChain(Kit *k, Core::Id language);
|
||||
static Abi targetAbi(const Kit *k);
|
||||
|
||||
|
||||
@@ -212,7 +212,10 @@ void FlatModel::addOrRebuildProjectModel(Project *project)
|
||||
|
||||
if (ProjectNode *projectNode = project->rootProjectNode()) {
|
||||
addFolderNode(container, projectNode, &seen);
|
||||
} else {
|
||||
if (m_trimEmptyDirectories)
|
||||
trimEmptyDirectories(container);
|
||||
}
|
||||
if (container->childCount() == 0) {
|
||||
FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false);
|
||||
seen.insert(projectFileNode);
|
||||
container->appendChild(new WrapperNode(projectFileNode));
|
||||
@@ -344,7 +347,7 @@ void FlatModel::addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<
|
||||
addFolderNode(parent, subFolderNode, seen);
|
||||
}
|
||||
} 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)) {
|
||||
seen->insert(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
|
||||
{
|
||||
return Qt::MoveAction;
|
||||
@@ -399,10 +414,20 @@ void FlatModel::setProjectFilterEnabled(bool filter)
|
||||
|
||||
void FlatModel::setGeneratedFilesFilterEnabled(bool filter)
|
||||
{
|
||||
if (filter == m_filterGeneratedFiles)
|
||||
return;
|
||||
m_filterGeneratedFiles = filter;
|
||||
rebuildModel();
|
||||
}
|
||||
|
||||
void FlatModel::setTrimEmptyDirectories(bool filter)
|
||||
{
|
||||
if (filter == m_trimEmptyDirectories)
|
||||
return;
|
||||
m_trimEmptyDirectories = filter;
|
||||
rebuildModel();
|
||||
}
|
||||
|
||||
bool FlatModel::projectFilterEnabled()
|
||||
{
|
||||
return m_filterProjects;
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
bool generatedFilesFilterEnabled();
|
||||
void setProjectFilterEnabled(bool filter);
|
||||
void setGeneratedFilesFilterEnabled(bool filter);
|
||||
void setTrimEmptyDirectories(bool filter);
|
||||
|
||||
void onExpanded(const QModelIndex &idx);
|
||||
void onCollapsed(const QModelIndex &idx);
|
||||
@@ -87,12 +88,14 @@ signals:
|
||||
private:
|
||||
bool m_filterProjects = false;
|
||||
bool m_filterGeneratedFiles = true;
|
||||
bool m_trimEmptyDirectories = true;
|
||||
|
||||
static const QLoggingCategory &logger();
|
||||
|
||||
void updateSubtree(FolderNode *node);
|
||||
void rebuildModel();
|
||||
void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen);
|
||||
bool trimEmptyDirectories(WrapperNode *parent);
|
||||
|
||||
ExpandData expandDataForNode(const Node *node) const;
|
||||
void loadExpandData();
|
||||
|
||||
@@ -119,8 +119,8 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder,
|
||||
\sa ProjectExplorer::NodesWatcher
|
||||
*/
|
||||
|
||||
Node::Node(NodeType nodeType, const Utils::FileName &filePath, int line) :
|
||||
m_filePath(filePath), m_line(line), m_nodeType(nodeType)
|
||||
Node::Node(NodeType nodeType, const Utils::FileName &filePath, int line, const QByteArray &id) :
|
||||
m_filePath(filePath), m_nodeId(id), m_line(line), m_nodeType(nodeType)
|
||||
{ }
|
||||
|
||||
void Node::setPriority(int p)
|
||||
@@ -222,6 +222,11 @@ int Node::line() const
|
||||
return m_line;
|
||||
}
|
||||
|
||||
QByteArray Node::id() const
|
||||
{
|
||||
return m_nodeId;
|
||||
}
|
||||
|
||||
QString Node::displayName() const
|
||||
{
|
||||
return filePath().fileName();
|
||||
@@ -309,9 +314,9 @@ FileType Node::fileTypeForFileName(const Utils::FileName &file)
|
||||
\sa ProjectExplorer::FolderNode, ProjectExplorer::ProjectNode
|
||||
*/
|
||||
|
||||
FileNode::FileNode(const Utils::FileName &filePath,
|
||||
const FileType fileType,
|
||||
bool generated, int line) : Node(NodeType::File, filePath, line),
|
||||
FileNode::FileNode(const Utils::FileName &filePath, const FileType fileType, bool generated,
|
||||
int line, const QByteArray &id) :
|
||||
Node(NodeType::File, filePath, line, id),
|
||||
m_fileType(fileType)
|
||||
{
|
||||
setListInProject(true);
|
||||
@@ -324,7 +329,7 @@ FileNode::FileNode(const Utils::FileName &filePath,
|
||||
|
||||
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->setPriority(priority());
|
||||
fn->setListInProject(listInProject());
|
||||
@@ -418,8 +423,9 @@ bool FileNode::supportsAction(ProjectAction action, const Node *node) const
|
||||
|
||||
\sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
|
||||
*/
|
||||
FolderNode::FolderNode(const Utils::FileName &folderPath, NodeType nodeType, const QString &displayName) :
|
||||
Node(nodeType, folderPath, -1),
|
||||
FolderNode::FolderNode(const Utils::FileName &folderPath, NodeType nodeType,
|
||||
const QString &displayName, const QByteArray &id) :
|
||||
Node(nodeType, folderPath, -1, id),
|
||||
m_displayName(displayName)
|
||||
{
|
||||
setPriority(DefaultFolderPriority);
|
||||
@@ -745,8 +751,9 @@ bool FolderNode::showInSimpleTree() const
|
||||
|
||||
\sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
|
||||
*/
|
||||
VirtualFolderNode::VirtualFolderNode(const Utils::FileName &folderPath, int priority) :
|
||||
FolderNode(folderPath, NodeType::VirtualFolder, QString())
|
||||
VirtualFolderNode::VirtualFolderNode(const Utils::FileName &folderPath, int priority,
|
||||
const QByteArray &id) :
|
||||
FolderNode(folderPath, NodeType::VirtualFolder, QString(), id)
|
||||
{
|
||||
setPriority(priority);
|
||||
}
|
||||
@@ -771,12 +778,11 @@ QString VirtualFolderNode::addFileFilter() const
|
||||
/*!
|
||||
Creates an uninitialized project node object.
|
||||
*/
|
||||
ProjectNode::ProjectNode(const Utils::FileName &projectFilePath) :
|
||||
FolderNode(projectFilePath, NodeType::Project)
|
||||
ProjectNode::ProjectNode(const Utils::FileName &projectFilePath, const QByteArray &id) :
|
||||
FolderNode(projectFilePath, NodeType::Project, projectFilePath.fileName(), id)
|
||||
{
|
||||
setPriority(DefaultProjectPriority);
|
||||
setListInProject(true);
|
||||
setDisplayName(projectFilePath.fileName());
|
||||
}
|
||||
|
||||
bool ProjectNode::canAddSubProject(const QString &proFilePath) const
|
||||
|
||||
@@ -125,6 +125,7 @@ public:
|
||||
|
||||
const Utils::FileName &filePath() const; // file system path
|
||||
int line() const;
|
||||
QByteArray id() const;
|
||||
virtual QString displayName() const;
|
||||
virtual QString tooltip() const;
|
||||
bool isEnabled() const;
|
||||
@@ -154,7 +155,8 @@ public:
|
||||
static FileType fileTypeForFileName(const Utils::FileName &file);
|
||||
|
||||
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 setIsGenerated(bool g);
|
||||
@@ -162,6 +164,7 @@ protected:
|
||||
private:
|
||||
FolderNode *m_parentFolderNode = nullptr;
|
||||
Utils::FileName m_filePath;
|
||||
QByteArray m_nodeId;
|
||||
int m_line = -1;
|
||||
int m_priority = DefaultPriority;
|
||||
const NodeType m_nodeType;
|
||||
@@ -178,7 +181,8 @@ private:
|
||||
class PROJECTEXPLORER_EXPORT FileNode : public Node
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -208,7 +212,7 @@ class PROJECTEXPLORER_EXPORT FolderNode : public Node
|
||||
{
|
||||
public:
|
||||
explicit FolderNode(const Utils::FileName &folderPath, NodeType nodeType = NodeType::Folder,
|
||||
const QString &displayName = QString());
|
||||
const QString &displayName = QString(), const QByteArray &id = {});
|
||||
~FolderNode() override;
|
||||
|
||||
QString displayName() const override;
|
||||
@@ -299,7 +303,8 @@ private:
|
||||
class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode
|
||||
{
|
||||
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; }
|
||||
QString addFileFilter() const override;
|
||||
@@ -334,7 +339,7 @@ public:
|
||||
const ProjectNode *asProjectNode() const final { return this; }
|
||||
|
||||
protected:
|
||||
explicit ProjectNode(const Utils::FileName &projectFilePath);
|
||||
explicit ProjectNode(const Utils::FileName &projectFilePath, const QByteArray &id = {});
|
||||
};
|
||||
|
||||
class PROJECTEXPLORER_EXPORT ContainerNode : public FolderNode
|
||||
|
||||
@@ -255,6 +255,12 @@ ProjectTreeWidget::ProjectTreeWidget(QWidget *parent) : QWidget(parent)
|
||||
connect(m_filterGeneratedFilesAction, &QAction::toggled,
|
||||
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
|
||||
connect(m_model, &FlatModel::renamed,
|
||||
this, &ProjectTreeWidget::renamed);
|
||||
@@ -494,6 +500,12 @@ void ProjectTreeWidget::setGeneratedFilesFilter(bool filter)
|
||||
m_filterGeneratedFilesAction->setChecked(filter);
|
||||
}
|
||||
|
||||
void ProjectTreeWidget::setTrimEmptyDirectories(bool filter)
|
||||
{
|
||||
m_model->setTrimEmptyDirectories(filter);
|
||||
m_trimEmptyDirectoriesAction->setChecked(filter);
|
||||
}
|
||||
|
||||
bool ProjectTreeWidget::generatedFilesFilter()
|
||||
{
|
||||
return m_model->generatedFilesFilterEnabled();
|
||||
@@ -527,6 +539,7 @@ NavigationView ProjectTreeWidgetFactory::createWidget()
|
||||
auto filterMenu = new QMenu(filter);
|
||||
filterMenu->addAction(ptw->m_filterProjectsAction);
|
||||
filterMenu->addAction(ptw->m_filterGeneratedFilesAction);
|
||||
filterMenu->addAction(ptw->m_trimEmptyDirectoriesAction);
|
||||
filter->setMenu(filterMenu);
|
||||
|
||||
n.dockToolBarWidgets << filter << ptw->toggleSync();
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
private:
|
||||
void setProjectFilter(bool filter);
|
||||
void setGeneratedFilesFilter(bool filter);
|
||||
void setTrimEmptyDirectories(bool filter);
|
||||
|
||||
void handleCurrentItemChange(const QModelIndex ¤t);
|
||||
void showContextMenu(const QPoint &pos);
|
||||
@@ -83,8 +84,9 @@ private:
|
||||
QTreeView *m_view = nullptr;
|
||||
FlatModel *m_model = nullptr;
|
||||
QAction *m_filterProjectsAction = nullptr;
|
||||
QAction *m_filterGeneratedFilesAction;
|
||||
QToolButton *m_toggleSync;
|
||||
QAction *m_filterGeneratedFilesAction = nullptr;
|
||||
QAction *m_trimEmptyDirectoriesAction = nullptr;
|
||||
QToolButton *m_toggleSync = nullptr;
|
||||
|
||||
QString m_modelId;
|
||||
bool m_autoSync = true;
|
||||
|
||||
@@ -533,7 +533,7 @@ namespace Internal {
|
||||
|
||||
enum class RunWorkerState
|
||||
{
|
||||
Initialized, Starting, Running, Stopping, Done, Failed
|
||||
Initialized, Starting, Running, Stopping, Done
|
||||
};
|
||||
|
||||
static QString stateName(RunWorkerState s)
|
||||
@@ -545,7 +545,6 @@ static QString stateName(RunWorkerState s)
|
||||
SN(RunWorkerState::Running)
|
||||
SN(RunWorkerState::Stopping)
|
||||
SN(RunWorkerState::Done)
|
||||
SN(RunWorkerState::Failed)
|
||||
}
|
||||
return QString("<unknown: %1>").arg(int(s));
|
||||
# undef SN
|
||||
@@ -557,12 +556,14 @@ public:
|
||||
RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl);
|
||||
|
||||
bool canStart() const;
|
||||
bool canStop() const;
|
||||
void timerEvent(QTimerEvent *ev) override;
|
||||
|
||||
RunWorker *q;
|
||||
RunWorkerState state = RunWorkerState::Initialized;
|
||||
QPointer<RunControl> runControl;
|
||||
QList<RunWorker *> dependencies;
|
||||
QList<RunWorker *> startDependencies;
|
||||
QList<RunWorker *> stopDependencies;
|
||||
QString id;
|
||||
|
||||
QVariantMap data;
|
||||
@@ -571,6 +572,7 @@ public:
|
||||
int stopWatchdogInterval = 0; // 5000;
|
||||
int stopWatchdogTimerId = -1;
|
||||
bool supportsReRunning = true;
|
||||
bool essential = false;
|
||||
};
|
||||
|
||||
enum class RunControlState
|
||||
@@ -637,6 +639,7 @@ public:
|
||||
void initiateReStart();
|
||||
void continueStart();
|
||||
void initiateStop();
|
||||
void continueStopOrFinish();
|
||||
void initiateFinish();
|
||||
|
||||
void onWorkerStarted(RunWorker *worker);
|
||||
@@ -834,12 +837,6 @@ void RunControlPrivate::continueStart()
|
||||
case RunWorkerState::Stopping:
|
||||
debugMessage(" " + workerId + " currently stopping");
|
||||
continue;
|
||||
case RunWorkerState::Failed:
|
||||
// Should not happen.
|
||||
debugMessage(" " + workerId + " failed before");
|
||||
QTC_CHECK(false);
|
||||
//setState(RunControlState::Stopped);
|
||||
break;
|
||||
case RunWorkerState::Done:
|
||||
debugMessage(" " + workerId + " was done before");
|
||||
break;
|
||||
@@ -854,11 +851,29 @@ void RunControlPrivate::continueStart()
|
||||
|
||||
void RunControlPrivate::initiateStop()
|
||||
{
|
||||
checkState(RunControlState::Running);
|
||||
if (state != RunControlState::Starting && state != RunControlState::Running)
|
||||
qDebug() << "Unexpected initiateStop() in state" << stateName(state);
|
||||
|
||||
setState(RunControlState::Stopping);
|
||||
debugMessage("Queue: Stopping for all workers");
|
||||
|
||||
continueStopOrFinish();
|
||||
}
|
||||
|
||||
void RunControlPrivate::continueStopOrFinish()
|
||||
{
|
||||
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) {
|
||||
if (worker) {
|
||||
const QString &workerId = worker->d->id;
|
||||
@@ -873,33 +888,35 @@ void RunControlPrivate::initiateStop()
|
||||
allDone = false;
|
||||
break;
|
||||
case RunWorkerState::Starting:
|
||||
debugMessage(" " + workerId + " was Starting, queuing stop");
|
||||
worker->d->state = RunWorkerState::Stopping;
|
||||
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
|
||||
queueStop(worker, " " + workerId + " was Starting, queuing stop");
|
||||
allDone = false;
|
||||
break;
|
||||
case RunWorkerState::Running:
|
||||
debugMessage(" " + workerId + " was Running, queuing stop");
|
||||
worker->d->state = RunWorkerState::Stopping;
|
||||
queueStop(worker, " " + workerId + " was Running, queuing stop");
|
||||
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) {
|
||||
debugMessage("All stopped.");
|
||||
setState(RunControlState::Stopped);
|
||||
|
||||
RunControlState targetState;
|
||||
if (state == RunControlState::Finishing) {
|
||||
targetState = RunControlState::Finished;
|
||||
} 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);
|
||||
debugMessage("Ramping down");
|
||||
|
||||
bool allDone = true;
|
||||
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...");
|
||||
}
|
||||
continueStopOrFinish();
|
||||
}
|
||||
|
||||
void RunControlPrivate::onWorkerStarted(RunWorker *worker)
|
||||
@@ -968,10 +944,13 @@ void RunControlPrivate::onWorkerStarted(RunWorker *worker)
|
||||
|
||||
void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg)
|
||||
{
|
||||
worker->d->state = RunWorkerState::Failed;
|
||||
worker->d->state = RunWorkerState::Done;
|
||||
|
||||
showError(msg);
|
||||
initiateStop();
|
||||
if (state == RunControlState::Running || state == RunControlState::Starting)
|
||||
initiateStop();
|
||||
else
|
||||
continueStopOrFinish();
|
||||
}
|
||||
|
||||
void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
||||
@@ -994,10 +973,34 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
||||
default:
|
||||
debugMessage(workerId + " stopped unexpectedly in state"
|
||||
+ stateName(worker->d->state));
|
||||
worker->d->state = RunWorkerState::Failed;
|
||||
worker->d->state = RunWorkerState::Done;
|
||||
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");
|
||||
bool allDone = true;
|
||||
for (RunWorker *worker : m_workers) {
|
||||
@@ -1023,32 +1026,21 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
||||
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 (state == RunControlState::Finishing) {
|
||||
if (allDone) {
|
||||
debugMessage("All finished. Deleting myself");
|
||||
setState(RunControlState::Finished);
|
||||
|
||||
if (allDone) {
|
||||
if (state == RunControlState::Stopped) {
|
||||
debugMessage("All workers stopped, but runControl was already stopped.");
|
||||
} else {
|
||||
debugMessage("Not all workers finished. Waiting...");
|
||||
debugMessage("All workers stopped. Set runControl to Stopped");
|
||||
setState(RunControlState::Stopped);
|
||||
}
|
||||
} else {
|
||||
if (allDone) {
|
||||
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...");
|
||||
}
|
||||
debugMessage("Not all workers stopped. Waiting...");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1254,6 +1246,7 @@ bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlStat
|
||||
|| to == RunControlState::Finishing;
|
||||
case RunControlState::Starting:
|
||||
return to == RunControlState::Running
|
||||
|| to == RunControlState::Stopping
|
||||
|| to == RunControlState::Finishing;
|
||||
case RunControlState::Running:
|
||||
return to == RunControlState::Stopping
|
||||
@@ -1480,8 +1473,8 @@ bool RunWorkerPrivate::canStart() const
|
||||
{
|
||||
if (state != RunWorkerState::Initialized)
|
||||
return false;
|
||||
for (RunWorker *worker : dependencies) {
|
||||
QTC_ASSERT(worker, return true);
|
||||
for (RunWorker *worker : startDependencies) {
|
||||
QTC_ASSERT(worker, continue);
|
||||
if (worker->d->state != RunWorkerState::Done
|
||||
&& worker->d->state != RunWorkerState::Running)
|
||||
return false;
|
||||
@@ -1489,6 +1482,18 @@ bool RunWorkerPrivate::canStart() const
|
||||
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)
|
||||
{
|
||||
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)
|
||||
: d(new RunWorkerPrivate(this, runControl))
|
||||
@@ -1513,6 +1553,10 @@ RunWorker::~RunWorker()
|
||||
delete d;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function is called by the RunControl once all dependencies
|
||||
* are fulfilled.
|
||||
*/
|
||||
void RunWorker::initiateStart()
|
||||
{
|
||||
if (d->startWatchdogInterval != 0)
|
||||
@@ -1521,6 +1565,12 @@ void RunWorker::initiateStart()
|
||||
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()
|
||||
{
|
||||
if (d->startWatchdogInterval != 0)
|
||||
@@ -1529,6 +1579,12 @@ void RunWorker::reportStarted()
|
||||
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()
|
||||
{
|
||||
if (d->stopWatchdogInterval != 0)
|
||||
@@ -1538,6 +1594,15 @@ void RunWorker::initiateStop()
|
||||
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()
|
||||
{
|
||||
if (d->stopWatchdogInterval != 0)
|
||||
@@ -1546,11 +1611,47 @@ void RunWorker::reportStopped()
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (msg.endsWith('\n'))
|
||||
@@ -1574,9 +1675,14 @@ Core::Id RunWorker::runMode() const
|
||||
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
|
||||
@@ -1619,11 +1725,6 @@ bool RunWorker::supportsReRunning() const
|
||||
return d->supportsReRunning;
|
||||
}
|
||||
|
||||
bool RunWorker::hasFailed() const
|
||||
{
|
||||
return d->state == RunWorkerState::Failed;
|
||||
}
|
||||
|
||||
QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const QString &program)
|
||||
{
|
||||
QString failedToStart = tr("The process failed to start.");
|
||||
@@ -1657,6 +1758,16 @@ QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, cons
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool RunWorker::isEssential() const
|
||||
{
|
||||
return d->essential;
|
||||
}
|
||||
|
||||
void RunWorker::setEssential(bool essential)
|
||||
{
|
||||
d->essential = essential;
|
||||
}
|
||||
|
||||
void RunWorker::start()
|
||||
{
|
||||
reportStarted();
|
||||
|
||||
@@ -326,7 +326,8 @@ public:
|
||||
|
||||
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 setId(const QString &id);
|
||||
@@ -350,13 +351,17 @@ public:
|
||||
void initiateStop();
|
||||
void reportStopped();
|
||||
|
||||
void reportDone();
|
||||
|
||||
void reportFailure(const QString &msg = QString());
|
||||
void setSupportsReRunning(bool reRunningSupported);
|
||||
bool supportsReRunning() const;
|
||||
bool hasFailed() const;
|
||||
|
||||
static QString userMessageForProcessError(QProcess::ProcessError, const QString &programName);
|
||||
|
||||
bool isEssential() const;
|
||||
void setEssential(bool essential);
|
||||
|
||||
signals:
|
||||
void started();
|
||||
void stopped();
|
||||
|
||||
@@ -62,6 +62,9 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
@@ -76,6 +79,9 @@
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -1011,7 +1011,7 @@ void QbsProject::updateCppCodeModel()
|
||||
rpp.setDisplayName(grp.name());
|
||||
rpp.setProjectFileLocation(grp.location().filePath(),
|
||||
grp.location().line(), grp.location().column());
|
||||
rpp.setBuildSystemTarget(prd.name() + '|' + rpp.projectFile);
|
||||
rpp.setBuildSystemTarget(prd.name());
|
||||
|
||||
QHash<QString, qbs::ArtifactData> filePathToSourceArtifact;
|
||||
bool hasCFiles = false;
|
||||
|
||||