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
This commit is contained in:
Eike Ziller
2017-08-15 10:07:51 +02:00
209 changed files with 5006 additions and 2423 deletions
+1
View File
@@ -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/
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+22 -47
View File
@@ -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]
*/
+7 -3
View File
@@ -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.
+10 -29
View File
@@ -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.
+8 -2
View File
@@ -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}.
*/
+46 -4
View File
@@ -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
+3 -4
View File
@@ -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
+3 -3
View File
@@ -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)
+2 -1
View File
@@ -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()
+8 -11
View File
@@ -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():
+16 -3
View File
@@ -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: {
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -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"]
+2
View File
@@ -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);
+5
View File
@@ -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;
+3
View File
@@ -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;
+9 -23
View File
@@ -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
+23
View File
@@ -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);
+2
View File
@@ -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.
+1
View File
@@ -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);
+3 -2
View File
@@ -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;
}
+1 -1
View File
@@ -99,7 +99,7 @@ AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl)
{
setDisplayName("AndroidDebugger");
m_runner = new AndroidRunner(runControl);
addDependency(m_runner);
addStartDependency(m_runner);
}
void AndroidDebugSupport::start()
+1 -1
View File
@@ -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;
}
}
+80 -32
View File
@@ -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 &currentExecutable = 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();
}
/**
+1 -1
View File
@@ -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
+1 -2
View File
@@ -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) {
-2
View File
@@ -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);
+5 -5
View File
@@ -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;
}
+27 -29
View File
@@ -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()
+4 -2
View File
@@ -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())
+17 -15
View File
@@ -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;
+1 -1
View File
@@ -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 "
+1 -1
View File
@@ -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);
+437 -306
View File
@@ -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;
+3 -3
View File
@@ -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()
+2 -2
View File
@@ -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);
+27 -2
View File
@@ -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();
+19 -13
View File
@@ -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
+10 -5
View File
@@ -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 &current);
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;
+207 -96
View File
@@ -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>
+1 -1
View File
@@ -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;

Some files were not shown because too many files have changed in this diff Show More