forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/10.0'
Conflicts: src/plugins/clangcodemodel/clangcodemodelplugin.cpp Change-Id: Idb3d6e8fdfd278979f6180dc3795a2138bc2e61d
This commit is contained in:
236
dist/changelog/changes-10.0.0.md
vendored
Normal file
236
dist/changelog/changes-10.0.0.md
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
Qt Creator 10
|
||||
=============
|
||||
|
||||
Qt Creator version 10 contains bug fixes and new features.
|
||||
|
||||
The most important changes are listed in this document. For a complete list of
|
||||
changes, see the Git log for the Qt Creator sources that you can check out from
|
||||
the public Git repository. For example:
|
||||
|
||||
git clone git://code.qt.io/qt-creator/qt-creator.git
|
||||
git log --cherry-pick --pretty=oneline origin/9.0..v10.0.0
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
* Added support for temporarily dragging progress details out of the way
|
||||
(QTCREATORBUG-28078)
|
||||
|
||||
Editing
|
||||
-------
|
||||
|
||||
* Added `Follow Symbol` for `http(s)` string literals (QTCREATORBUG-14967)
|
||||
* Added environment expansion to file system locator filter (QTCREATORBUG-242)
|
||||
* Added `Temporarily hide inline annotations` for types of annotations
|
||||
* Improved cursor visibility with indentation visualization (QTCREATORBUG-28645)
|
||||
* Fixed editor scrolling when pressing backspace (QTCREATORBUG-28316)
|
||||
* Fixed performance of rendering many search results (QTCREATORBUG-21056)
|
||||
|
||||
### C++
|
||||
|
||||
* Added renaming of includes when renaming `.ui` files (QTCREATORBUG-14259)
|
||||
* Added automatic refactoring of C++ code when forms in `.ui` files are renamed
|
||||
(QTCREATORBUG-1179)
|
||||
* Added option for ignoring files for indexing (QTCREATORBUG-28313)
|
||||
* Added `Tools > C++ > Find Unused Functions`, and `Find Unused C/C++ Functions`
|
||||
to the project context menu (QTCREATORBUG-6772)
|
||||
* Fixed text codec when rewriting headers as part of renaming
|
||||
(QTCREATORBUG-28164)
|
||||
* Fixed color of whitespace visualization in string literals
|
||||
(QTCREATORBUG-26693, QTCREATORBUG-28284)
|
||||
* Fixed `Move Definition` for template functions (QTCREATORBUG-28186)
|
||||
* Clangd
|
||||
* Made temporary disabling of global indexing possibly by cancelling in the
|
||||
progress indicator
|
||||
* Added support for highlighting angle brackets
|
||||
* Built-in
|
||||
* Added support for spaceship operator (QTCREATORBUG-27503)
|
||||
* Fixed handling of `= default` (QTCREATORBUG-28102)
|
||||
* ClangFormat
|
||||
* Enabled by default
|
||||
* Added project setting for `ClangFormat` (QTCREATORBUG-28188)
|
||||
|
||||
### Language Server Protocol
|
||||
|
||||
* Added `Restart` action to menu in editor tool bar
|
||||
* Added `Call Hierarchy` (QTCREATORBUG-11660)
|
||||
|
||||
### QML
|
||||
|
||||
* Updated code model to Qt 6.5
|
||||
* Added experimental support for QML language server
|
||||
* Added color preview tooltip (QTCREATORBUG-28446)
|
||||
* Added option for applying `qmlformat` on file save (QTCREATORBUG-28192,
|
||||
QTCREATORBUG-26602)
|
||||
* Added `Follow Symbol` for QRC paths in string literals (QTCREATORBUG-28087)
|
||||
* Adapted wizard to new features in Qt 6.4 and Qt 6.5 (QTBUG-47996)
|
||||
* Fixed freeze when closing file (QTCREATORBUG-28206)
|
||||
* Fixed that `QtObject` was not recognized (QTCREATORBUG-28287,
|
||||
QTCREATORBUG-28375)
|
||||
|
||||
### Python
|
||||
|
||||
* Added interpreter selector to editor toolbar (PYSIDE-2154)
|
||||
|
||||
Projects
|
||||
--------
|
||||
|
||||
* Added `Build > Run Generator` for exporting projects to other build systems
|
||||
(QTCREATORBUG-28149)
|
||||
* Added option for browsing remote file system for remote builds and targets
|
||||
* Added support for opening remote terminal
|
||||
* Fixed that wizards did not create target directories (QTCREATORBUG-28346)
|
||||
* Fixed that absolute paths could be shown when relative paths would be
|
||||
preferable (QTCREATORBUG-288)
|
||||
|
||||
### CMake
|
||||
|
||||
* Added deployment method with `cmake --install` (QTCREATORBUG-25880)
|
||||
* Added option for using `cmake-format` for CMake files
|
||||
([cmake-format Documentation](https://cmake-format.readthedocs.io/en/latest/))
|
||||
* Added option for showing advanced configure items by default
|
||||
* Moved `Autorun CMake` to global settings
|
||||
* Changed environment for running CMake to be based on build environment by
|
||||
default (QTCREATORBUG-28513)
|
||||
* Fixed that `Package manager auto setup` created dependency of project build to
|
||||
Qt Creator installation
|
||||
|
||||
### Qbs
|
||||
|
||||
* Added `Profile` build variant (QTCREATORBUG-27206)
|
||||
* Fixed that generated files were not made known to the code model
|
||||
|
||||
### Python
|
||||
|
||||
* Removed wizard for dynamically loaded `.ui` projects (QTCREATORBUG-25807)
|
||||
|
||||
### Qt Quick UI Prototype
|
||||
|
||||
* Added support for running on remote Linux devices
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
### C++
|
||||
|
||||
* Added pretty printers for `variant`, `optional` and `tuple` from `libcpp`
|
||||
(QTCREATORBUG-25865)
|
||||
* Fixed highlighting in disassembly view
|
||||
* Fixed skipping `std::function` details when stepping
|
||||
|
||||
Analyzer
|
||||
--------
|
||||
|
||||
### Clang
|
||||
|
||||
* Split `Clang-Tidy and Clazy` into separate `Clang-Tidy` and `Clazy` analyzers
|
||||
|
||||
Version Control Systems
|
||||
-----------------------
|
||||
|
||||
* Moved support for `Fossil` SCM into mainline repository
|
||||
* Removed settings for prompting to submit (QTCREATORBUG-22233)
|
||||
* Added links to file names in diff output (QTCREATORBUG-27309)
|
||||
* Fixed blame on symbolic links (QTCREATORBUG-20792)
|
||||
* Fixed saving of files before applying action on chunks (QTCREATORBUG-22506)
|
||||
* Fixed line ending preservation when reverting chunks (QTCREATORBUG-12690)
|
||||
|
||||
### Git
|
||||
|
||||
* Improved tracking of external changes (QTCREATORBUG-21089)
|
||||
* Added editor annotation for blame information (instant blame), with setting
|
||||
(opt-out), and action to show it manually for the current line
|
||||
(QTCREATORBUG-23299)
|
||||
|
||||
Test Integration
|
||||
----------------
|
||||
|
||||
* Improved `Run` and `Debug Test Under Cursor` (QTCREATORBUG-28496)
|
||||
* Improved number of files that are scanned for tests
|
||||
* Improved output handling (QTCREATORBUG-28706)
|
||||
* Made expensive checking for tests in derived `TestCase` objects optional
|
||||
|
||||
Platforms
|
||||
---------
|
||||
|
||||
### macOS
|
||||
|
||||
* Changed kits to prefer Xcode toolchain over the wrappers in `/bin`
|
||||
|
||||
### Android
|
||||
|
||||
* Removed service management from manifest editor (QTCREATORBUG-28024)
|
||||
|
||||
### Remote Linux
|
||||
|
||||
* Fixed that opening file dialog unnecessarily queried for password for remote
|
||||
devices
|
||||
|
||||
### Docker
|
||||
|
||||
* Added support for remote code model via remote Clangd
|
||||
* Added support for loading and attaching to core dumps from remote devices
|
||||
* Added support for using ClangFormat on remote files
|
||||
* Added option to enable necessary capabilities for debugging with LLDB
|
||||
* Fixed issue with space in file paths (QTCREATORBUG-28476)
|
||||
* Fixed that auto-detection controls were shown for devices registered by the
|
||||
installer
|
||||
|
||||
Credits for these changes go to:
|
||||
--------------------------------
|
||||
Aleksei German
|
||||
Alessandro Portale
|
||||
Alexander Pershin
|
||||
Ali Kianian
|
||||
Amr Essam
|
||||
Andre Hartmann
|
||||
André Pönitz
|
||||
Antti Määttä
|
||||
Artem Sokolovskii
|
||||
Artur Shepilko
|
||||
Assam Boudjelthia
|
||||
BogDan Vatra
|
||||
Burak Hancerli
|
||||
Christian Kandeler
|
||||
Christian Stenger
|
||||
Cristian Adam
|
||||
Cristián Maureira-Fredes
|
||||
David Schulz
|
||||
Dmitry Bravikov
|
||||
Eike Ziller
|
||||
Fabian Kosmale
|
||||
Fawzi Mohamed
|
||||
Henning Gruendl
|
||||
Jaroslaw Kobus
|
||||
Jussi Witick
|
||||
Kai Köhne
|
||||
Knud Dollereder
|
||||
Knut Petter Svendsen
|
||||
Leena Miettinen
|
||||
Łukasz Wojniłowicz
|
||||
Mahmoud Badri
|
||||
Marc Mutz
|
||||
Marco Bubke
|
||||
Marcus Tillmanns
|
||||
Mats Honkamaa
|
||||
Miikka Heikkinen
|
||||
Mikhail Khachayants
|
||||
Orgad Shaneh
|
||||
Oswald Buddenhagen
|
||||
Philip Van Hoof
|
||||
Pranta Dastider
|
||||
Robert Löhning
|
||||
Sami Shalayel
|
||||
Samuel Ghinet
|
||||
Sergey Levin
|
||||
Sivert Krøvel
|
||||
Tasuku Suzuki
|
||||
Thiago Macieira
|
||||
Thomas Hartmann
|
||||
Tim Jenssen
|
||||
Tomáš Juřena
|
||||
Topi Reinio
|
||||
Ulf Hermann
|
||||
Vikas Pachdha
|
||||
Xavier Besson
|
||||
Yasser Grimes
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,61 +0,0 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
//! [clang format]
|
||||
|
||||
\section2 Automatic Formatting and Indentation
|
||||
|
||||
The Clang Format plugin uses the
|
||||
\l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat}
|
||||
library for automatic formatting and indentation.
|
||||
|
||||
To enable the plugin, select \uicontrol Help > \uicontrol {About Plugins} >
|
||||
\uicontrol {C++} > \uicontrol {ClangFormat}. Then select
|
||||
\uicontrol {Restart Now} to restart \QC and load the plugin.
|
||||
|
||||
\note If you enable Clang Format, do not use the \l{Beautifying Source Code}
|
||||
{Beautifier} because combining them can lead to unexpected results.
|
||||
|
||||
You can use Clang Format to enforce a coding style for a project or the
|
||||
whole organization. Create a \c {.clang-format} file that has the
|
||||
\l{https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
|
||||
{Clang Format Style Options} to use and save it in the root folder of the
|
||||
project or one of its parent folders. The plugin searches for the Clang
|
||||
format file recursively from the directory that has the source file
|
||||
up to the file system root.
|
||||
|
||||
To override the \c {.clang-format} file globally for all projects, select
|
||||
\uicontrol Edit > \uicontrol Preferences > \uicontrol {C++} > \uicontrol Copy >
|
||||
\uicontrol Edit > \uicontrol {ClangFormat} >
|
||||
\uicontrol {Override Clang Format configuration file}.
|
||||
|
||||
\image qtcreator-code-style-clang-format.png "C++ Clang Format preferences"
|
||||
|
||||
In \uicontrol {Formatting mode}, select \uicontrol {Indenting Only} to only
|
||||
indent code. Select \uicontrol {Full Formatting} to use the \key {Ctrl+I}
|
||||
keyboard shortcut to format code instead of indenting. To apply the
|
||||
formatting while you type, select \uicontrol {Format while typing}. To apply
|
||||
the formatting to the edited code when you save the file, select
|
||||
\uicontrol {Format edited code on file save}.
|
||||
|
||||
This creates a local configuration file that overrides the one stored in the
|
||||
file system.
|
||||
|
||||
To override the \c {.clang-format} file for a particular project, create a
|
||||
copy of the built-in style and edit its settings by selecting
|
||||
\uicontrol Projects > \uicontrol {Project Settings} >
|
||||
\uicontrol {Code Style} > \uicontrol Copy > \uicontrol Edit >
|
||||
\uicontrol {ClangFormat} >
|
||||
\uicontrol {Override Clang Format configuration file}.
|
||||
|
||||
You can create \c {.clang-format} files that have the configuration
|
||||
options of a certain predefined style from the command line. For example,
|
||||
to create a format file for the LLVM style, enter the following command:
|
||||
|
||||
\badcode
|
||||
clang-format -style=llvm -dump-config > .clang-format
|
||||
\endcode
|
||||
|
||||
//! [clang format]
|
||||
*/
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
@@ -60,7 +60,7 @@
|
||||
the \uicontrol {Skip clean whitespace for file types} check box to
|
||||
exclude the specified file types.
|
||||
|
||||
\image qtcreator-options-text-editor-behavior.png "Text Editor Behavior preferences"
|
||||
\image qtcreator-options-text-editor-behavior.png {Text Editor Behavior preferences}
|
||||
|
||||
To visualize whitespace in the editor, select \uicontrol Edit >
|
||||
\uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Display >
|
||||
@@ -69,7 +69,7 @@
|
||||
change the value of the Visual Whitespace setting of the editor color scheme
|
||||
in \uicontrol {Font & Colors}.
|
||||
|
||||
\image qtcreator-options-text-editor-display.png "Text Editor Display preferences"
|
||||
\image qtcreator-options-text-editor-display.png {Text Editor Display preferences}
|
||||
|
||||
To help you keep line length at a particular number of characters, set the
|
||||
number of characters in the \uicontrol {Display right margin at column}
|
||||
@@ -80,24 +80,39 @@
|
||||
To use a context-specific margin when available, select the
|
||||
\uicontrol {Use context-specific margin} check box.
|
||||
\if defined(qtcreator)
|
||||
For example, the margin could be set by the \c ColumnLimit option of the
|
||||
\l{Specifying Code Style Settings}{Clang Format plugin}.
|
||||
Then, use the ClangFormat \c ColumnLimit option to set the margin, for
|
||||
example.
|
||||
|
||||
\section1 Indenting C++ Files
|
||||
|
||||
\QC uses the Clang \l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat}
|
||||
library to automatically format and indent C++ code. It enforces a coding
|
||||
style for a project or the whole organization.
|
||||
|
||||
To specify indentation settings for the C++ editor:
|
||||
|
||||
\list 1
|
||||
\li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol {C++}.
|
||||
\image qtcreator-code-style-clang-format-global.webp {Code Style preferences}
|
||||
\li In \uicontrol {Formatting mode}, select \uicontrol {Indenting Only}
|
||||
to only indent code. Select \uicontrol {Full Formatting} to use the
|
||||
\key {Ctrl+I} keyboard shortcut to format code instead of indenting.
|
||||
Select \uicontrol Disable to turn off ClangFormat.
|
||||
\li To apply the formatting while you type, select
|
||||
\uicontrol {Format while typing}.
|
||||
\li To apply the formatting to the edited code when you save the file,
|
||||
select \uicontrol {Format edited code on file save}.
|
||||
\li To change the ClangFormat style globally for all projects,
|
||||
select \uicontrol {Override ClangFormat configuration file}.
|
||||
\li In the \uicontrol {Current settings} field, select the settings to
|
||||
modify and click \uicontrol Copy.
|
||||
\image qtcreator-options-code-style-cpp.png "C++ Code Style preferences"
|
||||
\li Give a name to the settings and click \uicontrol OK.
|
||||
\li Click \uicontrol Edit to specify code style settings for the project.
|
||||
\image qtcreator-code-style-settings-edit-cpp.png "Edit Code Style dialog"
|
||||
\li Click \uicontrol Edit to set
|
||||
\l{https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
|
||||
{ClangFormat Style Options}.
|
||||
\endlist
|
||||
|
||||
You can specify how to:
|
||||
In the other tabs, you can specify how to:
|
||||
|
||||
\list
|
||||
\li Interpret the \key Tab and \key Backspace key presses.
|
||||
@@ -111,12 +126,27 @@
|
||||
\li Name getter functions.
|
||||
\endlist
|
||||
|
||||
You can use the live preview to see how the preferences change the indentation.
|
||||
Use the live preview to see how the preferences change the indentation.
|
||||
|
||||
To specify different settings for a particular project, select
|
||||
\uicontrol Projects > \uicontrol {Code Style}.
|
||||
\section2 Creating Project-Specific ClangFormat Files
|
||||
|
||||
To override the \c {.clang-format} file for a particular project, create a
|
||||
copy of the built-in style and edit its settings by selecting
|
||||
\uicontrol Projects > \uicontrol {Project Settings} >
|
||||
\uicontrol {Code Style} > \uicontrol Copy > \uicontrol Edit >
|
||||
\uicontrol {ClangFormat} >
|
||||
\uicontrol {Override ClangFormat configuration file}.
|
||||
|
||||
\section2 Creating ClangFormat Files from Command Line
|
||||
|
||||
You can create \c {.clang-format} files that have the configuration
|
||||
options of a certain predefined style from the command line. For example,
|
||||
to create a format file for the LLVM style, enter the following command:
|
||||
|
||||
\badcode
|
||||
clang-format -style=llvm -dump-config > .clang-format
|
||||
\endcode
|
||||
|
||||
\include creator-clangformat.qdocinc clang format
|
||||
\endif
|
||||
|
||||
\section1 Indenting QML Files
|
||||
@@ -128,10 +158,10 @@
|
||||
\uicontrol {Qt Quick}.
|
||||
\li In the \uicontrol {Current settings} field, select the settings to
|
||||
modify and click \uicontrol Copy.
|
||||
\image qtcreator-options-code-style-qml.png "QML Code Style preferences"
|
||||
\image qtcreator-options-code-style-qml.png {QML Code Style preferences}
|
||||
\li Give a name to the settings and click \uicontrol OK.
|
||||
\li Click \uicontrol Edit to specify code style settings for the project.
|
||||
\image qtcreator-code-style-settings-edit-qtquick.png "Edit Code Style dialog"
|
||||
\image qtcreator-code-style-settings-edit-qtquick.png {Edit Code Style dialog}
|
||||
\endlist
|
||||
|
||||
You can specify how to interpret the \key Tab key presses and how to align
|
||||
@@ -152,10 +182,10 @@
|
||||
\li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Nim.
|
||||
\li In the \uicontrol {Current settings} field, select the settings to
|
||||
modify and click \uicontrol Copy.
|
||||
\image qtcreator-options-code-style-nim.png "Nim Code Style preferences"
|
||||
\image qtcreator-options-code-style-nim.png {Nim Code Style preferences}
|
||||
\li Give a name to the settings and click \uicontrol OK.
|
||||
\li Click \uicontrol Edit to specify code style settings for the project.
|
||||
\image qtcreator-code-style-settings-edit-nim.png "Edit Code Style dialog"
|
||||
\image qtcreator-code-style-settings-edit-nim.png {Edit Code Style dialog}
|
||||
\endlist
|
||||
|
||||
You can specify how to interpret the \key Tab key presses and how to align
|
||||
@@ -171,7 +201,7 @@
|
||||
QML code (such as Python code files), select \uicontrol Edit >
|
||||
\uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Behavior.
|
||||
|
||||
\image qtcreator-indentation.png "Text Editor Behavior preferences"
|
||||
\image qtcreator-indentation.png {Text Editor Behavior preferences}
|
||||
|
||||
To specify different settings for a particular project, select
|
||||
\uicontrol Projects > \uicontrol Editor.
|
||||
@@ -251,21 +281,21 @@
|
||||
You can also indent statements within functions and blocks and declarations
|
||||
within namespaces.
|
||||
|
||||
\image qtcreator-code-style-content.png "Content preferences"
|
||||
\image qtcreator-code-style-content.png {Content preferences}
|
||||
|
||||
\section1 Specifying Settings for Braces
|
||||
|
||||
You can indent class, namespace, enum and function declarations and code
|
||||
blocks.
|
||||
|
||||
\image qtcreator-code-style-braces.png "Braces preferences"
|
||||
\image qtcreator-code-style-braces.png {Braces preferences}
|
||||
|
||||
\section1 Specifying Settings for Switch Statements
|
||||
|
||||
You can indent case or default statements, or statements or blocks related
|
||||
to them within switch statements.
|
||||
|
||||
\image qtcreator-code-style-switch.png "Switch preferences"
|
||||
\image qtcreator-code-style-switch.png {Switch preferences}
|
||||
|
||||
\section1 Specifying Alignment
|
||||
|
||||
@@ -278,7 +308,7 @@
|
||||
aligned with the following line. Usually, this only affects \c if
|
||||
statements.
|
||||
|
||||
\image qtcreator-code-style-alignment.png "Alignment preferences"
|
||||
\image qtcreator-code-style-alignment.png {Alignment preferences}
|
||||
|
||||
\section1 Binding Pointers and References
|
||||
|
||||
@@ -289,6 +319,6 @@
|
||||
The \c * and \c & characters are automatically bound to identifiers of
|
||||
pointers to functions and pointers to arrays.
|
||||
|
||||
\image qtcreator-pointers-references.png "Pointers and References preferences"
|
||||
\image qtcreator-pointers-references.png {Pointers and References preferences}
|
||||
\endif
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,12 @@
|
||||
\li Select \uicontrol Help > \uicontrol {About Plugins} > \uicontrol {C++} >
|
||||
\uicontrol Beautifier to enable the plugin.
|
||||
|
||||
\note Since \QC 10.0.0, the ClangFormat plugin is enabled by
|
||||
default. Select \uicontrol Edit > \uicontrol Preferences >
|
||||
\uicontrol {C++} > \uicontrol {Formatting mode} > \uicontrol Disable
|
||||
to turn off ClangFormat if you enable Beautifier because combining
|
||||
them can lead to unexpected results.
|
||||
|
||||
\li Select \uicontrol {Restart Now} to restart \QC and load the plugin.
|
||||
|
||||
\li Select \uicontrol Edit > \uicontrol Preferences >
|
||||
@@ -75,7 +81,7 @@
|
||||
project} check box to only beautify files that belong to the
|
||||
current project.
|
||||
|
||||
\li Select \uicontrol {Artistic Style}, \uicontrol {Clang Format}, or
|
||||
\li Select \uicontrol {Artistic Style}, \uicontrol {ClangFormat}, or
|
||||
\uicontrol Uncrustify to specify settings for the tool you want to
|
||||
use.
|
||||
|
||||
@@ -83,7 +89,7 @@
|
||||
|
||||
\li In the \uicontrol Configuration group, specify the path to
|
||||
the tool executable in the \uicontrol {Artistic Style command},
|
||||
\uicontrol {Clang Format command}, or
|
||||
\uicontrol {ClangFormat command}, or
|
||||
\uicontrol {Uncrustify command} field.
|
||||
|
||||
\li In the \uicontrol {Restrict to MIME types} field, define the MIME
|
||||
@@ -113,12 +119,12 @@
|
||||
use the specified file in the user's home directory as the
|
||||
configuration file for the selected tool.
|
||||
|
||||
\li For Clang Format, you can use a predefined style, by
|
||||
\li For ClangFormat, you can use a predefined style, by
|
||||
selecting the \uicontrol {Use predefined style} radio
|
||||
button, and then selecting the style to use from the list of
|
||||
available styles.
|
||||
|
||||
\image qtcreator-beautifier-options-clangformat.png "Clang Format Beautifier preferences"
|
||||
\image qtcreator-beautifier-options-clangformat.png "ClangFormat Beautifier preferences"
|
||||
|
||||
Select \uicontrol File to load the style configuration from
|
||||
the \c .clang-format or \c _clang-format file located in the
|
||||
|
||||
@@ -437,7 +437,9 @@
|
||||
\li Follow symbol under cursor
|
||||
|
||||
Works with namespaces, classes, functions, variables, include
|
||||
statements and macros
|
||||
statements, and macros. Also, opens URLs in the default browser
|
||||
and Qt resource files (.qrc) in the \l{Creating Resource Files}
|
||||
{resource editor}
|
||||
\li F2
|
||||
\row
|
||||
\li Rename symbol under cursor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019 The Qt Company Ltd.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
// **********************************************************************
|
||||
@@ -12,40 +12,37 @@
|
||||
\page creator-code-style-settings.html
|
||||
\nextpage creator-build-dependencies.html
|
||||
|
||||
\title Specifying Code Style Settings
|
||||
\title Specifying Code Style
|
||||
|
||||
\QC uses the \l{Editing MIME Types}{MIME type} of the file to
|
||||
determine which mode and editor to use for opening the file.
|
||||
\QC opens C++ files in \uicontrol Edit mode in the C++ code editor and
|
||||
QML files in the Qt Quick editor.
|
||||
|
||||
You can configure the code style according to your needs. You can specify
|
||||
code style either globally for all projects or separately for each
|
||||
project. You can specify several sets of code style settings and easily
|
||||
switch between them. In addition, you can import and export code style
|
||||
settings.
|
||||
|
||||
\image qtcreator-projects-code-style.png "Code Style settings in Projects mode"
|
||||
|
||||
Alternatively, you can enable the Clang Format plugin to enforce
|
||||
the code style specified in a \c {.clang-format} file. It uses the
|
||||
\QC uses ClangFormat to enforce the C++ code style specified in a
|
||||
\c {.clang-format} file. It uses the
|
||||
\l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat} library for
|
||||
automatic code formatting and indentation. For more information, see
|
||||
\l {Automatic Formatting and Indentation}.
|
||||
\l {Indenting C++ Files}.
|
||||
|
||||
\image qtcreator-code-style-clang-format.png "Clang Format Code Style settings in Projects mode"
|
||||
\image qtcreator-code-style-clang-format-project.webp {Code Style settings in Projects mode}
|
||||
|
||||
To specify global code style settings sets for C++ files, select
|
||||
In rare cases, ClangFormat can trip over a code construct and
|
||||
trigger a \QC crash. If that happens for your project, select
|
||||
\uicontrol {Formatting mode} > \uicontrol Disable to switch
|
||||
ClangFormat off for the project. If you can reproduce the crash,
|
||||
please select \uicontrol Help > \uicontrol {Report Bug} to report
|
||||
the bug and and attach the code that triggers the crash.
|
||||
|
||||
To specify a global code style for C++ files, select
|
||||
\uicontrol Edit > \uicontrol Preferences > \uicontrol C++.
|
||||
|
||||
\image qtcreator-code-style-settings-edit-cpp.png "Edit Code Style Settings dialog"
|
||||
\image qtcreator-code-style-clang-format-global.webp {Code Style preferences}
|
||||
|
||||
To specify global code style settings sets for QML files, select
|
||||
To specify a global code style for QML files, select
|
||||
\uicontrol Edit > \uicontrol Preferences > \uicontrol {Qt Quick}.
|
||||
|
||||
\image qtcreator-code-style-settings-edit-qtquick.png "Edit Code Style Settings view"
|
||||
|
||||
Only \uicontrol General settings are available for QML files.
|
||||
\image qtcreator-code-style-settings-edit-qtquick.png {Code Style view}
|
||||
|
||||
To configure the editor behavior for the current project:
|
||||
|
||||
@@ -62,7 +59,7 @@
|
||||
|
||||
\li Give a name to the settings and click \uicontrol OK.
|
||||
|
||||
\li Click \uicontrol Edit to specify code style settings for the project.
|
||||
\li Click \uicontrol Edit to specify a code style for the project.
|
||||
|
||||
\endlist
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
\li \l{Specifying Editor Settings}{Editor}
|
||||
|
||||
\li \l{Specifying Code Style Settings}{Code Style}
|
||||
\li \l{Specifying Code Style}{Code Style}
|
||||
|
||||
\li \l{Specifying Dependencies}{Dependencies}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
\endlist
|
||||
\li \l{Specifying Run Settings}
|
||||
\li \l{Specifying Editor Settings}
|
||||
\li \l{Specifying Code Style Settings}
|
||||
\li \l{Specifying Code Style}
|
||||
\li \l{Specifying Dependencies}
|
||||
\li \l{Specifying Environment Settings}
|
||||
\li \l{Using Custom Output Parsers}
|
||||
|
||||
@@ -739,10 +739,6 @@
|
||||
\note As an exception, imported third party code as well as code
|
||||
interfacing the native APIs (src/support/os_*) can use NULL or 0.
|
||||
|
||||
\section2 C++11 and C++14 Features
|
||||
|
||||
Code should compile with Microsoft Visual Studio 2013, g++ 4.7, and Clang 3.1.
|
||||
|
||||
\section3 Lambdas
|
||||
|
||||
When using lambdas, note the following:
|
||||
@@ -911,11 +907,6 @@
|
||||
Make sure that a class uses \c{override} consistently, either for all overridden functions or
|
||||
for none.
|
||||
|
||||
\section3 Nullptr
|
||||
|
||||
All compilers support \c{nullptr}, but there is no consensus on using it. If in doubt, ask
|
||||
the maintainer of the module whether they prefer using \c{nullptr}.
|
||||
|
||||
\section3 Range-Based for-Loop
|
||||
|
||||
You may use range-based for-loops, but beware of the spurious detachment problem.
|
||||
|
||||
@@ -189,7 +189,7 @@ def build_qtcreator(args, paths):
|
||||
if args.with_cpack:
|
||||
cmake_args += ['-DCPACK_PACKAGE_FILE_NAME=qtcreator' + args.zip_infix]
|
||||
if common.is_linux_platform():
|
||||
cmake_args += ['-DCPACK_INSTALL_PREFIX=/opt']
|
||||
cmake_args += ['-DCPACK_INSTALL_PREFIX=/opt/qt-creator']
|
||||
|
||||
cmake_args += args.config_args
|
||||
|
||||
|
||||
@@ -790,15 +790,24 @@ def qdump__std__valarray(d, value):
|
||||
|
||||
|
||||
def qdump__std__variant(d, value):
|
||||
if d.isMsvcTarget():
|
||||
which = int(value["_Which"])
|
||||
else:
|
||||
which = int(value["_M_index"])
|
||||
type = d.templateArgument(value.type, which)
|
||||
d.putValue("<%s:%s>" % (which, type.name))
|
||||
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
if d.isMsvcTarget():
|
||||
storage = value
|
||||
while which > 0:
|
||||
storage = storage["_Tail"]
|
||||
which = which - 1
|
||||
storage = storage["_Head"]
|
||||
else:
|
||||
storage = value["_M_u"]["_M_first"]["_M_storage"]
|
||||
with Children(d, 1):
|
||||
d.putSubItem("value", storage.cast(type))
|
||||
storage = storage.cast(type)
|
||||
d.putItem(storage)
|
||||
d.putBetterType(type)
|
||||
|
||||
|
||||
def qform__std__vector():
|
||||
return [DisplayFormat.ArrayPlot]
|
||||
@@ -1055,7 +1064,7 @@ def qdump__std__optional(d, value):
|
||||
(payload, pad, initialized) = d.split('{%s}@b' % innerType.name, value)
|
||||
if initialized:
|
||||
d.putItem(payload)
|
||||
d.putBetterType(value.type)
|
||||
d.putBetterType(innerType)
|
||||
else:
|
||||
d.putSpecialValue("uninitialized")
|
||||
|
||||
|
||||
@@ -100,7 +100,13 @@ def main():
|
||||
shell_script.write(commands)
|
||||
shell_script.flush()
|
||||
os.chmod(shell_script.name, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
|
||||
subprocess.call(['/usr/bin/open', '-a', 'Terminal', shell_script.name])
|
||||
# TODO /usr/bin/open doesn't work with notarized app in macOS 13,
|
||||
# use osascript instead (QTCREATORBUG-28683).
|
||||
# This has the disadvantage that the Terminal windows doesn't close
|
||||
# automatically anymore.
|
||||
# subprocess.call(['/usr/bin/open', '-a', 'Terminal', shell_script.name])
|
||||
subprocess.call(['/usr/bin/osascript', '-e', 'tell app "Terminal" to activate'])
|
||||
subprocess.call(['/usr/bin/osascript', '-e', 'tell app "Terminal" to do script "' + shell_script.name + '"'])
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -36922,8 +36922,8 @@ Prověřte, prosím, oprávnění pro přístup k adresáři.</translation>
|
||||
<translation>Styly kódování (*.xml);;Všechny soubory (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Nelze importovat styl kódování z %1</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Nelze importovat styl kódování z "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style</source>
|
||||
|
||||
@@ -4456,8 +4456,8 @@ F.eks., vil "Revision: 15" efterlade grenen ved revision 15.</translat
|
||||
<translation>*** cmake proces afsluttede med afslutningskode %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Target type: </source>
|
||||
<translation>Måltype: </translation>
|
||||
<source>Target type:</source>
|
||||
<translation>Måltype:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No build artifacts</source>
|
||||
@@ -16077,8 +16077,7 @@ Commit nu?</translation>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files: %1
|
||||
</source>
|
||||
<source>Cannot commit %n files: %1</source>
|
||||
<translation>
|
||||
<numerusform>Kan ikke commit %n fil: %1
|
||||
</numerusform>
|
||||
@@ -33328,8 +33327,8 @@ med en adgangskode, som du kan indtaste herunder.</translation>
|
||||
<translation>Kodestile (*.xml);;Alle filer (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Kan ikke importere kodestil fra %1</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Kan ikke importere kodestil fra "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export Code Style</source>
|
||||
|
||||
@@ -692,11 +692,11 @@ Möchten Sie den Pfad zu den Quelldateien in die Zwischenablage kopieren?</trans
|
||||
<translation>CMake mit Änderungen der Konfiguration ausführen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>CMake configuration failed<b><p>The backup of the previous configuration has been restored.</p><p>Issues and "Projects > Build" settings show more information about the failure.</p</source>
|
||||
<source><b>CMake configuration failed<b><p>The backup of the previous configuration has been restored.</p><p>Issues and "Projects > Build" settings show more information about the failure.</p></source>
|
||||
<translation><b>CMake-Konfiguration ist fehlgeschlagen<b/><p>Das Backup der vorherigen Konfiguration wurde wiederhergestellt.</p><p>Details zu dem Problem finden Sie in "Probleme" und den Einstellungen in "Projekte > Erstellen".</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Failed to load project<b><p>Issues and "Projects > Build" settings show more information about the failure.</p</source>
|
||||
<source><b>Failed to load project<b><p>Issues and "Projects > Build" settings show more information about the failure.</p></source>
|
||||
<translation><b>Laden des Projekts fehlgeschlagen</b><p>Details zu dem Problem finden Sie in "Probleme" und den Einstellungen in "Projekte > Erstellen".</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -944,8 +944,8 @@ Möchten Sie den Pfad zu den Quelldateien in die Zwischenablage kopieren?</trans
|
||||
<translation>CMake-Module</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Target type: </source>
|
||||
<translation>Zieltyp: </translation>
|
||||
<source>Target type:</source>
|
||||
<translation>Zieltyp:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No build artifacts</source>
|
||||
@@ -1068,24 +1068,24 @@ Stellen Sie sicher, dass der Wert der CMAKE_BUILD_TYPE-Variable derselbe wie der
|
||||
<translation>CMake hat einen Fehlerwert zurückgegeben: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to rename %1 to %2.</source>
|
||||
<translation>Umbenennen von %1 nach %2 ist fehlgeschlagen.</translation>
|
||||
<source>Failed to rename "%1" to "%2".</source>
|
||||
<translation>Umbenennen von "%1" nach "%2" ist fehlgeschlagen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to copy %1 to %2.</source>
|
||||
<translation>Kopieren von %1 nach %2 ist fehlgeschlagen.</translation>
|
||||
<source>Failed to copy "%1" to "%2".</source>
|
||||
<translation>Kopieren von "%1" nach "%2" ist fehlgeschlagen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to read %1 file</source>
|
||||
<translation>Die Datei %1 konnte nicht gelesen werden</translation>
|
||||
<source>Failed to read file "%1".</source>
|
||||
<translation>Die Datei "%1" konnte nicht gelesen werden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid %1 file</source>
|
||||
<translation>Ungültige Datei %1</translation>
|
||||
<source>Invalid file "%1".</source>
|
||||
<translation>Ungültige Datei "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid "version" in %1 file</source>
|
||||
<translation>Ungültige "version" in Datei %1</translation>
|
||||
<source>Invalid "version" in file "%1".</source>
|
||||
<translation>Ungültige "version" in Datei "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid "configurePresets" section in %1 file</source>
|
||||
@@ -7374,8 +7374,7 @@ Jetzt Commit ausführen?</translation>
|
||||
<translation>Commit "%1" geändert.</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files
|
||||
</source>
|
||||
<source>Cannot commit %n files</source>
|
||||
<translation>
|
||||
<numerusform>Commit schlug für eine Datei fehl
|
||||
</numerusform>
|
||||
@@ -7488,8 +7487,8 @@ Jetzt Commit ausführen?</translation>
|
||||
<translation>branch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><a href="head">Show HEAD</a></source>
|
||||
<translation><a href="head">HEAD anzeigen</a></translation>
|
||||
<source>Show HEAD</source>
|
||||
<translation>HEAD anzeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Commit Information</source>
|
||||
@@ -20898,8 +20897,8 @@ When disabled, moves targets straight to the current mouse position.</source>
|
||||
<translation>Coding-Stile (*.xml);;Alle Dateien (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Der Coding-Stil kann nicht von %1 importiert werden</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Der Coding-Stil kann nicht von "%1" importiert werden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export Code Style</source>
|
||||
@@ -43118,7 +43117,7 @@ You might find further explanations in the Application Output view.</source>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceDialog</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>&New</source>
|
||||
<translation>&Neu</translation>
|
||||
@@ -43745,7 +43744,7 @@ Doppelklicken Sie einen Eintrag um ihn zu ändern.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockWidgetTab</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>Detach</source>
|
||||
<translation>Ablösen</translation>
|
||||
@@ -43758,9 +43757,6 @@ Doppelklicken Sie einen Eintrag um ihn zu ändern.</translation>
|
||||
<source>Close Others</source>
|
||||
<translation>Andere schließen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceModel</name>
|
||||
<message>
|
||||
<source>Workspace</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -43793,10 +43789,6 @@ Doppelklicken Sie einen Eintrag um ihn zu ändern.</translation>
|
||||
<source>Rename Workspace</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Rename</source>
|
||||
<translation>&Umbenennen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename and &Open</source>
|
||||
<translation>Umbenennen und ö&ffnen</translation>
|
||||
@@ -49953,7 +49945,7 @@ Teilnamen können verwendet werden, sofern sie eindeutig sind.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockAreaTitleBar</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>Detach Area</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -49966,9 +49958,6 @@ Teilnamen können verwendet werden, sofern sie eindeutig sind.</translation>
|
||||
<source>Close Other Areas</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockManager</name>
|
||||
<message>
|
||||
<source>Cannot Save Workspace</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -50002,16 +49991,10 @@ Teilnamen können verwendet werden, sofern sie eindeutig sind.</translation>
|
||||
<source>Could not restore workspace %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceNameInputDialog</name>
|
||||
<message>
|
||||
<source>Enter the name of the workspace:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceView</name>
|
||||
<message>
|
||||
<source>Import Workspace</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -51141,8 +51124,7 @@ in "%2" aus.
|
||||
<translation>Vorverarbeitete Datei kann nicht angezeigt werden: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1, falling back to built-in preprocessor.</source>
|
||||
<translatorcomment>Explicitly not using a full stop at the end, because the value for %1 contains one</translatorcomment>
|
||||
<source>Falling back to built-in preprocessor: %1</source>
|
||||
<translation>Falle auf den integrierten Präprozessor zurück: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -35420,8 +35420,8 @@ Pour compiler l'observateur QML, allez à la page des versions de Qt, séle
|
||||
<translation>Styles de code (*.xml);;Tous les fichiers (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Impossible d'importer le style de code à partir de %1</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Impossible d'importer le style de code à partir de "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style</source>
|
||||
|
||||
@@ -16654,7 +16654,7 @@ Kopirati stazu do izvornih datoteka u međuspremnik?</translation>
|
||||
<translation>CMake moduli</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Target type: </source>
|
||||
<source>Target type:</source>
|
||||
<translation>Vrsta odredišta:</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -23048,8 +23048,7 @@ Commit now?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files: %1
|
||||
</source>
|
||||
<source>Cannot commit %n files: %1</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
@@ -33148,7 +33147,7 @@ Row: %4, Column: %5
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -19802,8 +19802,7 @@ Commit now?</source>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files
|
||||
</source>
|
||||
<source>Cannot commit %n files</source>
|
||||
<translation>
|
||||
<numerusform>%n 個のファイルをコミットできません
|
||||
</numerusform>
|
||||
@@ -29197,8 +29196,8 @@ In addition, device connectivity will be tested.</source>
|
||||
<translation>コードスタイル (*.xml);;すべてのファイル (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>%1 からコードスタイルをインポートできません</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>"%1" からコードスタイルをインポートできません。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export Code Style</source>
|
||||
@@ -41860,7 +41859,7 @@ Output:
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceDialog</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>&New</source>
|
||||
<translation>新規作成(&N)</translation>
|
||||
@@ -42539,7 +42538,7 @@ Output:
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockAreaTitleBar</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>Detach Area</source>
|
||||
<translation>エリアを切り離す</translation>
|
||||
@@ -42552,9 +42551,6 @@ Output:
|
||||
<source>Close Other Areas</source>
|
||||
<translation>その他のエリアを閉じる</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockManager</name>
|
||||
<message>
|
||||
<source>Cannot Save Workspace</source>
|
||||
<translation>ワークスペースを保存できません</translation>
|
||||
@@ -42594,7 +42590,7 @@ Output:
|
||||
<name>QtC::Utils</name>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockWidgetTab</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>Detach</source>
|
||||
<translation>切り離す</translation>
|
||||
@@ -42607,16 +42603,10 @@ Output:
|
||||
<source>Close Others</source>
|
||||
<translation>他を閉じる</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceNameInputDialog</name>
|
||||
<message>
|
||||
<source>Enter the name of the workspace:</source>
|
||||
<translation>ワークスペースの名前を入力してください:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceModel</name>
|
||||
<message>
|
||||
<source>Workspace</source>
|
||||
<translation>ワークスペース</translation>
|
||||
@@ -42649,17 +42639,10 @@ Output:
|
||||
<source>Rename Workspace</source>
|
||||
<translation>ワークスペースの名前変更</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Rename</source>
|
||||
<translation>名前を変更(&R)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename and &Open</source>
|
||||
<translation>名前変更して開く(&O)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceView</name>
|
||||
<message>
|
||||
<source>Import Workspace</source>
|
||||
<translation>ワークスペースをインポートする</translation>
|
||||
|
||||
@@ -5499,8 +5499,7 @@ Przebudowanie projektu może pomóc w ich odnalezieniu.</translation>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files: %1
|
||||
</source>
|
||||
<source>Cannot commit %n files: %1</source>
|
||||
<translation>
|
||||
<numerusform>Nie można utworzyć poprawki ze zmian w %n pliku: %1
|
||||
</numerusform>
|
||||
@@ -17814,8 +17813,8 @@ Dodatkowo, przetestowane zostanie połączenie z urządzeniem.</translation>
|
||||
<translation>Style kodu (*.xml);;Wszystkie pliki (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Nie można zaimportować stylu kodu z %1</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Nie można zaimportować stylu kodu z "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export Code Style</source>
|
||||
@@ -38667,8 +38666,8 @@ Błąd: %2</translation>
|
||||
<translation>Moduły CMake</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Target type: </source>
|
||||
<translation>Typ docelowy: </translation>
|
||||
<source>Target type:</source>
|
||||
<translation>Typ docelowy:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No build artifacts</source>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="ru">
|
||||
<context>
|
||||
<name>ADS::DockAreaTitleBar</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>Detach Area</source>
|
||||
<translation>Отцепить область</translation>
|
||||
@@ -15,9 +15,6 @@
|
||||
<source>Close Other Areas</source>
|
||||
<translation>Закрыть другие области</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockManager</name>
|
||||
<message>
|
||||
<source>Cannot Save Workspace</source>
|
||||
<translation>Не удалось сохранить сессию</translation>
|
||||
@@ -52,9 +49,6 @@
|
||||
<source>Could not restore workspace %1</source>
|
||||
<translation>Не удалось восстановить сессию %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockWidgetTab</name>
|
||||
<message>
|
||||
<source>Detach</source>
|
||||
<translation>Отцепить</translation>
|
||||
@@ -67,9 +61,6 @@
|
||||
<source>Close Others</source>
|
||||
<translation>Закрыть другие</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceDialog</name>
|
||||
<message>
|
||||
<source>Workspace Manager</source>
|
||||
<translation>Управление сессиями</translation>
|
||||
@@ -114,9 +105,6 @@
|
||||
<source>Export</source>
|
||||
<translation>Экспортировать</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceModel</name>
|
||||
<message>
|
||||
<source>Workspace</source>
|
||||
<translation>Сессия</translation>
|
||||
@@ -149,24 +137,14 @@
|
||||
<source>Rename Workspace</source>
|
||||
<translation>Переименование сессии</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Rename</source>
|
||||
<translation>&Переименовать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename and &Open</source>
|
||||
<translation>П&ереименовать и открыть</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceNameInputDialog</name>
|
||||
<message>
|
||||
<source>Enter the name of the workspace:</source>
|
||||
<translation>Введите название сессии:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceView</name>
|
||||
<message>
|
||||
<source>Import Workspace</source>
|
||||
<translation>Импорт сессии</translation>
|
||||
@@ -6028,8 +6006,8 @@ For example, "Revision: 15" will leave the branch at revision 15.</sou
|
||||
<translation><Заголовки></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Target type: </source>
|
||||
<translation>Тип цели: </translation>
|
||||
<source>Target type:</source>
|
||||
<translation>Тип цели:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No build artifacts</source>
|
||||
@@ -19732,8 +19710,7 @@ Commit now?</source>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files: %1
|
||||
</source>
|
||||
<source>Cannot commit %n files: %1</source>
|
||||
<translation>
|
||||
<numerusform>Не удалось фиксировать %n файл: %1
|
||||
</numerusform>
|
||||
@@ -42694,8 +42671,8 @@ Row: %4, Column: %5
|
||||
<translation>Стили кода (*.xml);;Все файлы (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Не удалось импортировать стиль кода из %1</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Не удалось импортировать стиль кода из "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export Code Style</source>
|
||||
|
||||
@@ -18300,8 +18300,8 @@ with a password, which you can enter below.</source>
|
||||
<translation>Стилі коду (*.xml);;Усі файли (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<translation>Неможливо імпортувати стиль коду з %1</translation>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation>Неможливо імпортувати стиль коду з "%1".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export Code Style</source>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="zh_CN">
|
||||
<context>
|
||||
<name>ADS::DockAreaTitleBar</name>
|
||||
<name>QtC::ADS</name>
|
||||
<message>
|
||||
<source>Detach Area</source>
|
||||
<translation>分离区域</translation>
|
||||
@@ -15,9 +15,6 @@
|
||||
<source>Close Other Areas</source>
|
||||
<translation>关闭其它区域</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockManager</name>
|
||||
<message>
|
||||
<source>Cannot Save Workspace</source>
|
||||
<translation>无法保存工作区</translation>
|
||||
@@ -52,9 +49,6 @@
|
||||
<source>Could not restore workspace %1</source>
|
||||
<translation>无法恢复 %1 工作区</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::DockWidgetTab</name>
|
||||
<message>
|
||||
<source>Detach</source>
|
||||
<translation>分离</translation>
|
||||
@@ -67,9 +61,6 @@
|
||||
<source>Close Others</source>
|
||||
<translation>关闭其它</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceDialog</name>
|
||||
<message>
|
||||
<source>Workspace Manager</source>
|
||||
<translation>工作区管理器</translation>
|
||||
@@ -114,9 +105,6 @@
|
||||
<source><a href="qthelp://org.qt-project.qtcreator/doc/creator-project-managing-workspaces.html">What is a Workspace?</a></source>
|
||||
<translation><a href="qthelp://org.qt-project.qtcreator/doc/creator-project-managing-workspaces.html">什么是工作区?</a></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceModel</name>
|
||||
<message>
|
||||
<source>Workspace</source>
|
||||
<translation>工作区</translation>
|
||||
@@ -149,24 +137,14 @@
|
||||
<source>Rename Workspace</source>
|
||||
<translation>重命名工作区</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Rename</source>
|
||||
<translation>重命名(&R)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename and &Open</source>
|
||||
<translation>重命名并打开(&O)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceNameInputDialog</name>
|
||||
<message>
|
||||
<source>Enter the name of the workspace:</source>
|
||||
<translation>输入工作区名称:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ADS::WorkspaceView</name>
|
||||
<message>
|
||||
<source>Import Workspace</source>
|
||||
<translation>导入工作区</translation>
|
||||
@@ -5772,11 +5750,11 @@ Copy the path to the source files to the clipboard?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>CMake configuration failed<b><p>The backup of the previous configuration has been restored.</p><p>Issues and "Projects > Build" settings show more information about the failure.</p</source>
|
||||
<source><b>CMake configuration failed<b><p>The backup of the previous configuration has been restored.</p><p>Issues and "Projects > Build" settings show more information about the failure.</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Failed to load project<b><p>Issues and "Projects > Build" settings show more information about the failure.</p</source>
|
||||
<source><b>Failed to load project<b><p>Issues and "Projects > Build" settings show more information about the failure.</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -6056,7 +6034,7 @@ Copy the path to the source files to the clipboard?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Target type: </source>
|
||||
<source>Target type:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -6383,23 +6361,23 @@ Make sure that CMAKE_BUILD_TYPE variable matches the "Build type" fiel
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to rename %1 to %2.</source>
|
||||
<source>Failed to rename "%1" to "%2".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to copy %1 to %2.</source>
|
||||
<source>Failed to copy "%1" to "%2".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to read %1 file</source>
|
||||
<source>Failed to read file "%1".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid %1 file</source>
|
||||
<source>Invalid file "%1".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid "version" in %1 file</source>
|
||||
<source>Invalid "version" in file "%1".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -11320,7 +11298,7 @@ to version control (%2)
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1, falling back to built-in preprocessor.</source>
|
||||
<source>Falling back to built-in preprocessor: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -15717,7 +15695,7 @@ Install the "Qt Debug Information Files" Package from the Maintenance
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempts to correct the location of a breakpoint based on file and line number shouldit be in a comment or in a line for which no code is generated. The correction is based on the code model.</source>
|
||||
<source>Attempts to correct the location of a breakpoint based on file and line number should it be in a comment or in a line for which no code is generated. The correction is based on the code model.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -20760,8 +20738,7 @@ Commit now?</source>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Cannot commit %n files
|
||||
</source>
|
||||
<source>Cannot commit %n files</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
@@ -21236,7 +21213,7 @@ instead of its installation directory when run outside git bash.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><a href="head">Show HEAD</a></source>
|
||||
<source>Show HEAD</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -24112,7 +24112,7 @@ Remote error output was: %1</source>
|
||||
<translation>代碼風格 (*.xml);;所有檔案(*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot import code style from %1</source>
|
||||
<source>Cannot import code style from "%1".</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace AdvancedDockingSystem {
|
||||
namespace ADS {
|
||||
|
||||
struct Tr
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QtC::AdvancedDockingSystem)
|
||||
Q_DECLARE_TR_FUNCTIONS(QtC::ADS)
|
||||
};
|
||||
|
||||
} // AdvancedDockingSystem
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include "dockareatitlebar.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "advanceddockingsystemtr.h"
|
||||
#include "dockareatabbar.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockcomponentsfactory.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "dockwidget.h"
|
||||
@@ -13,7 +15,6 @@
|
||||
#include "floatingdockcontainer.h"
|
||||
#include "floatingdragpreview.h"
|
||||
#include "iconprovider.h"
|
||||
#include "dockcomponentsfactory.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QLoggingCategory>
|
||||
@@ -119,7 +120,7 @@ namespace ADS
|
||||
#endif
|
||||
QObject::connect(tabsMenu, &QMenu::aboutToShow, q, &DockAreaTitleBar::onTabsMenuAboutToShow);
|
||||
m_tabsMenuButton->setMenu(tabsMenu);
|
||||
internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs"));
|
||||
internal::setToolTip(m_tabsMenuButton, Tr::tr("List All Tabs"));
|
||||
m_tabsMenuButton->setSizePolicy(sizePolicy);
|
||||
m_tabsMenuButton->setIconSize(iconSize);
|
||||
m_tabsMenuButton->setFixedSize(buttonSize);
|
||||
@@ -133,7 +134,7 @@ namespace ADS
|
||||
m_undockButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasUndockButton));
|
||||
m_undockButton->setObjectName("detachGroupButton");
|
||||
//m_undockButton->setAutoRaise(true);
|
||||
internal::setToolTip(m_undockButton, QObject::tr("Detach Group"));
|
||||
internal::setToolTip(m_undockButton, Tr::tr("Detach Group"));
|
||||
internal::setButtonIcon(m_undockButton,
|
||||
QStyle::SP_TitleBarNormalButton,
|
||||
ADS::DockAreaUndockIcon);
|
||||
@@ -154,9 +155,9 @@ namespace ADS
|
||||
QStyle::SP_TitleBarCloseButton,
|
||||
ADS::DockAreaCloseIcon);
|
||||
if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab))
|
||||
internal::setToolTip(m_closeButton, QObject::tr("Close Active Tab"));
|
||||
internal::setToolTip(m_closeButton, Tr::tr("Close Active Tab"));
|
||||
else
|
||||
internal::setToolTip(m_closeButton, QObject::tr("Close Group"));
|
||||
internal::setToolTip(m_closeButton, Tr::tr("Close Group"));
|
||||
|
||||
m_closeButton->setSizePolicy(sizePolicy);
|
||||
m_closeButton->setIconSize(iconSize);
|
||||
@@ -532,14 +533,14 @@ namespace ADS
|
||||
return;
|
||||
|
||||
QMenu menu(this);
|
||||
auto action = menu.addAction(tr("Detach Area"),
|
||||
auto action = menu.addAction(Tr::tr("Detach Area"),
|
||||
this,
|
||||
&DockAreaTitleBar::onUndockButtonClicked);
|
||||
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable));
|
||||
menu.addSeparator();
|
||||
action = menu.addAction(tr("Close Area"), this, &DockAreaTitleBar::onCloseButtonClicked);
|
||||
action = menu.addAction(Tr::tr("Close Area"), this, &DockAreaTitleBar::onCloseButtonClicked);
|
||||
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
|
||||
menu.addAction(tr("Close Other Areas"), d->m_dockArea, &DockAreaWidget::closeOtherAreas);
|
||||
menu.addAction(Tr::tr("Close Other Areas"), d->m_dockArea, &DockAreaWidget::closeOtherAreas);
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
||||
|
||||
@@ -576,8 +576,8 @@ namespace ADS
|
||||
d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime());
|
||||
else
|
||||
QMessageBox::warning(parentWidget(),
|
||||
tr("Cannot Save Workspace"),
|
||||
tr("Could not save workspace to file %1")
|
||||
Tr::tr("Cannot Save Workspace"),
|
||||
Tr::tr("Could not save workspace to file %1")
|
||||
.arg(workspaceNameToFilePath(d->m_workspaceName)
|
||||
.toUserOutput()));
|
||||
|
||||
@@ -684,8 +684,8 @@ namespace ADS
|
||||
emit workspaceListChanged();
|
||||
} else {
|
||||
QMessageBox::warning(parentWidget(),
|
||||
tr("Cannot Save Workspace"),
|
||||
tr("Could not save workspace to file %1")
|
||||
Tr::tr("Cannot Save Workspace"),
|
||||
Tr::tr("Could not save workspace to file %1")
|
||||
.arg(workspaceNameToFilePath(d->m_workspaceName)
|
||||
.toUserOutput()));
|
||||
}
|
||||
@@ -752,11 +752,11 @@ namespace ADS
|
||||
*/
|
||||
bool DockManager::confirmWorkspaceDelete(const QStringList &workspace)
|
||||
{
|
||||
const QString title = workspace.size() == 1 ? tr("Delete Workspace")
|
||||
: tr("Delete Workspaces");
|
||||
const QString title = workspace.size() == 1 ? Tr::tr("Delete Workspace")
|
||||
: Tr::tr("Delete Workspaces");
|
||||
const QString question = workspace.size() == 1
|
||||
? tr("Delete workspace %1?").arg(workspace.first())
|
||||
: tr("Delete these workspaces?\n %1")
|
||||
? Tr::tr("Delete workspace %1?").arg(workspace.first())
|
||||
: Tr::tr("Delete these workspaces?\n %1")
|
||||
.arg(workspace.join("\n "));
|
||||
return QMessageBox::question(parentWidget(),
|
||||
title,
|
||||
@@ -964,8 +964,8 @@ namespace ADS
|
||||
|
||||
if (!data) {
|
||||
QMessageBox::warning(parentWidget(),
|
||||
tr("Cannot Restore Workspace"),
|
||||
tr("Could not restore workspace %1")
|
||||
Tr::tr("Cannot Restore Workspace"),
|
||||
Tr::tr("Could not restore workspace %1")
|
||||
.arg(fileName.toUserOutput()));
|
||||
|
||||
qCWarning(adsLog) << QString("Could not restore workspace %1: %2")
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "dockwidgettab.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "advanceddockingsystemtr.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockoverlay.h"
|
||||
@@ -158,7 +159,7 @@ namespace ADS
|
||||
m_closeButton->setIconSize(QSize(11, 11));
|
||||
m_closeButton->setFixedSize(QSize(17, 17));
|
||||
q->onDockWidgetFeaturesChanged();
|
||||
internal::setToolTip(m_closeButton, QObject::tr("Close Tab"));
|
||||
internal::setToolTip(m_closeButton, Tr::tr("Close Tab"));
|
||||
QObject::connect(m_closeButton,
|
||||
&QAbstractButton::clicked,
|
||||
q,
|
||||
@@ -410,12 +411,12 @@ namespace ADS
|
||||
const bool isNotOnlyTabInContainer = !d->m_dockArea->dockContainer()->hasTopLevelDockWidget();
|
||||
const bool isDetachable = isFloatable && isNotOnlyTabInContainer;
|
||||
|
||||
auto action = menu.addAction(tr("Detach"), this, &DockWidgetTab::detachDockWidget);
|
||||
auto action = menu.addAction(Tr::tr("Detach"), this, &DockWidgetTab::detachDockWidget);
|
||||
action->setEnabled(isDetachable);
|
||||
menu.addSeparator();
|
||||
action = menu.addAction(tr("Close"), this, &DockWidgetTab::closeRequested);
|
||||
action = menu.addAction(Tr::tr("Close"), this, &DockWidgetTab::closeRequested);
|
||||
action->setEnabled(isClosable());
|
||||
menu.addAction(tr("Close Others"), this, &DockWidgetTab::closeOtherTabsRequested);
|
||||
menu.addAction(Tr::tr("Close Others"), this, &DockWidgetTab::closeOtherTabsRequested);
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "workspacedialog.h"
|
||||
|
||||
#include "advanceddockingsystemtr.h"
|
||||
#include "dockmanager.h"
|
||||
#include "workspaceview.h"
|
||||
|
||||
@@ -65,7 +66,7 @@ WorkspaceNameInputDialog::WorkspaceNameInputDialog(DockManager *manager, QWidget
|
||||
: QDialog(parent)
|
||||
, m_manager(manager)
|
||||
{
|
||||
auto label = new QLabel(tr("Enter the name of the workspace:"), this);
|
||||
auto label = new QLabel(Tr::tr("Enter the name of the workspace:"), this);
|
||||
m_newWorkspaceLineEdit = new QLineEdit(this);
|
||||
m_newWorkspaceLineEdit->setValidator(new WorkspaceValidator(this, m_manager->workspaces()));
|
||||
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
|
||||
@@ -113,21 +114,21 @@ WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_manager(manager)
|
||||
, m_workspaceView(new WorkspaceView(manager))
|
||||
, m_btCreateNew(new QPushButton(tr("&New")))
|
||||
, m_btRename(new QPushButton(tr("&Rename")))
|
||||
, m_btClone(new QPushButton(tr("C&lone")))
|
||||
, m_btDelete(new QPushButton(tr("&Delete")))
|
||||
, m_btReset(new QPushButton(tr("Reset")))
|
||||
, m_btSwitch(new QPushButton(tr("&Switch To")))
|
||||
, m_btImport(new QPushButton(tr("Import")))
|
||||
, m_btExport(new QPushButton(tr("Export")))
|
||||
, m_autoLoadCheckBox(new QCheckBox(tr("Restore last workspace on startup")))
|
||||
, m_btCreateNew(new QPushButton(Tr::tr("&New")))
|
||||
, m_btRename(new QPushButton(Tr::tr("&Rename")))
|
||||
, m_btClone(new QPushButton(Tr::tr("C&lone")))
|
||||
, m_btDelete(new QPushButton(Tr::tr("&Delete")))
|
||||
, m_btReset(new QPushButton(Tr::tr("Reset")))
|
||||
, m_btSwitch(new QPushButton(Tr::tr("&Switch To")))
|
||||
, m_btImport(new QPushButton(Tr::tr("Import")))
|
||||
, m_btExport(new QPushButton(Tr::tr("Export")))
|
||||
, m_autoLoadCheckBox(new QCheckBox(Tr::tr("Restore last workspace on startup")))
|
||||
{
|
||||
setWindowTitle(tr("Workspace Manager"));
|
||||
setWindowTitle(Tr::tr("Workspace Manager"));
|
||||
|
||||
m_workspaceView->setActivationMode(Utils::DoubleClickActivation);
|
||||
|
||||
QLabel *whatsAWorkspaceLabel = new QLabel(tr("<a href=\"qthelp://org.qt-project.qtcreator/doc/"
|
||||
QLabel *whatsAWorkspaceLabel = new QLabel(Tr::tr("<a href=\"qthelp://org.qt-project.qtcreator/doc/"
|
||||
"creator-project-managing-workspaces.html\">What is a Workspace?</a>"));
|
||||
whatsAWorkspaceLabel->setOpenExternalLinks(true);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "workspacemodel.h"
|
||||
|
||||
#include "advanceddockingsystemtr.h"
|
||||
#include "dockmanager.h"
|
||||
#include "workspacedialog.h"
|
||||
|
||||
@@ -42,10 +43,10 @@ QVariant WorkspaceModel::headerData(int section, Qt::Orientation orientation, in
|
||||
case Qt::DisplayRole:
|
||||
switch (section) {
|
||||
case 0:
|
||||
result = tr("Workspace");
|
||||
result = Tr::tr("Workspace");
|
||||
break;
|
||||
case 1:
|
||||
result = tr("Last Modified");
|
||||
result = Tr::tr("Last Modified");
|
||||
break;
|
||||
} // switch (section)
|
||||
break;
|
||||
@@ -162,8 +163,8 @@ void WorkspaceModel::resetWorkspaces()
|
||||
void WorkspaceModel::newWorkspace(QWidget *parent)
|
||||
{
|
||||
WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent);
|
||||
workspaceInputDialog.setWindowTitle(tr("New Workspace Name"));
|
||||
workspaceInputDialog.setActionText(tr("&Create"), tr("Create and &Open"));
|
||||
workspaceInputDialog.setWindowTitle(Tr::tr("New Workspace Name"));
|
||||
workspaceInputDialog.setActionText(Tr::tr("&Create"), Tr::tr("Create and &Open"));
|
||||
|
||||
runWorkspaceNameInputDialog(&workspaceInputDialog, [this](const QString &newName) {
|
||||
m_manager->createWorkspace(newName);
|
||||
@@ -173,8 +174,8 @@ void WorkspaceModel::newWorkspace(QWidget *parent)
|
||||
void WorkspaceModel::cloneWorkspace(QWidget *parent, const QString &workspace)
|
||||
{
|
||||
WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent);
|
||||
workspaceInputDialog.setWindowTitle(tr("New Workspace Name"));
|
||||
workspaceInputDialog.setActionText(tr("&Clone"), tr("Clone and &Open"));
|
||||
workspaceInputDialog.setWindowTitle(Tr::tr("New Workspace Name"));
|
||||
workspaceInputDialog.setActionText(Tr::tr("&Clone"), Tr::tr("Clone and &Open"));
|
||||
workspaceInputDialog.setValue(workspace + " (2)");
|
||||
|
||||
runWorkspaceNameInputDialog(&workspaceInputDialog, [this, workspace](const QString &newName) {
|
||||
@@ -195,8 +196,8 @@ void WorkspaceModel::deleteWorkspaces(const QStringList &workspaces)
|
||||
void WorkspaceModel::renameWorkspace(QWidget *parent, const QString &workspace)
|
||||
{
|
||||
WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent);
|
||||
workspaceInputDialog.setWindowTitle(tr("Rename Workspace"));
|
||||
workspaceInputDialog.setActionText(tr("&Rename"), tr("Rename and &Open"));
|
||||
workspaceInputDialog.setWindowTitle(Tr::tr("Rename Workspace"));
|
||||
workspaceInputDialog.setActionText(Tr::tr("&Rename"), Tr::tr("Rename and &Open"));
|
||||
workspaceInputDialog.setValue(workspace);
|
||||
|
||||
runWorkspaceNameInputDialog(&workspaceInputDialog, [this, workspace](const QString &newName) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "workspaceview.h"
|
||||
|
||||
#include "dockmanager.h"
|
||||
#include "advanceddockingsystemtr.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
@@ -95,7 +96,7 @@ void WorkspaceView::importWorkspace()
|
||||
static QString lastDir;
|
||||
const QString currentDir = lastDir.isEmpty() ? "" : lastDir;
|
||||
const auto fileName = QFileDialog::getOpenFileName(this,
|
||||
tr("Import Workspace"),
|
||||
Tr::tr("Import Workspace"),
|
||||
currentDir,
|
||||
"Workspaces (*" + m_manager->workspaceFileExtension() + ")");
|
||||
|
||||
@@ -112,7 +113,7 @@ void WorkspaceView::exportCurrentWorkspace()
|
||||
QFileInfo fileInfo(currentDir, m_manager->workspaceNameToFileName(currentWorkspace()));
|
||||
|
||||
const auto fileName = QFileDialog::getSaveFileName(this,
|
||||
tr("Export Workspace"),
|
||||
Tr::tr("Export Workspace"),
|
||||
fileInfo.absoluteFilePath(),
|
||||
"Workspaces (*" + m_manager->workspaceFileExtension() + ")");
|
||||
|
||||
|
||||
@@ -197,9 +197,12 @@ bool IPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||
/*!
|
||||
Registers a function object that creates a test object.
|
||||
|
||||
The ownership of the created object is transferred to the plugin.
|
||||
The created objects are meant to be passed on to \l QTest::qExec().
|
||||
|
||||
\sa createTestObjects()
|
||||
The function objects will be called if the user starts \QC with
|
||||
\c {-test PluginName} or \c {-test all}.
|
||||
|
||||
The ownership of the created object is transferred to the plugin.
|
||||
*/
|
||||
|
||||
void IPlugin::addTestCreator(const TestCreator &creator)
|
||||
@@ -208,15 +211,7 @@ void IPlugin::addTestCreator(const TestCreator &creator)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns objects that are meant to be passed on to \l QTest::qExec().
|
||||
|
||||
This function will be called if the user starts \QC with
|
||||
\c {-test PluginName} or \c {-test all}.
|
||||
|
||||
By default, this function creates test objects using the functors
|
||||
added by \c addTest().
|
||||
|
||||
The ownership of returned objects is transferred to caller.
|
||||
\deprecated [10.0] Use addTest() instead
|
||||
|
||||
\sa addTest()
|
||||
*/
|
||||
|
||||
@@ -33,6 +33,9 @@ public:
|
||||
virtual QObject *remoteCommand(const QStringList & /* options */,
|
||||
const QString & /* workingDirectory */,
|
||||
const QStringList & /* arguments */) { return nullptr; }
|
||||
|
||||
|
||||
// Deprecated in 10.0, use addTest()
|
||||
virtual QVector<QObject *> createTestObjects() const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -29,6 +29,10 @@ public:
|
||||
explicit Environment(const NameValueDictionary &dict) : m_dict(dict) {}
|
||||
|
||||
QString value(const QString &key) const { return m_dict.value(key); }
|
||||
QString value_or(const QString &key, const QString &defaultValue) const
|
||||
{
|
||||
return m_dict.hasKey(key) ? m_dict.value(key) : defaultValue;
|
||||
}
|
||||
bool hasKey(const QString &key) const { return m_dict.hasKey(key); }
|
||||
|
||||
void set(const QString &key, const QString &value, bool enabled = true) { m_dict.set(key, value, enabled); }
|
||||
|
||||
@@ -47,6 +47,11 @@ struct FilePathInfo
|
||||
|
||||
Q_DECLARE_FLAGS(FileFlags, FileFlag)
|
||||
|
||||
bool operator==(const FilePathInfo &other) const
|
||||
{
|
||||
return fileSize == other.fileSize && fileFlags == other.fileFlags
|
||||
&& lastModified == other.lastModified;
|
||||
}
|
||||
|
||||
qint64 fileSize = 0;
|
||||
FileFlags fileFlags;
|
||||
|
||||
@@ -586,8 +586,36 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
|
||||
|
||||
#endif // QT_WIDGETS_LIB
|
||||
|
||||
// Converts a hex string of the st_mode field of a stat structure to FileFlags.
|
||||
FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString)
|
||||
{
|
||||
// Copied from stat.h
|
||||
enum st_mode {
|
||||
IFMT = 00170000,
|
||||
IFSOCK = 0140000,
|
||||
IFLNK = 0120000,
|
||||
IFREG = 0100000,
|
||||
IFBLK = 0060000,
|
||||
IFDIR = 0040000,
|
||||
IFCHR = 0020000,
|
||||
IFIFO = 0010000,
|
||||
ISUID = 0004000,
|
||||
ISGID = 0002000,
|
||||
ISVTX = 0001000,
|
||||
IRWXU = 00700,
|
||||
IRUSR = 00400,
|
||||
IWUSR = 00200,
|
||||
IXUSR = 00100,
|
||||
IRWXG = 00070,
|
||||
IRGRP = 00040,
|
||||
IWGRP = 00020,
|
||||
IXGRP = 00010,
|
||||
IRWXO = 00007,
|
||||
IROTH = 00004,
|
||||
IWOTH = 00002,
|
||||
IXOTH = 00001,
|
||||
};
|
||||
|
||||
bool ok = false;
|
||||
uint mode = hexString.toUInt(&ok, 16);
|
||||
|
||||
@@ -595,31 +623,32 @@ FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString
|
||||
|
||||
FilePathInfo::FileFlags result;
|
||||
|
||||
if (mode & 0x100) // S_IRUSR
|
||||
if (mode & IRUSR)
|
||||
result |= FilePathInfo::ReadOwnerPerm;
|
||||
if (mode & 0x80) // S_IWUSR
|
||||
if (mode & IWUSR)
|
||||
result |= FilePathInfo::WriteOwnerPerm;
|
||||
if (mode & 0x40) // S_IXUSR
|
||||
if (mode & IXUSR)
|
||||
result |= FilePathInfo::ExeOwnerPerm;
|
||||
if (mode & 0x20) // S_IRGRP
|
||||
if (mode & IRGRP)
|
||||
result |= FilePathInfo::ReadGroupPerm;
|
||||
if (mode & 0x10) // S_IWGRP
|
||||
if (mode & IWGRP)
|
||||
result |= FilePathInfo::WriteGroupPerm;
|
||||
if (mode & 0x8) // S_IXGRP
|
||||
if (mode & IXGRP)
|
||||
result |= FilePathInfo::ExeGroupPerm;
|
||||
if (mode & 0x4) // S_IROTH
|
||||
if (mode & IROTH)
|
||||
result |= FilePathInfo::ReadOtherPerm;
|
||||
if (mode & 0x2) // S_IWOTH
|
||||
if (mode & IWOTH)
|
||||
result |= FilePathInfo::WriteOtherPerm;
|
||||
if (mode & 0x1) // S_IXOTH
|
||||
if (mode & IXOTH)
|
||||
result |= FilePathInfo::ExeOtherPerm;
|
||||
if (mode & 0xa000) // S_IFLNK
|
||||
|
||||
if ((mode & IFMT) == IFLNK)
|
||||
result |= FilePathInfo::LinkType;
|
||||
if (mode & 0x8000) // S_IFREG
|
||||
if ((mode & IFMT) == IFREG)
|
||||
result |= FilePathInfo::FileType;
|
||||
if (mode & 0x4000) // S_IFDIR
|
||||
if ((mode & IFMT) == IFDIR)
|
||||
result |= FilePathInfo::DirectoryType;
|
||||
if (mode & 0x6000) // S_IFBLK
|
||||
if ((mode & IFMT) == IFBLK)
|
||||
result |= FilePathInfo::LocalDiskFlag;
|
||||
|
||||
if (result != 0) // There is no Exist flag, but if anything was set before, it must exist.
|
||||
|
||||
@@ -589,46 +589,715 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
|
||||
/*!
|
||||
\class Utils::TaskTree
|
||||
|
||||
\brief The TaskTree class is responsible for running async task tree structure defined in a
|
||||
\inheaderfile utils/tasktree.h
|
||||
\inmodule QtCreator
|
||||
\ingroup mainclasses
|
||||
\brief The TaskTree class runs an async task tree structure defined in a
|
||||
declarative way.
|
||||
|
||||
The Tasking namespace (similar to Layouting) is designer for building declarative task
|
||||
tree structure. The examples of tasks that can be used inside TaskTree are e.g. QtcProcess,
|
||||
FileTransfer, AsyncTask<>. It's extensible, so any possible asynchronous task may be
|
||||
integrated and used inside TaskTree. TaskTree enables to form sophisticated mixtures of
|
||||
parallel or sequential flow of tasks in tree form.
|
||||
Use the Tasking namespace to build extensible, declarative task tree
|
||||
structures that contain possibly asynchronous tasks, such as QtcProcess,
|
||||
FileTransfer, or AsyncTask<ReturnType>. TaskTree structures enable you
|
||||
to create a sophisticated mixture of a parallel or sequential flow of tasks
|
||||
in the form of a tree and to run it any time later.
|
||||
|
||||
The TaskTree consist of Group root element. The Group can have nested Group elements.
|
||||
The Group may also contain any number of tasks, e.g. Process, FileTransfer,
|
||||
AsyncTask<ReturnType>.
|
||||
\section1 Root Element and Tasks
|
||||
|
||||
Each Group can contain various other elements describing the processing flow.
|
||||
The TaskTree has a mandatory Group root element, which may contain
|
||||
any number of tasks of various types, such as Process, FileTransfer,
|
||||
or AsyncTask<ReturnType>:
|
||||
|
||||
The execute mode elements of a Group specify how direct children of a Group will be executed.
|
||||
The "sequential" element of a Group means all tasks in a group will be executed in chain,
|
||||
so after the previous task finished, the next will be started. This is the default Group
|
||||
behavior. The "parallel" element of a Group means that all tasks in a Group will be started
|
||||
simultaneously. When having nested Groups hierarchy, we may mix execute modes freely
|
||||
and each Group will be executed according to its own execute mode.
|
||||
The "sequential" mode may be very useful in cases when result data from one task is need to
|
||||
be passed as an input data to the other task - sequential mode guarantees that the next
|
||||
task will be started only after the previous task has already finished.
|
||||
\code
|
||||
using namespace Utils;
|
||||
using namespace Tasking;
|
||||
|
||||
There are many possible "workflow" behaviors for the Group. E.g. "stopOnError",
|
||||
the default Group workflow behavior, means that whenever any direct child of a Group
|
||||
finished with error, we immediately stop processing other tasks in this group
|
||||
(in parallel case) by canceling them and immediately finish the Group with error.
|
||||
const Group root {
|
||||
Process(...),
|
||||
Async<int>(...),
|
||||
Transfer(...)
|
||||
};
|
||||
|
||||
The user of TaskTree specifies how to setup his tasks (by providing TaskSetupHandlers)
|
||||
and how to collect output data from the finished tasks (by providing TaskEndHandlers).
|
||||
The user don't need to create tasks manually - TaskTree will create them when it's needed
|
||||
and destroy when they are not used anymore.
|
||||
TaskTree *taskTree = new TaskTree(root);
|
||||
connect(taskTree, &TaskTree::done, ...); // a successfully finished handler
|
||||
connect(taskTree, &TaskTree::errorOccurred, ...); // an erroneously finished handler
|
||||
taskTree->start();
|
||||
\endcode
|
||||
|
||||
Whenever a Group elemenent is being started, the Group's OnGroupSetup handler is being called.
|
||||
Just after the handler finishes, all Group's children are executed (either in parallel or
|
||||
in sequence). When all Group's children finished, one of Group's OnGroupDone or OnGroupError
|
||||
is being executed, depending on results of children execution and Group's workflow policy.
|
||||
The task tree above has a top level element of the Group type that contains
|
||||
tasks of the type QtcProcess, FileTransfer, and AsyncTask<int>.
|
||||
After taskTree->start() is called, the tasks are run in a chain, starting
|
||||
with Process. When Process finishes successfully, the Async<int> task is
|
||||
started. Finally, when the asynchronous task finishes successfully, the
|
||||
FileTransfer task is started.
|
||||
|
||||
When the last running task finishes with success, the task tree is considered
|
||||
to have run successfully and the TaskTree::done() signal is emitted.
|
||||
When a task finishes with an error, the execution of the task tree is stopped
|
||||
and the remaining tasks are skipped. The task tree finishes with an error and
|
||||
sends the TaskTree::errorOccurred() signal.
|
||||
|
||||
\section1 Groups
|
||||
|
||||
The parent of the Group sees it as a single task. Like other tasks,
|
||||
the group can be started and it can finish with success or an error.
|
||||
The Group elements can be nested to create a tree structure:
|
||||
|
||||
\code
|
||||
const Group root {
|
||||
Group {
|
||||
parallel,
|
||||
Process(...),
|
||||
Async<int>(...)
|
||||
},
|
||||
Transfer(...)
|
||||
};
|
||||
\endcode
|
||||
|
||||
The example above differs from the first example in that the root element has
|
||||
a subgroup that contains the Process and Async<int> tasks. The subgroup is a
|
||||
sibling element of the Transfer task in the root. The subgroup contains an
|
||||
additional \e parallel element that instructs its Group to execute its tasks
|
||||
in parallel.
|
||||
|
||||
So, when the tree above is started, the Process and Async<int> tasks start
|
||||
immediately and run in parallel. Since the root group doesn't contain a
|
||||
\e parallel element, its direct child tasks are run in sequence. Thus, the
|
||||
Transfer task starts when the whole subgroup finishes. The group is
|
||||
considered as finished when all its tasks have finished. The order in which
|
||||
the tasks finish is not relevant.
|
||||
|
||||
So, depending on which task lasts longer (Process or Async<int>), the
|
||||
following scenarios can take place:
|
||||
|
||||
\table
|
||||
\header
|
||||
\li Scenario 1
|
||||
\li Scenario 2
|
||||
\row
|
||||
\li Root Group starts
|
||||
\li Root Group starts
|
||||
\row
|
||||
\li Sub Group starts
|
||||
\li Sub Group starts
|
||||
\row
|
||||
\li Process starts
|
||||
\li Process starts
|
||||
\row
|
||||
\li Async<int> starts
|
||||
\li Async<int> starts
|
||||
\row
|
||||
\li ...
|
||||
\li ...
|
||||
\row
|
||||
\li \b {Process finishes}
|
||||
\li \b {Async<int> finishes}
|
||||
\row
|
||||
\li ...
|
||||
\li ...
|
||||
\row
|
||||
\li \b {Async<int> finishes}
|
||||
\li \b {Process finishes}
|
||||
\row
|
||||
\li Sub Group finishes
|
||||
\li Sub Group finishes
|
||||
\row
|
||||
\li Transfer starts
|
||||
\li Transfer starts
|
||||
\row
|
||||
\li ...
|
||||
\li ...
|
||||
\row
|
||||
\li Transfer finishes
|
||||
\li Transfer finishes
|
||||
\row
|
||||
\li Root Group finishes
|
||||
\li Root Group finishes
|
||||
\endtable
|
||||
|
||||
The differences between the scenarios are marked with bold. Three dots mean
|
||||
that an unspecified amount of time passes between previous and next events
|
||||
(a task or tasks continue to run). No dots between events
|
||||
means that they occur synchronously.
|
||||
|
||||
The presented scenarios assume that all tasks run successfully. If a task
|
||||
fails during execution, the task tree finishes with an error. In particular,
|
||||
when Process finishes with an error while Async<int> is still being executed,
|
||||
Async<int> is automatically stopped, the subgroup finishes with an error,
|
||||
Transfer is skipped, and the tree finishes with an error.
|
||||
|
||||
\section1 Task Types
|
||||
|
||||
Each task type is associated with its corresponding task class that executes
|
||||
the task. For example, a Process task inside a task tree is associated with
|
||||
the QtcProcess class that executes the process. The associated objects are
|
||||
automatically created, started, and destructed exclusively by the task tree
|
||||
at the appropriate time.
|
||||
|
||||
If a root group consists of five sequential Process tasks, and the task tree
|
||||
executes the group, it creates an instance of QtcProcess for the first
|
||||
Process task and starts it. If the QtcProcess instance finishes successfully,
|
||||
the task tree destructs it and creates a new QtcProcess instance for the
|
||||
second Process, and so on. If the first task finishes with an error, the task
|
||||
tree stops creating QtcProcess instances, and the root group finishes with an
|
||||
error.
|
||||
|
||||
The following table shows examples of task types and their corresponding task
|
||||
classes:
|
||||
|
||||
\table
|
||||
\header
|
||||
\li Task Type (Tasking Namespace)
|
||||
\li Associated Task Class
|
||||
\li Brief Description
|
||||
\row
|
||||
\li Process
|
||||
\li Utils::QtcProcess
|
||||
\li Starts processes.
|
||||
\row
|
||||
\li Async<ReturnType>
|
||||
\li Utils::AsyncTask<ReturnType>
|
||||
\li Starts asynchronous tasks; run in separate thread.
|
||||
\row
|
||||
\li Tree
|
||||
\li Utils::TaskTree
|
||||
\li Starts a nested task tree.
|
||||
\row
|
||||
\li Transfer
|
||||
\li ProjectExplorer::FileTransfer
|
||||
\li Starts file transfer between different devices.
|
||||
\endtable
|
||||
|
||||
\section1 Task Handlers
|
||||
|
||||
Use Task handlers to set up a task for execution and to enable reading
|
||||
the output data from the task when it finishes with success or an error.
|
||||
|
||||
\section2 Task Start Handler
|
||||
|
||||
When a corresponding task class object is created and before it's started,
|
||||
the task tree invokes a mandatory user-provided setup handler. The setup
|
||||
handler should always take a \e reference to the associated task class object:
|
||||
|
||||
\code
|
||||
const auto onSetup = [](QtcProcess &process) {
|
||||
process.setCommand({"sleep", {"3"}});
|
||||
};
|
||||
const Group root {
|
||||
Process(onSetup)
|
||||
};
|
||||
\endcode
|
||||
|
||||
You can modify the passed QtcProcess in the setup handler, so that the task
|
||||
tree can start the process according to your configuration.
|
||||
You do not need to call \e {process.start();} in the setup handler,
|
||||
as the task tree calls it when needed. The setup handler is mandatory
|
||||
and must be the first argument of the task's constructor.
|
||||
|
||||
Optionally, the setup handler may return a TaskAction. The returned
|
||||
TaskAction influences the further start behavior of a given task. The
|
||||
possible values are:
|
||||
|
||||
\table
|
||||
\header
|
||||
\li TaskAction Value
|
||||
\li Brief Description
|
||||
\row
|
||||
\li Continue
|
||||
\li The task is started normally. This is the default behavior when the
|
||||
setup handler doesn't return TaskAction (that is, its return type is
|
||||
void).
|
||||
\row
|
||||
\li StopWithDone
|
||||
\li The task won't be started and it will report success to its parent.
|
||||
\row
|
||||
\li StopWithError
|
||||
\li The task won't be started and it will report an error to its parent.
|
||||
\endtable
|
||||
|
||||
This is useful for running a task only when a condition is met and the data
|
||||
needed to evaluate this condition is not known until previously started tasks
|
||||
finish. This way, the setup handler dynamically decides whether to start the
|
||||
corresponding task normally or skip it and report success or an error.
|
||||
For more information about inter-task data exchange, see \l Storage.
|
||||
|
||||
\section2 Task's Done and Error Handlers
|
||||
|
||||
When a running task finishes, the task tree invokes an optionally provided
|
||||
done or error handler. Both handlers should always take a \e {const reference}
|
||||
to the associated task class object:
|
||||
|
||||
\code
|
||||
const auto onSetup = [](QtcProcess &process) {
|
||||
process.setCommand({"sleep", {"3"}});
|
||||
};
|
||||
const auto onDone = [](const QtcProcess &process) {
|
||||
qDebug() << "Success" << process.cleanedStdOut();
|
||||
};
|
||||
const auto onError = [](const QtcProcess &process) {
|
||||
qDebug() << "Failure" << process.cleanedStdErr();
|
||||
};
|
||||
const Group root {
|
||||
Process(onSetup, onDone, onError)
|
||||
};
|
||||
\endcode
|
||||
|
||||
The done and error handlers may collect output data from QtcProcess, and store it
|
||||
for further processing or perform additional actions. The done handler is optional.
|
||||
When used, it must be the second argument of the task constructor.
|
||||
The error handler must always be the third argument.
|
||||
You can omit the handlers or substitute the ones that you do not need with curly braces ({}).
|
||||
|
||||
\note If the task setup handler returns StopWithDone or StopWithError,
|
||||
neither the done nor error handler is invoked.
|
||||
|
||||
\section1 Group Handlers
|
||||
|
||||
Similarly to task handlers, group handlers enable you to set up a group to
|
||||
execute and to apply more actions when the whole group finishes with
|
||||
success or an error.
|
||||
|
||||
\section2 Group's Start Handler
|
||||
|
||||
The task tree invokes the group start handler before it starts the child
|
||||
tasks. The group handler doesn't take any arguments:
|
||||
|
||||
\code
|
||||
const auto onGroupSetup = [] {
|
||||
qDebug() << "Entering the group";
|
||||
};
|
||||
const Group root {
|
||||
OnGroupSetup(onGroupSetup),
|
||||
Process(...)
|
||||
};
|
||||
\endcode
|
||||
|
||||
The group setup handler is optional. To define a group setup handler, add an
|
||||
OnGroupSetup element to a group. The argument of OnGroupSetup is a user
|
||||
handler. If you add more than one OnGroupSetup element to a group, an assert
|
||||
is triggered at runtime that includes an error message.
|
||||
|
||||
Like the task start handler, the group start handler may return TaskAction.
|
||||
The returned TaskAction value affects the start behavior of the
|
||||
whole group. If you do not specify a group start handler or its return type
|
||||
is void, the default group's action is TaskAction::Continue, so that all
|
||||
tasks are started normally. Otherwise, when the start handler returns
|
||||
TaskAction::StopWithDone or TaskAction::StopWithError, the tasks are not
|
||||
started (they are skipped) and the group itself reports success or failure,
|
||||
depending on the returned value, respectively.
|
||||
|
||||
\code
|
||||
const Group root {
|
||||
OnGroupSetup([] { qDebug() << "Root setup"; }),
|
||||
Group {
|
||||
OnGroupSetup([] { qDebug() << "Group 1 setup"; return TaskAction::Continue; }),
|
||||
Process(...) // Process 1
|
||||
},
|
||||
Group {
|
||||
OnGroupSetup([] { qDebug() << "Group 2 setup"; return TaskAction::StopWithDone; }),
|
||||
Process(...) // Process 2
|
||||
},
|
||||
Group {
|
||||
OnGroupSetup([] { qDebug() << "Group 3 setup"; return TaskAction::StopWithError; }),
|
||||
Process(...) // Process 3
|
||||
},
|
||||
Process(...) // Process 4
|
||||
};
|
||||
\endcode
|
||||
|
||||
In the above example, all subgroups of a root group define their setup handlers.
|
||||
The following scenario assumes that all started processes finish with success:
|
||||
|
||||
\table
|
||||
\header
|
||||
\li Scenario
|
||||
\li Comment
|
||||
\row
|
||||
\li Root Group starts
|
||||
\li Doesn't return TaskAction, so its tasks are executed.
|
||||
\row
|
||||
\li Group 1 starts
|
||||
\li Returns Continue, so its tasks are executed.
|
||||
\row
|
||||
\li Process 1 starts
|
||||
\li
|
||||
\row
|
||||
\li ...
|
||||
\li ...
|
||||
\row
|
||||
\li Process 1 finishes (success)
|
||||
\li
|
||||
\row
|
||||
\li Group 1 finishes (success)
|
||||
\li
|
||||
\row
|
||||
\li Group 2 starts
|
||||
\li Returns StopWithDone, so Process 2 is skipped and Group 2 reports
|
||||
success.
|
||||
\row
|
||||
\li Group 2 finishes (success)
|
||||
\li
|
||||
\row
|
||||
\li Group 3 starts
|
||||
\li Returns StopWithError, so Process 3 is skipped and Group 3 reports
|
||||
an error.
|
||||
\row
|
||||
\li Group 3 finishes (error)
|
||||
\li
|
||||
\row
|
||||
\li Root Group finishes (error)
|
||||
\li Group 3, which is a direct child of the root group, finished with an
|
||||
error, so the root group stops executing, skips Process 4, which has
|
||||
not started yet, and reports an error.
|
||||
\endtable
|
||||
|
||||
\section2 Groups's Done and Error Handlers
|
||||
|
||||
A Group's done or error handler is executed after the successful or failed
|
||||
execution of its tasks, respectively. The final value reported by the
|
||||
group depends on its \l {Workflow Policy}. The handlers can apply other
|
||||
necessary actions. The done and error handlers are defined inside the
|
||||
OnGroupDone and OnGroupError elements of a group, respectively. They do not
|
||||
take arguments:
|
||||
|
||||
\code
|
||||
const Group root {
|
||||
OnGroupSetup([] { qDebug() << "Root setup"; }),
|
||||
Process(...),
|
||||
OnGroupDone([] { qDebug() << "Root finished with success"; }),
|
||||
OnGroupError([] { qDebug() << "Root finished with error"; })
|
||||
};
|
||||
\endcode
|
||||
|
||||
The group done and error handlers are optional. If you add more than one
|
||||
OnGroupDone or OnGroupError each to a group, an assert is triggered at
|
||||
runtime that includes an error message.
|
||||
|
||||
\note Even if the group setup handler returns StopWithDone or StopWithError,
|
||||
one of the task's done or error handlers is invoked. This behavior differs
|
||||
from that of task handlers and might change in the future.
|
||||
|
||||
\section1 Other Group Elements
|
||||
|
||||
A group can contain other elements that describe the processing flow, such as
|
||||
the execution mode or workflow policy. It can also contain storage elements
|
||||
that are responsible for collecting and sharing custom common data gathered
|
||||
during group execution.
|
||||
|
||||
\section2 Execution Mode
|
||||
|
||||
The execution mode element in a Group specifies how the direct child tasks of
|
||||
the Group are started.
|
||||
|
||||
\table
|
||||
\header
|
||||
\li Execution Mode
|
||||
\li Description
|
||||
\row
|
||||
\li sequential
|
||||
\li Default. When a Group has no execution mode, it runs in the
|
||||
sequential mode. All the direct child tasks of a group are started
|
||||
in a chain, so that when one task finishes, the next one starts.
|
||||
This enables you to pass the results from the previous task
|
||||
as input to the next task before it starts. This mode guarantees
|
||||
that the next task is started only after the previous task finishes.
|
||||
\row
|
||||
\li parallel
|
||||
\li All the direct child tasks of a group are started after the group is
|
||||
started, without waiting for the previous tasks to finish. In this
|
||||
mode, all tasks run simultaneously.
|
||||
\row
|
||||
\li ParallelLimit(int limit)
|
||||
\li In this mode, a limited number of direct child tasks run simultaneously.
|
||||
The \e limit defines the maximum number of tasks running in parallel
|
||||
in a group. When the group is started, the first batch tasks is
|
||||
started (the number of tasks in batch equals to passed limit, at most),
|
||||
while the others are kept waiting. When a running task finishes,
|
||||
the group starts the next remaining one, so that the \e limit
|
||||
of simultaneously running tasks inside a group isn't exceeded.
|
||||
This repeats on every child task's finish until all child tasks are started.
|
||||
This enables you to limit the maximum number of tasks that
|
||||
run simultaneously, for example if running too many processes might
|
||||
block the machine for a long time. The value 1 means \e sequential
|
||||
execution. The value 0 means unlimited and equals \e parallel.
|
||||
\endtable
|
||||
|
||||
In all execution modes, a group starts tasks in the oder in which they appear.
|
||||
|
||||
If a child of a group is also a group (in a nested tree), the child group
|
||||
runs its tasks according to its own execution mode.
|
||||
|
||||
\section2 Workflow Policy
|
||||
|
||||
The workflow policy element in a Group specifies how the group should behave
|
||||
when its direct child tasks finish:
|
||||
|
||||
\table
|
||||
\header
|
||||
\li Workflow Policy
|
||||
\li Description
|
||||
\row
|
||||
\li stopOnError
|
||||
\li Default. If a task finishes with an error, the group:
|
||||
\list 1
|
||||
\li Stops the running tasks (if any - for example, in parallel
|
||||
mode).
|
||||
\li Skips executing tasks it has not started (for example, in the
|
||||
sequential mode).
|
||||
\li Immediately finishes with an error.
|
||||
\endlist
|
||||
If all child tasks finish successfully or the group is empty, the group
|
||||
finishes with success.
|
||||
\row
|
||||
\li continueOnError
|
||||
\li Similar to stopOnError, but in case any child finishes with
|
||||
an error, the execution continues until all tasks finish,
|
||||
and the group reports an error afterwards, even when some other
|
||||
tasks in group finished with success.
|
||||
If a task finishes with an error, the group:
|
||||
\list 1
|
||||
\li Continues executing the tasks that are running or have not
|
||||
started yet.
|
||||
\li Finishes with an error when all tasks finish.
|
||||
\endlist
|
||||
If all tasks finish successfully or the group is empty, the group
|
||||
finishes with success.
|
||||
\row
|
||||
\li stopOnDone
|
||||
\li If a task finishes with success, the group:
|
||||
\list 1
|
||||
\li Stops running tasks and skips those that it has not started.
|
||||
\li Immediately finishes with success.
|
||||
\endlist
|
||||
If all tasks finish with an error or the group is empty, the group
|
||||
finishes with an error.
|
||||
\row
|
||||
\li continueOnDone
|
||||
\li Similar to stopOnDone, but in case any child finishes
|
||||
successfully, the execution continues until all tasks finish,
|
||||
and the group reports success afterwards, even when some other
|
||||
tasks in group finished with an error.
|
||||
If a task finishes with success, the group:
|
||||
\list 1
|
||||
\li Continues executing the tasks that are running or have not
|
||||
started yet.
|
||||
\li Finishes with success when all tasks finish.
|
||||
\endlist
|
||||
If all tasks finish with an error or the group is empty, the group
|
||||
finishes with an error.
|
||||
\row
|
||||
\li optional
|
||||
\li The group executes all tasks and ignores their return state. If all
|
||||
tasks finish or the group is empty, the group finishes with success.
|
||||
\endtable
|
||||
|
||||
If a child of a group is also a group (in a nested tree), the child group
|
||||
runs its tasks according to its own workflow policy.
|
||||
|
||||
\section2 Storage
|
||||
|
||||
Use the Storage element to exchange information between tasks. Especially,
|
||||
in the sequential execution mode, when a task needs data from another task
|
||||
before it can start. For example, a task tree that copies data by reading
|
||||
it from a source and writing it to a destination might look as follows:
|
||||
|
||||
\code
|
||||
static QByteArray load(const FilePath &fileName) { ... }
|
||||
static void save(const FilePath &fileName, const QByteArray &array) { ... }
|
||||
|
||||
static TaskItem diffRecipe(const FilePath &source, const FilePath &destination)
|
||||
{
|
||||
struct CopyStorage { // [1] custom inter-task struct
|
||||
QByteArray content; // [2] custom inter-task data
|
||||
};
|
||||
|
||||
// [3] instance of custom inter-task struct manageable by task tree
|
||||
const TreeStorage<CopyStorage> storage;
|
||||
|
||||
const auto onLoaderSetup = [source](Async<QByteArray> &async) {
|
||||
async.setAsyncCallData(&load, source);
|
||||
};
|
||||
// [4] runtime: task tree activates the instance from [5] before invoking handler
|
||||
const auto onLoaderDone = [storage](const Async<QByteArray> &async) {
|
||||
storage->content = async.result();
|
||||
};
|
||||
|
||||
// [4] runtime: task tree activates the instance from [5] before invoking handler
|
||||
const auto onSaverSetup = [storage, destination](Async<void> &async) {
|
||||
async.setAsyncCallData(&save, destination, storage->content);
|
||||
};
|
||||
const auto onSaverDone = [](const Async<void> &async) {
|
||||
qDebug() << "Save done successfully";
|
||||
};
|
||||
|
||||
const Group root {
|
||||
// [5] runtime: task tree creates an instance of CopyStorage when root is entered
|
||||
Storage(storage),
|
||||
Async<QByteArray>(onLoaderSetup, onLoaderDone),
|
||||
Async<void>(onSaverSetup, onSaverDone)
|
||||
};
|
||||
return root;
|
||||
}
|
||||
\endcode
|
||||
|
||||
In the example above, the inter-task data consists of a QByteArray content
|
||||
variable [2] enclosed in a CopyStorage custom struct [1]. If the loader
|
||||
finishes successfully, it stores the data in a CopyStorage::content
|
||||
variable. The saver then uses the variable to configure the saving task.
|
||||
|
||||
To enable a task tree to manage the CopyStorage struct, an instance of
|
||||
TreeStorage<CopyStorage> is created [3]. If a copy of this object is
|
||||
inserted as group's child task [5], an instance of CopyStorage struct is
|
||||
created dynamically when the task tree enters this group. When the task
|
||||
tree leaves this group, the existing instance of CopyStorage struct is
|
||||
destructed as it's no longer needed.
|
||||
|
||||
If several task trees that hold a copy of the common TreeStorage<CopyStorage>
|
||||
instance run simultaneously, each task tree contains its own copy of the
|
||||
CopyStorage struct.
|
||||
|
||||
You can access CopyStorage from any handler in the group with a storage object.
|
||||
This includes all handlers of all descendant tasks of the group with
|
||||
a storage object. To access the custom struct in a handler, pass the
|
||||
copy of the TreeStorage<CopyStorage> object to the handler (for example, in
|
||||
a lambda capture) [4].
|
||||
|
||||
When the task tree invokes a handler in a subtree containing the storage [5],
|
||||
the task tree activates its own CopyStorage instance inside the
|
||||
TreeStorage<CopyStorage> object. Therefore, the CopyStorage struct may be
|
||||
accessed only from within the handler body. To access the currently active
|
||||
CopyStorage from within TreeStorage<CopyStorage>, use the TreeStorage::operator->()
|
||||
or TreeStorage::activeStorage() method.
|
||||
|
||||
The following list summarizes how to employ a Storage object into the task
|
||||
tree:
|
||||
\list 1
|
||||
\li Define the custom structure MyStorage with custom data [1], [2]
|
||||
\li Create an instance of TreeStorage<MyStorage> storage [3]
|
||||
\li Pass the TreeStorage<MyStorage> instance to handlers [4]
|
||||
\li Insert the TreeStorage<MyStorage> instance into a group [5]
|
||||
\endlist
|
||||
|
||||
\note The current implementation assumes that all running task trees
|
||||
containing copies of the same TreeStorage run in the same thread. Otherwise,
|
||||
the behavior is undefined.
|
||||
|
||||
\section1 TaskTree
|
||||
|
||||
TaskTree executes the tree structure of asynchronous tasks according to the
|
||||
recipe described by the Group root element.
|
||||
|
||||
As TaskTree is also an asynchronous task, it can be a part of another TaskTree.
|
||||
To place a nested TaskTree inside another TaskTree, insert the Tasking::Tree
|
||||
element into other tree's Group element.
|
||||
|
||||
TaskTree reports progress of completed tasks when running. The progress value
|
||||
is increased when a task finishes or is skipped or stopped.
|
||||
When TaskTree is finished and the TaskTree::done() or TaskTree::errorOccurred()
|
||||
signal is emitted, the current value of the progress equals the maximum
|
||||
progress value. Maximum progress equals the total number of tasks in a tree.
|
||||
A nested TaskTree is counted as a single task, and its child tasks are not
|
||||
counted in the top level tree. Groups themselves are not counted as tasks,
|
||||
but their tasks are counted.
|
||||
|
||||
To set additional initial data for the running tree, modify the storage
|
||||
instances in a tree when it creates them by installing a storage setup
|
||||
handler:
|
||||
|
||||
\code
|
||||
TreeStorage<CopyStorage> storage;
|
||||
Group root = ...; // storage placed inside root's group and inside handlers
|
||||
TaskTree taskTree(root);
|
||||
auto initStorage = [](CopyStorage *storage){
|
||||
storage->content = "initial content";
|
||||
};
|
||||
taskTree.onStorageSetup(storage, initStorage);
|
||||
taskTree.start();
|
||||
\endcode
|
||||
|
||||
When the running task tree creates a CopyStorage instance, and before any
|
||||
handler inside a tree is called, the task tree calls the initStorage handler,
|
||||
to enable setting up initial data of the storage, unique to this particular
|
||||
run of taskTree.
|
||||
|
||||
Similarly, to collect some additional result data from the running tree,
|
||||
read it from storage instances in the tree when they are about to be
|
||||
destroyed. To do this, install a storage done handler:
|
||||
|
||||
\code
|
||||
TreeStorage<CopyStorage> storage;
|
||||
Group root = ...; // storage placed inside root's group and inside handlers
|
||||
TaskTree taskTree(root);
|
||||
auto collectStorage = [](CopyStorage *storage){
|
||||
qDebug() << "final content" << storage->content;
|
||||
};
|
||||
taskTree.onStorageDone(storage, collectStorage);
|
||||
taskTree.start();
|
||||
\endcode
|
||||
|
||||
When the running task tree is about to destroy a CopyStorage instance, the
|
||||
task tree calls the collectStorage handler, to enable reading the final data
|
||||
from the storage, unique to this particular run of taskTree.
|
||||
|
||||
\section1 Task Adapters
|
||||
|
||||
To extend a TaskTree with new a task type, implement a simple adapter class
|
||||
derived from the TaskAdapter class template. The following class is an
|
||||
adapter for a single shot timer, which may be considered as a new
|
||||
asynchronous task:
|
||||
|
||||
\code
|
||||
class TimeoutAdapter : public Utils::Tasking::TaskAdapter<QTimer>
|
||||
{
|
||||
public:
|
||||
TimeoutAdapter() {
|
||||
task()->setSingleShot(true);
|
||||
task()->setInterval(1000);
|
||||
connect(task(), &QTimer::timeout, this, [this] { emit done(true); });
|
||||
}
|
||||
void start() final { task()->start(); }
|
||||
};
|
||||
|
||||
QTC_DECLARE_CUSTOM_TASK(Timeout, TimeoutAdapter);
|
||||
\endcode
|
||||
|
||||
You must derive the custom adapter from the TaskAdapter class template
|
||||
instantiated with a template parameter of the class implementing a running
|
||||
task. The code above uses QTimer to run the task. This class appears
|
||||
later as an argument to the task's handlers. The instance of this class
|
||||
parameter automatically becomes a member of the TaskAdapter template, and is
|
||||
accessible through the TaskAdapter::task() method. The constructor
|
||||
of TimeoutAdapter initially configures the QTimer object and connects
|
||||
to the QTimer::timeout signal. When the signal is triggered, TimeoutAdapter
|
||||
emits the done(true) signal to inform the task tree that the task finished
|
||||
successfully. If it emits done(false), the task finished with an error.
|
||||
The TaskAdapter::start() method starts the timer.
|
||||
|
||||
To make QTimer accessible inside TaskTree under the \e Timeout name,
|
||||
register it with QTC_DECLARE_CUSTOM_TASK(Timeout, TimeoutAdapter). Timeout
|
||||
becomes a new task type inside Utils::Tasking namespace, using TimeoutAdapter.
|
||||
|
||||
The new task type is now registered, and you can use it in TaskTree:
|
||||
|
||||
\code
|
||||
const auto onTimeoutSetup = [](QTimer &task) {
|
||||
task.setInterval(2000);
|
||||
};
|
||||
const auto onTimeoutDone = [](const QTimer &task) {
|
||||
qDebug() << "timeout triggered";
|
||||
};
|
||||
|
||||
const Group root {
|
||||
Timeout(onTimeoutSetup, onTimeoutDone)
|
||||
};
|
||||
\endcode
|
||||
|
||||
When a task tree containing the root from the above example is started, it
|
||||
prints a debug message within two seconds and then finishes successfully.
|
||||
|
||||
\note The class implementing the running task should have a default constructor,
|
||||
and objects of this class should be freely destructible. It should be allowed
|
||||
to destroy a running object, preferably without waiting for the running task
|
||||
to finish (that is, safe non-blocking destructor of a running task).
|
||||
*/
|
||||
|
||||
TaskTree::TaskTree()
|
||||
|
||||
@@ -52,6 +52,7 @@ add_qtc_plugin(Android
|
||||
javaparser.cpp javaparser.h
|
||||
splashscreencontainerwidget.cpp splashscreencontainerwidget.h
|
||||
splashscreenwidget.cpp splashscreenwidget.h
|
||||
sdkmanageroutputparser.cpp sdkmanageroutputparser.h
|
||||
)
|
||||
|
||||
extend_qtc_plugin(Android
|
||||
|
||||
@@ -111,7 +111,9 @@ Project {
|
||||
"splashscreencontainerwidget.cpp",
|
||||
"splashscreencontainerwidget.h",
|
||||
"splashscreenwidget.cpp",
|
||||
"splashscreenwidget.h"
|
||||
"splashscreenwidget.h",
|
||||
"sdkmanageroutputparser.cpp",
|
||||
"sdkmanageroutputparser.h"
|
||||
]
|
||||
|
||||
Group {
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "androidconfigurations.h"
|
||||
#include "androidconstants.h"
|
||||
#include "androidmanager.h"
|
||||
#include "androidsdkmanager.h"
|
||||
#include "androidtr.h"
|
||||
#include "avdmanageroutputparser.h"
|
||||
#include "sdkmanageroutputparser.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -29,16 +28,13 @@
|
||||
using namespace Utils;
|
||||
|
||||
namespace {
|
||||
static Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
||||
Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
||||
const char commonArgsKey[] = "Common Arguments:";
|
||||
}
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
const char installLocationKey[] = "Installed Location:";
|
||||
const char revisionKey[] = "Version:";
|
||||
const char descriptionKey[] = "Description:";
|
||||
const char commonArgsKey[] = "Common Arguments:";
|
||||
|
||||
const int sdkManagerCmdTimeoutS = 60;
|
||||
const int sdkManagerOperationTimeoutS = 600;
|
||||
@@ -54,28 +50,14 @@ static const QRegularExpression &assertionRegExp()
|
||||
return theRegExp;
|
||||
}
|
||||
|
||||
/*!
|
||||
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
|
||||
\c true if \a key is found, false otherwise. Result is copied into \a value.
|
||||
*/
|
||||
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
|
||||
{
|
||||
auto trimmedInput = line.trimmed();
|
||||
if (trimmedInput.startsWith(key)) {
|
||||
if (value)
|
||||
*value = trimmedInput.section(key, 1, 1).trimmed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int parseProgress(const QString &out, bool &foundAssertion)
|
||||
{
|
||||
int progress = -1;
|
||||
if (out.isEmpty())
|
||||
return progress;
|
||||
QRegularExpression reg("(?<progress>\\d*)%");
|
||||
QStringList lines = out.split(QRegularExpression("[\\n\\r]"), Qt::SkipEmptyParts);
|
||||
static const QRegularExpression reg("(?<progress>\\d*)%");
|
||||
static const QRegularExpression regEndOfLine("[\\n\\r]");
|
||||
const QStringList lines = out.split(regEndOfLine, Qt::SkipEmptyParts);
|
||||
for (const QString &line : lines) {
|
||||
QRegularExpressionMatch match = reg.match(line);
|
||||
if (match.hasMatch()) {
|
||||
@@ -223,87 +205,6 @@ public:
|
||||
bool m_packageListingSuccessful = false;
|
||||
};
|
||||
|
||||
/*!
|
||||
\class SdkManagerOutputParser
|
||||
\brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager
|
||||
commands.
|
||||
*/
|
||||
class SdkManagerOutputParser
|
||||
{
|
||||
class GenericPackageData
|
||||
{
|
||||
public:
|
||||
bool isValid() const { return !revision.isNull() && !description.isNull(); }
|
||||
QStringList headerParts;
|
||||
QVersionNumber revision;
|
||||
QString description;
|
||||
Utils::FilePath installedLocation;
|
||||
QMap<QString, QString> extraData;
|
||||
};
|
||||
|
||||
public:
|
||||
enum MarkerTag
|
||||
{
|
||||
None = 0x001,
|
||||
InstalledPackagesMarker = 0x002,
|
||||
AvailablePackagesMarkers = 0x004,
|
||||
AvailableUpdatesMarker = 0x008,
|
||||
EmptyMarker = 0x010,
|
||||
PlatformMarker = 0x020,
|
||||
SystemImageMarker = 0x040,
|
||||
BuildToolsMarker = 0x080,
|
||||
SdkToolsMarker = 0x100,
|
||||
PlatformToolsMarker = 0x200,
|
||||
EmulatorToolsMarker = 0x400,
|
||||
NdkMarker = 0x800,
|
||||
ExtrasMarker = 0x1000,
|
||||
CmdlineSdkToolsMarker = 0x2000,
|
||||
GenericToolMarker = 0x4000,
|
||||
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
|
||||
};
|
||||
|
||||
SdkManagerOutputParser(AndroidSdkPackageList &container) : m_packages(container) {}
|
||||
void parsePackageListing(const QString &output);
|
||||
|
||||
AndroidSdkPackageList &m_packages;
|
||||
|
||||
private:
|
||||
void compilePackageAssociations();
|
||||
void parsePackageData(MarkerTag packageMarker, const QStringList &data);
|
||||
bool parseAbstractData(GenericPackageData &output, const QStringList &input, int minParts,
|
||||
const QString &logStrTag,
|
||||
const QStringList &extraKeys = QStringList()) const;
|
||||
AndroidSdkPackage *parsePlatform(const QStringList &data) const;
|
||||
QPair<SystemImage *, int> parseSystemImage(const QStringList &data) const;
|
||||
BuildTools *parseBuildToolsPackage(const QStringList &data) const;
|
||||
SdkTools *parseSdkToolsPackage(const QStringList &data) const;
|
||||
PlatformTools *parsePlatformToolsPackage(const QStringList &data) const;
|
||||
EmulatorTools *parseEmulatorToolsPackage(const QStringList &data) const;
|
||||
Ndk *parseNdkPackage(const QStringList &data) const;
|
||||
ExtraTools *parseExtraToolsPackage(const QStringList &data) const;
|
||||
GenericSdkPackage *parseGenericTools(const QStringList &data) const;
|
||||
MarkerTag parseMarkers(const QString &line);
|
||||
|
||||
MarkerTag m_currentSection = MarkerTag::None;
|
||||
QHash<AndroidSdkPackage *, int> m_systemImages;
|
||||
};
|
||||
|
||||
using MarkerTagsType = std::map<SdkManagerOutputParser::MarkerTag, const char *>;
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(MarkerTagsType, markerTags, ({
|
||||
{SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"},
|
||||
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"},
|
||||
{SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"},
|
||||
{SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"},
|
||||
{SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"},
|
||||
{SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"},
|
||||
{SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"},
|
||||
{SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, Constants::cmdlineToolsName},
|
||||
{SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"},
|
||||
{SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"},
|
||||
{SdkManagerOutputParser::MarkerTag::NdkMarker, Constants::ndkPackageName},
|
||||
{SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"}
|
||||
}));
|
||||
|
||||
AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config):
|
||||
m_d(new AndroidSdkManagerPrivate(*this, config))
|
||||
{
|
||||
@@ -464,403 +365,6 @@ void AndroidSdkManager::acceptSdkLicense(bool accept)
|
||||
m_d->setLicenseInput(accept);
|
||||
}
|
||||
|
||||
void SdkManagerOutputParser::parsePackageListing(const QString &output)
|
||||
{
|
||||
QStringList packageData;
|
||||
bool collectingPackageData = false;
|
||||
MarkerTag currentPackageMarker = MarkerTag::None;
|
||||
|
||||
auto processCurrentPackage = [&] {
|
||||
if (collectingPackageData) {
|
||||
collectingPackageData = false;
|
||||
parsePackageData(currentPackageMarker, packageData);
|
||||
packageData.clear();
|
||||
}
|
||||
};
|
||||
|
||||
QRegularExpression delimiters("[\\n\\r]");
|
||||
const auto lines = output.split(delimiters);
|
||||
for (const QString &outputLine : lines) {
|
||||
|
||||
// NOTE: we don't want to parse Dependencies part as it does not add value
|
||||
if (outputLine.startsWith(" "))
|
||||
continue;
|
||||
|
||||
// We don't need to parse this because they would still be listed on available packages
|
||||
if (m_currentSection == AvailableUpdatesMarker)
|
||||
continue;
|
||||
|
||||
MarkerTag marker = parseMarkers(outputLine.trimmed());
|
||||
if (marker & SectionMarkers) {
|
||||
// Section marker found. Update the current section being parsed.
|
||||
m_currentSection = marker;
|
||||
processCurrentPackage();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_currentSection == None)
|
||||
continue; // Continue with the verbose output until a valid section starts.
|
||||
|
||||
if (marker == EmptyMarker) {
|
||||
// Empty marker. Occurs at the end of a package details.
|
||||
// Process the collected package data, if any.
|
||||
processCurrentPackage();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (marker == None) {
|
||||
if (collectingPackageData)
|
||||
packageData << outputLine; // Collect data until next marker.
|
||||
else
|
||||
continue;
|
||||
} else {
|
||||
// Package marker found.
|
||||
processCurrentPackage(); // New package starts. Process the collected package data, if any.
|
||||
currentPackageMarker = marker;
|
||||
collectingPackageData = true;
|
||||
packageData << outputLine;
|
||||
}
|
||||
}
|
||||
compilePackageAssociations();
|
||||
}
|
||||
|
||||
void SdkManagerOutputParser::compilePackageAssociations()
|
||||
{
|
||||
// Return true if package p is already installed i.e. there exists a installed package having
|
||||
// same sdk style path and same revision as of p.
|
||||
auto isInstalled = [](const AndroidSdkPackageList &container, AndroidSdkPackage *p) {
|
||||
return Utils::anyOf(container, [p](AndroidSdkPackage *other) {
|
||||
return other->state() == AndroidSdkPackage::Installed &&
|
||||
other->sdkStylePath() == p->sdkStylePath() &&
|
||||
other->revision() == p->revision();
|
||||
});
|
||||
};
|
||||
|
||||
auto deleteAlreadyInstalled = [isInstalled](AndroidSdkPackageList &packages) {
|
||||
for (auto p = packages.begin(); p != packages.end();) {
|
||||
if ((*p)->state() == AndroidSdkPackage::Available && isInstalled(packages, *p)) {
|
||||
delete *p;
|
||||
p = packages.erase(p);
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Remove already installed packages.
|
||||
deleteAlreadyInstalled(m_packages);
|
||||
|
||||
// Filter out available images that are already installed.
|
||||
AndroidSdkPackageList images = m_systemImages.keys();
|
||||
deleteAlreadyInstalled(images);
|
||||
|
||||
// Associate the system images with sdk platforms.
|
||||
for (AndroidSdkPackage *image : std::as_const(images)) {
|
||||
int imageApi = m_systemImages[image];
|
||||
auto itr = std::find_if(m_packages.begin(), m_packages.end(),
|
||||
[imageApi](const AndroidSdkPackage *p) {
|
||||
const SdkPlatform *platform = nullptr;
|
||||
if (p->type() == AndroidSdkPackage::SdkPlatformPackage)
|
||||
platform = static_cast<const SdkPlatform*>(p);
|
||||
return platform && platform->apiLevel() == imageApi;
|
||||
});
|
||||
if (itr != m_packages.end()) {
|
||||
auto platform = static_cast<SdkPlatform*>(*itr);
|
||||
platform->addSystemImage(static_cast<SystemImage *>(image));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QStringList &data)
|
||||
{
|
||||
QTC_ASSERT(!data.isEmpty() && packageMarker != None, return);
|
||||
|
||||
AndroidSdkPackage *package = nullptr;
|
||||
auto createPackage = [&](std::function<AndroidSdkPackage *(SdkManagerOutputParser *,
|
||||
const QStringList &)> creator) {
|
||||
if ((package = creator(this, data)))
|
||||
m_packages.append(package);
|
||||
};
|
||||
|
||||
switch (packageMarker) {
|
||||
case MarkerTag::BuildToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseBuildToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::SdkToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::CmdlineSdkToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::PlatformToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parsePlatformToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::EmulatorToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseEmulatorToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::PlatformMarker:
|
||||
createPackage(&SdkManagerOutputParser::parsePlatform);
|
||||
break;
|
||||
|
||||
case MarkerTag::SystemImageMarker:
|
||||
{
|
||||
QPair<SystemImage *, int> result = parseSystemImage(data);
|
||||
if (result.first) {
|
||||
m_systemImages[result.first] = result.second;
|
||||
package = result.first;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MarkerTag::NdkMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseNdkPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::ExtrasMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseExtraToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::GenericToolMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseGenericTools);
|
||||
break;
|
||||
|
||||
default:
|
||||
qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags->at(packageMarker);
|
||||
break;
|
||||
}
|
||||
|
||||
if (package) {
|
||||
switch (m_currentSection) {
|
||||
case MarkerTag::InstalledPackagesMarker:
|
||||
package->setState(AndroidSdkPackage::Installed);
|
||||
break;
|
||||
case MarkerTag::AvailablePackagesMarkers:
|
||||
case MarkerTag::AvailableUpdatesMarker:
|
||||
package->setState(AndroidSdkPackage::Available);
|
||||
break;
|
||||
default:
|
||||
qCDebug(sdkManagerLog) << "Invalid section marker: " << markerTags->at(m_currentSection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SdkManagerOutputParser::parseAbstractData(SdkManagerOutputParser::GenericPackageData &output,
|
||||
const QStringList &input, int minParts,
|
||||
const QString &logStrTag,
|
||||
const QStringList &extraKeys) const
|
||||
{
|
||||
if (input.isEmpty()) {
|
||||
qCDebug(sdkManagerLog) << logStrTag + ": Empty input";
|
||||
return false;
|
||||
}
|
||||
|
||||
output.headerParts = input.at(0).split(';');
|
||||
if (output.headerParts.count() < minParts) {
|
||||
qCDebug(sdkManagerLog) << logStrTag + "%1: Unexpected header:" << input;
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList keys = extraKeys;
|
||||
keys << installLocationKey << revisionKey << descriptionKey;
|
||||
for (const QString &line : input) {
|
||||
QString value;
|
||||
for (const auto &key: std::as_const(keys)) {
|
||||
if (valueForKey(key, line, &value)) {
|
||||
if (key == installLocationKey)
|
||||
output.installedLocation = Utils::FilePath::fromUserInput(value);
|
||||
else if (key == revisionKey)
|
||||
output.revision = QVersionNumber::fromString(value);
|
||||
else if (key == descriptionKey)
|
||||
output.description = value;
|
||||
else
|
||||
output.extraData[key] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.isValid();
|
||||
}
|
||||
|
||||
AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const
|
||||
{
|
||||
SdkPlatform *platform = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 2, "Platform")) {
|
||||
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
||||
if (apiLevel == -1) {
|
||||
qCDebug(sdkManagerLog) << "Platform: Cannot parse api level:"<< data;
|
||||
return nullptr;
|
||||
}
|
||||
platform = new SdkPlatform(packageData.revision, data.at(0), apiLevel);
|
||||
platform->setExtension(convertNameToExtension(packageData.headerParts.at(1)));
|
||||
platform->setInstalledLocation(packageData.installedLocation);
|
||||
platform->setDescriptionText(packageData.description);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
QPair<SystemImage *, int> SdkManagerOutputParser::parseSystemImage(const QStringList &data) const
|
||||
{
|
||||
QPair <SystemImage *, int> result(nullptr, -1);
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 4, "System-image")) {
|
||||
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
||||
if (apiLevel == -1) {
|
||||
qCDebug(sdkManagerLog) << "System-image: Cannot parse api level:"<< data;
|
||||
return result;
|
||||
}
|
||||
auto image = new SystemImage(packageData.revision, data.at(0),
|
||||
packageData.headerParts.at(3));
|
||||
image->setInstalledLocation(packageData.installedLocation);
|
||||
image->setDisplayText(packageData.description);
|
||||
image->setDescriptionText(packageData.description);
|
||||
image->setApiLevel(apiLevel);
|
||||
result = {image, apiLevel};
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "System-image: Minimum required data unavailable: "<< data;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BuildTools *SdkManagerOutputParser::parseBuildToolsPackage(const QStringList &data) const
|
||||
{
|
||||
BuildTools *buildTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 2, "Build-tools")) {
|
||||
buildTools = new BuildTools(packageData.revision, data.at(0));
|
||||
buildTools->setDescriptionText(packageData.description);
|
||||
buildTools->setDisplayText(packageData.description);
|
||||
buildTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Build-tools: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return buildTools;
|
||||
}
|
||||
|
||||
SdkTools *SdkManagerOutputParser::parseSdkToolsPackage(const QStringList &data) const
|
||||
{
|
||||
SdkTools *sdkTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "SDK-tools")) {
|
||||
sdkTools = new SdkTools(packageData.revision, data.at(0));
|
||||
sdkTools->setDescriptionText(packageData.description);
|
||||
sdkTools->setDisplayText(packageData.description);
|
||||
sdkTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "SDK-tools: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return sdkTools;
|
||||
}
|
||||
|
||||
PlatformTools *SdkManagerOutputParser::parsePlatformToolsPackage(const QStringList &data) const
|
||||
{
|
||||
PlatformTools *platformTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Platform-tools")) {
|
||||
platformTools = new PlatformTools(packageData.revision, data.at(0));
|
||||
platformTools->setDescriptionText(packageData.description);
|
||||
platformTools->setDisplayText(packageData.description);
|
||||
platformTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Platform-tools: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return platformTools;
|
||||
}
|
||||
|
||||
EmulatorTools *SdkManagerOutputParser::parseEmulatorToolsPackage(const QStringList &data) const
|
||||
{
|
||||
EmulatorTools *emulatorTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Emulator-tools")) {
|
||||
emulatorTools = new EmulatorTools(packageData.revision, data.at(0));
|
||||
emulatorTools->setDescriptionText(packageData.description);
|
||||
emulatorTools->setDisplayText(packageData.description);
|
||||
emulatorTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Emulator-tools: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return emulatorTools;
|
||||
}
|
||||
|
||||
Ndk *SdkManagerOutputParser::parseNdkPackage(const QStringList &data) const
|
||||
{
|
||||
Ndk *ndk = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "NDK")) {
|
||||
ndk = new Ndk(packageData.revision, data.at(0));
|
||||
ndk->setDescriptionText(packageData.description);
|
||||
ndk->setDisplayText(packageData.description);
|
||||
ndk->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "NDK: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return ndk;
|
||||
}
|
||||
|
||||
ExtraTools *SdkManagerOutputParser::parseExtraToolsPackage(const QStringList &data) const
|
||||
{
|
||||
ExtraTools *extraTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Extras")) {
|
||||
extraTools = new ExtraTools(packageData.revision, data.at(0));
|
||||
extraTools->setDescriptionText(packageData.description);
|
||||
extraTools->setDisplayText(packageData.description);
|
||||
extraTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Extra-tools: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return extraTools;
|
||||
}
|
||||
|
||||
GenericSdkPackage *SdkManagerOutputParser::parseGenericTools(const QStringList &data) const
|
||||
{
|
||||
GenericSdkPackage *sdkPackage = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Generic")) {
|
||||
sdkPackage = new GenericSdkPackage(packageData.revision, data.at(0));
|
||||
sdkPackage->setDescriptionText(packageData.description);
|
||||
sdkPackage->setDisplayText(packageData.description);
|
||||
sdkPackage->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Generic: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return sdkPackage;
|
||||
}
|
||||
|
||||
SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line)
|
||||
{
|
||||
if (line.isEmpty())
|
||||
return EmptyMarker;
|
||||
|
||||
for (auto pair : *markerTags) {
|
||||
if (line.startsWith(QLatin1String(pair.second)))
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
QRegularExpressionMatch match = QRegularExpression("^[a-zA-Z]+[A-Za-z0-9;._-]+").match(line);
|
||||
if (match.hasMatch() && match.captured(0) == line)
|
||||
return GenericToolMarker;
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager,
|
||||
const AndroidConfig &config):
|
||||
m_activeOperation(nullptr, watcherDeleter),
|
||||
|
||||
449
src/plugins/android/sdkmanageroutputparser.cpp
Normal file
449
src/plugins/android/sdkmanageroutputparser.cpp
Normal file
@@ -0,0 +1,449 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "sdkmanageroutputparser.h"
|
||||
|
||||
#include "avdmanageroutputparser.h"
|
||||
#include "androidsdkpackage.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
namespace {
|
||||
Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
||||
const char installLocationKey[] = "Installed Location:";
|
||||
const char revisionKey[] = "Version:";
|
||||
const char descriptionKey[] = "Description:";
|
||||
} // namespace
|
||||
|
||||
using namespace Android;
|
||||
using namespace Android::Internal;
|
||||
|
||||
using MarkerTagsType = std::map<SdkManagerOutputParser::MarkerTag, const char *>;
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(MarkerTagsType, markerTags,
|
||||
({{SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"},
|
||||
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"},
|
||||
{SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"},
|
||||
{SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"},
|
||||
{SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"},
|
||||
{SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"},
|
||||
{SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"},
|
||||
{SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, Constants::cmdlineToolsName},
|
||||
{SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"},
|
||||
{SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"},
|
||||
{SdkManagerOutputParser::MarkerTag::NdkMarker, Constants::ndkPackageName},
|
||||
{SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"}}));
|
||||
|
||||
void SdkManagerOutputParser::parsePackageListing(const QString &output)
|
||||
{
|
||||
QStringList packageData;
|
||||
bool collectingPackageData = false;
|
||||
MarkerTag currentPackageMarker = MarkerTag::None;
|
||||
|
||||
auto processCurrentPackage = [&] {
|
||||
if (collectingPackageData) {
|
||||
collectingPackageData = false;
|
||||
parsePackageData(currentPackageMarker, packageData);
|
||||
packageData.clear();
|
||||
}
|
||||
};
|
||||
|
||||
static const QRegularExpression delimiters("[\\n\\r]");
|
||||
const auto lines = output.split(delimiters);
|
||||
for (const QString &outputLine : lines) {
|
||||
|
||||
// NOTE: we don't want to parse Dependencies part as it does not add value
|
||||
if (outputLine.startsWith(" "))
|
||||
continue;
|
||||
|
||||
// We don't need to parse this because they would still be listed on available packages
|
||||
if (m_currentSection == AvailableUpdatesMarker)
|
||||
continue;
|
||||
|
||||
MarkerTag marker = parseMarkers(outputLine.trimmed());
|
||||
if (marker & SectionMarkers) {
|
||||
// Section marker found. Update the current section being parsed.
|
||||
m_currentSection = marker;
|
||||
processCurrentPackage();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_currentSection == None)
|
||||
continue; // Continue with the verbose output until a valid section starts.
|
||||
|
||||
if (marker == EmptyMarker) {
|
||||
// Empty marker. Occurs at the end of a package details.
|
||||
// Process the collected package data, if any.
|
||||
processCurrentPackage();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (marker == None) {
|
||||
if (collectingPackageData)
|
||||
packageData << outputLine; // Collect data until next marker.
|
||||
else
|
||||
continue;
|
||||
} else {
|
||||
// Package marker found.
|
||||
processCurrentPackage(); // New package starts. Process the collected package data, if any.
|
||||
currentPackageMarker = marker;
|
||||
collectingPackageData = true;
|
||||
packageData << outputLine;
|
||||
}
|
||||
}
|
||||
compilePackageAssociations();
|
||||
}
|
||||
|
||||
void SdkManagerOutputParser::compilePackageAssociations()
|
||||
{
|
||||
// Return true if package p is already installed i.e. there exists a installed package having
|
||||
// same sdk style path and same revision as of p.
|
||||
auto isInstalled = [](const AndroidSdkPackageList &container, AndroidSdkPackage *p) {
|
||||
return Utils::anyOf(container, [p](AndroidSdkPackage *other) {
|
||||
return other->state() == AndroidSdkPackage::Installed &&
|
||||
other->sdkStylePath() == p->sdkStylePath() &&
|
||||
other->revision() == p->revision();
|
||||
});
|
||||
};
|
||||
|
||||
auto deleteAlreadyInstalled = [isInstalled](AndroidSdkPackageList &packages) {
|
||||
for (auto p = packages.begin(); p != packages.end();) {
|
||||
if ((*p)->state() == AndroidSdkPackage::Available && isInstalled(packages, *p)) {
|
||||
delete *p;
|
||||
p = packages.erase(p);
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Remove already installed packages.
|
||||
deleteAlreadyInstalled(m_packages);
|
||||
|
||||
// Filter out available images that are already installed.
|
||||
AndroidSdkPackageList images = m_systemImages.keys();
|
||||
deleteAlreadyInstalled(images);
|
||||
|
||||
// Associate the system images with sdk platforms.
|
||||
for (AndroidSdkPackage *image : std::as_const(images)) {
|
||||
int imageApi = m_systemImages[image];
|
||||
auto itr = std::find_if(m_packages.begin(), m_packages.end(),
|
||||
[imageApi](const AndroidSdkPackage *p) {
|
||||
const SdkPlatform *platform = nullptr;
|
||||
if (p->type() == AndroidSdkPackage::SdkPlatformPackage)
|
||||
platform = static_cast<const SdkPlatform*>(p);
|
||||
return platform && platform->apiLevel() == imageApi;
|
||||
});
|
||||
if (itr != m_packages.end()) {
|
||||
auto platform = static_cast<SdkPlatform*>(*itr);
|
||||
platform->addSystemImage(static_cast<SystemImage *>(image));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QStringList &data)
|
||||
{
|
||||
QTC_ASSERT(!data.isEmpty() && packageMarker != None, return);
|
||||
|
||||
AndroidSdkPackage *package = nullptr;
|
||||
auto createPackage = [&](std::function<AndroidSdkPackage *(SdkManagerOutputParser *,
|
||||
const QStringList &)> creator) {
|
||||
if ((package = creator(this, data)))
|
||||
m_packages.append(package);
|
||||
};
|
||||
|
||||
switch (packageMarker) {
|
||||
case MarkerTag::BuildToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseBuildToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::SdkToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::CmdlineSdkToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::PlatformToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parsePlatformToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::EmulatorToolsMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseEmulatorToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::PlatformMarker:
|
||||
createPackage(&SdkManagerOutputParser::parsePlatform);
|
||||
break;
|
||||
|
||||
case MarkerTag::SystemImageMarker:
|
||||
{
|
||||
QPair<SystemImage *, int> result = parseSystemImage(data);
|
||||
if (result.first) {
|
||||
m_systemImages[result.first] = result.second;
|
||||
package = result.first;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MarkerTag::NdkMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseNdkPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::ExtrasMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseExtraToolsPackage);
|
||||
break;
|
||||
|
||||
case MarkerTag::GenericToolMarker:
|
||||
createPackage(&SdkManagerOutputParser::parseGenericTools);
|
||||
break;
|
||||
|
||||
default:
|
||||
qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags->at(packageMarker);
|
||||
break;
|
||||
}
|
||||
|
||||
if (package) {
|
||||
switch (m_currentSection) {
|
||||
case MarkerTag::InstalledPackagesMarker:
|
||||
package->setState(AndroidSdkPackage::Installed);
|
||||
break;
|
||||
case MarkerTag::AvailablePackagesMarkers:
|
||||
case MarkerTag::AvailableUpdatesMarker:
|
||||
package->setState(AndroidSdkPackage::Available);
|
||||
break;
|
||||
default:
|
||||
qCDebug(sdkManagerLog) << "Invalid section marker: " << markerTags->at(m_currentSection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
|
||||
\c true if \a key is found, false otherwise. Result is copied into \a value.
|
||||
*/
|
||||
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
|
||||
{
|
||||
auto trimmedInput = line.trimmed();
|
||||
if (trimmedInput.startsWith(key)) {
|
||||
if (value)
|
||||
*value = trimmedInput.section(key, 1, 1).trimmed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SdkManagerOutputParser::parseAbstractData(SdkManagerOutputParser::GenericPackageData &output,
|
||||
const QStringList &input, int minParts,
|
||||
const QString &logStrTag,
|
||||
const QStringList &extraKeys) const
|
||||
{
|
||||
if (input.isEmpty()) {
|
||||
qCDebug(sdkManagerLog) << logStrTag + ": Empty input";
|
||||
return false;
|
||||
}
|
||||
|
||||
output.headerParts = input.at(0).split(';');
|
||||
if (output.headerParts.count() < minParts) {
|
||||
qCDebug(sdkManagerLog) << logStrTag + "%1: Unexpected header:" << input;
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList keys = extraKeys;
|
||||
keys << installLocationKey << revisionKey << descriptionKey;
|
||||
for (const QString &line : input) {
|
||||
QString value;
|
||||
for (const auto &key: std::as_const(keys)) {
|
||||
if (valueForKey(key, line, &value)) {
|
||||
if (key == installLocationKey)
|
||||
output.installedLocation = Utils::FilePath::fromUserInput(value);
|
||||
else if (key == revisionKey)
|
||||
output.revision = QVersionNumber::fromString(value);
|
||||
else if (key == descriptionKey)
|
||||
output.description = value;
|
||||
else
|
||||
output.extraData[key] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.isValid();
|
||||
}
|
||||
|
||||
AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const
|
||||
{
|
||||
SdkPlatform *platform = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 2, "Platform")) {
|
||||
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
||||
if (apiLevel == -1) {
|
||||
qCDebug(sdkManagerLog) << "Platform: Cannot parse api level:"<< data;
|
||||
return nullptr;
|
||||
}
|
||||
platform = new SdkPlatform(packageData.revision, data.at(0), apiLevel);
|
||||
platform->setExtension(convertNameToExtension(packageData.headerParts.at(1)));
|
||||
platform->setInstalledLocation(packageData.installedLocation);
|
||||
platform->setDescriptionText(packageData.description);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
QPair<SystemImage *, int> SdkManagerOutputParser::parseSystemImage(const QStringList &data) const
|
||||
{
|
||||
QPair <SystemImage *, int> result(nullptr, -1);
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 4, "System-image")) {
|
||||
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
||||
if (apiLevel == -1) {
|
||||
qCDebug(sdkManagerLog) << "System-image: Cannot parse api level:"<< data;
|
||||
return result;
|
||||
}
|
||||
auto image = new SystemImage(packageData.revision, data.at(0),
|
||||
packageData.headerParts.at(3));
|
||||
image->setInstalledLocation(packageData.installedLocation);
|
||||
image->setDisplayText(packageData.description);
|
||||
image->setDescriptionText(packageData.description);
|
||||
image->setApiLevel(apiLevel);
|
||||
result = {image, apiLevel};
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "System-image: Minimum required data unavailable: "<< data;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BuildTools *SdkManagerOutputParser::parseBuildToolsPackage(const QStringList &data) const
|
||||
{
|
||||
BuildTools *buildTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 2, "Build-tools")) {
|
||||
buildTools = new BuildTools(packageData.revision, data.at(0));
|
||||
buildTools->setDescriptionText(packageData.description);
|
||||
buildTools->setDisplayText(packageData.description);
|
||||
buildTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Build-tools: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return buildTools;
|
||||
}
|
||||
|
||||
SdkTools *SdkManagerOutputParser::parseSdkToolsPackage(const QStringList &data) const
|
||||
{
|
||||
SdkTools *sdkTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "SDK-tools")) {
|
||||
sdkTools = new SdkTools(packageData.revision, data.at(0));
|
||||
sdkTools->setDescriptionText(packageData.description);
|
||||
sdkTools->setDisplayText(packageData.description);
|
||||
sdkTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "SDK-tools: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return sdkTools;
|
||||
}
|
||||
|
||||
PlatformTools *SdkManagerOutputParser::parsePlatformToolsPackage(const QStringList &data) const
|
||||
{
|
||||
PlatformTools *platformTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Platform-tools")) {
|
||||
platformTools = new PlatformTools(packageData.revision, data.at(0));
|
||||
platformTools->setDescriptionText(packageData.description);
|
||||
platformTools->setDisplayText(packageData.description);
|
||||
platformTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Platform-tools: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return platformTools;
|
||||
}
|
||||
|
||||
EmulatorTools *SdkManagerOutputParser::parseEmulatorToolsPackage(const QStringList &data) const
|
||||
{
|
||||
EmulatorTools *emulatorTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Emulator-tools")) {
|
||||
emulatorTools = new EmulatorTools(packageData.revision, data.at(0));
|
||||
emulatorTools->setDescriptionText(packageData.description);
|
||||
emulatorTools->setDisplayText(packageData.description);
|
||||
emulatorTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Emulator-tools: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return emulatorTools;
|
||||
}
|
||||
|
||||
Ndk *SdkManagerOutputParser::parseNdkPackage(const QStringList &data) const
|
||||
{
|
||||
Ndk *ndk = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "NDK")) {
|
||||
ndk = new Ndk(packageData.revision, data.at(0));
|
||||
ndk->setDescriptionText(packageData.description);
|
||||
ndk->setDisplayText(packageData.description);
|
||||
ndk->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "NDK: Parsing failed. Minimum required data unavailable:"
|
||||
<< data;
|
||||
}
|
||||
return ndk;
|
||||
}
|
||||
|
||||
ExtraTools *SdkManagerOutputParser::parseExtraToolsPackage(const QStringList &data) const
|
||||
{
|
||||
ExtraTools *extraTools = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Extras")) {
|
||||
extraTools = new ExtraTools(packageData.revision, data.at(0));
|
||||
extraTools->setDescriptionText(packageData.description);
|
||||
extraTools->setDisplayText(packageData.description);
|
||||
extraTools->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Extra-tools: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return extraTools;
|
||||
}
|
||||
|
||||
GenericSdkPackage *SdkManagerOutputParser::parseGenericTools(const QStringList &data) const
|
||||
{
|
||||
GenericSdkPackage *sdkPackage = nullptr;
|
||||
GenericPackageData packageData;
|
||||
if (parseAbstractData(packageData, data, 1, "Generic")) {
|
||||
sdkPackage = new GenericSdkPackage(packageData.revision, data.at(0));
|
||||
sdkPackage->setDescriptionText(packageData.description);
|
||||
sdkPackage->setDisplayText(packageData.description);
|
||||
sdkPackage->setInstalledLocation(packageData.installedLocation);
|
||||
} else {
|
||||
qCDebug(sdkManagerLog) << "Generic: Parsing failed. Minimum required data "
|
||||
"unavailable:" << data;
|
||||
}
|
||||
return sdkPackage;
|
||||
}
|
||||
|
||||
SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line)
|
||||
{
|
||||
if (line.isEmpty())
|
||||
return EmptyMarker;
|
||||
|
||||
for (auto pair : *markerTags) {
|
||||
if (line.startsWith(QLatin1String(pair.second)))
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
QRegularExpressionMatch match = QRegularExpression("^[a-zA-Z]+[A-Za-z0-9;._-]+").match(line);
|
||||
if (match.hasMatch() && match.captured(0) == line)
|
||||
return GenericToolMarker;
|
||||
|
||||
return None;
|
||||
}
|
||||
79
src/plugins/android/sdkmanageroutputparser.h
Normal file
79
src/plugins/android/sdkmanageroutputparser.h
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
#pragma once
|
||||
|
||||
#include "androidconstants.h"
|
||||
#include "androidsdkpackage.h"
|
||||
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <QVersionNumber>
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
/*!
|
||||
\class SdkManagerOutputParser
|
||||
\brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager
|
||||
commands.
|
||||
*/
|
||||
class SdkManagerOutputParser
|
||||
{
|
||||
class GenericPackageData
|
||||
{
|
||||
public:
|
||||
bool isValid() const { return !revision.isNull() && !description.isNull(); }
|
||||
QStringList headerParts;
|
||||
QVersionNumber revision;
|
||||
QString description;
|
||||
Utils::FilePath installedLocation;
|
||||
QMap<QString, QString> extraData;
|
||||
};
|
||||
|
||||
public:
|
||||
enum MarkerTag
|
||||
{
|
||||
None = 0x001,
|
||||
InstalledPackagesMarker = 0x002,
|
||||
AvailablePackagesMarkers = 0x004,
|
||||
AvailableUpdatesMarker = 0x008,
|
||||
EmptyMarker = 0x010,
|
||||
PlatformMarker = 0x020,
|
||||
SystemImageMarker = 0x040,
|
||||
BuildToolsMarker = 0x080,
|
||||
SdkToolsMarker = 0x100,
|
||||
PlatformToolsMarker = 0x200,
|
||||
EmulatorToolsMarker = 0x400,
|
||||
NdkMarker = 0x800,
|
||||
ExtrasMarker = 0x1000,
|
||||
CmdlineSdkToolsMarker = 0x2000,
|
||||
GenericToolMarker = 0x4000,
|
||||
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
|
||||
};
|
||||
|
||||
SdkManagerOutputParser(AndroidSdkPackageList &container) : m_packages(container) {}
|
||||
void parsePackageListing(const QString &output);
|
||||
|
||||
AndroidSdkPackageList &m_packages;
|
||||
|
||||
private:
|
||||
void compilePackageAssociations();
|
||||
void parsePackageData(MarkerTag packageMarker, const QStringList &data);
|
||||
bool parseAbstractData(GenericPackageData &output, const QStringList &input, int minParts,
|
||||
const QString &logStrTag,
|
||||
const QStringList &extraKeys = QStringList()) const;
|
||||
AndroidSdkPackage *parsePlatform(const QStringList &data) const;
|
||||
QPair<SystemImage *, int> parseSystemImage(const QStringList &data) const;
|
||||
BuildTools *parseBuildToolsPackage(const QStringList &data) const;
|
||||
SdkTools *parseSdkToolsPackage(const QStringList &data) const;
|
||||
PlatformTools *parsePlatformToolsPackage(const QStringList &data) const;
|
||||
EmulatorTools *parseEmulatorToolsPackage(const QStringList &data) const;
|
||||
Ndk *parseNdkPackage(const QStringList &data) const;
|
||||
ExtraTools *parseExtraToolsPackage(const QStringList &data) const;
|
||||
GenericSdkPackage *parseGenericTools(const QStringList &data) const;
|
||||
MarkerTag parseMarkers(const QString &line);
|
||||
|
||||
MarkerTag m_currentSection = MarkerTag::None;
|
||||
QHash<AndroidSdkPackage *, int> m_systemImages;
|
||||
};
|
||||
} // namespace Internal
|
||||
} // namespace Android
|
||||
@@ -42,8 +42,7 @@ using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
namespace ClangCodeModel::Internal {
|
||||
|
||||
void ClangCodeModelPlugin::generateCompilationDB()
|
||||
{
|
||||
@@ -77,11 +76,22 @@ ClangCodeModelPlugin::~ClangCodeModelPlugin()
|
||||
|
||||
void ClangCodeModelPlugin::initialize()
|
||||
{
|
||||
TaskHub::addCategory(Constants::TASK_CATEGORY_DIAGNOSTICS,
|
||||
Tr::tr("Clang Code Model"));
|
||||
TaskHub::addCategory(Constants::TASK_CATEGORY_DIAGNOSTICS, Tr::tr("Clang Code Model"));
|
||||
CppEditor::CppModelManager::instance()->activateClangCodeModel(
|
||||
std::make_unique<ClangModelManagerSupport>());
|
||||
createCompilationDBAction();
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
addTest<Tests::ActivationSequenceProcessorTest>();
|
||||
addTest<Tests::ClangdTestCompletion>();
|
||||
addTest<Tests::ClangdTestExternalChanges>();
|
||||
addTest<Tests::ClangdTestFindReferences>();
|
||||
addTest<Tests::ClangdTestFollowSymbol>();
|
||||
addTest<Tests::ClangdTestHighlighting>();
|
||||
addTest<Tests::ClangdTestLocalReferences>();
|
||||
addTest<Tests::ClangdTestTooltips>();
|
||||
addTest<Tests::ClangFixItTest>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClangCodeModelPlugin::createCompilationDBAction()
|
||||
@@ -158,22 +168,4 @@ void ClangCodeModelPlugin::createCompilationDBAction()
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
QVector<QObject *> ClangCodeModelPlugin::createTestObjects() const
|
||||
{
|
||||
return {
|
||||
new Tests::ActivationSequenceProcessorTest,
|
||||
new Tests::ClangdTestCompletion,
|
||||
new Tests::ClangdTestExternalChanges,
|
||||
new Tests::ClangdTestFindReferences,
|
||||
new Tests::ClangdTestFollowSymbol,
|
||||
new Tests::ClangdTestHighlighting,
|
||||
new Tests::ClangdTestLocalReferences,
|
||||
new Tests::ClangdTestTooltips,
|
||||
new Tests::ClangFixItTest,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Clang
|
||||
} // namespace ClangCodeModel::Internal
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
#include <QFutureWatcher>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
namespace ClangCodeModel::Internal {
|
||||
|
||||
class ClangCodeModelPlugin final: public ExtensionSystem::IPlugin
|
||||
{
|
||||
@@ -29,10 +28,6 @@ private:
|
||||
|
||||
Utils::ParameterAction *m_generateCompilationDBAction = nullptr;
|
||||
QFutureWatcher<GenerateCompilationDbResult> m_generatorWatcher;
|
||||
#ifdef WITH_TESTS
|
||||
QVector<QObject *> createTestObjects() const override;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Clang
|
||||
} // ClangCodeModel::Internal
|
||||
|
||||
@@ -103,15 +103,10 @@ void ClangFormatPlugin::initialize()
|
||||
openClangFormatConfigAction->setData(doc->filePath().toVariant());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QObject *> ClangFormatPlugin::createTestObjects() const
|
||||
{
|
||||
return {
|
||||
#ifdef WITH_TESTS
|
||||
new Internal::ClangFormatTest,
|
||||
addTest<Internal::ClangFormatTest>();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ClangFormat
|
||||
} // ClangFormat
|
||||
|
||||
@@ -16,7 +16,6 @@ class ClangFormatPlugin : public ExtensionSystem::IPlugin
|
||||
|
||||
~ClangFormatPlugin() override;
|
||||
void initialize() final;
|
||||
QVector<QObject *> createTestObjects() const override;
|
||||
|
||||
TextEditor::ICodeStylePreferencesFactory *m_factory = nullptr;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "clangtool.h"
|
||||
#include "clangtoolsconstants.h"
|
||||
#include "clangtoolsprojectsettings.h"
|
||||
#include "clangtoolsprojectsettingswidget.h"
|
||||
#include "clangtoolstr.h"
|
||||
#include "documentclangtoolrunner.h"
|
||||
@@ -49,8 +48,7 @@
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
namespace ClangTools::Internal {
|
||||
|
||||
static ProjectPanelFactory *m_projectPanelFactoryInstance = nullptr;
|
||||
|
||||
@@ -112,6 +110,12 @@ void ClangToolsPlugin::initialize()
|
||||
&Core::EditorManager::currentEditorChanged,
|
||||
this,
|
||||
&ClangToolsPlugin::onCurrentEditorChanged);
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
addTest<PreconfiguredSessionTests>();
|
||||
addTest<ClangToolsUnitTests>();
|
||||
addTest<ReadExportedDiagnosticsTest>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClangToolsPlugin::onCurrentEditorChanged()
|
||||
@@ -185,16 +189,4 @@ void ClangToolsPlugin::registerAnalyzeActions()
|
||||
});
|
||||
}
|
||||
|
||||
QVector<QObject *> ClangToolsPlugin::createTestObjects() const
|
||||
{
|
||||
QVector<QObject *> tests;
|
||||
#ifdef WITH_TESTS
|
||||
tests << new PreconfiguredSessionTests;
|
||||
tests << new ClangToolsUnitTests;
|
||||
tests << new ReadExportedDiagnosticsTest;
|
||||
#endif
|
||||
return tests;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
} // ClangTools::Internal
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
namespace Core { class IDocument; }
|
||||
namespace ProjectExplorer { class ProjectPanelFactory; }
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
namespace ClangTools::Internal {
|
||||
|
||||
ProjectExplorer::ProjectPanelFactory *projectPanelFactory();
|
||||
|
||||
@@ -27,10 +26,7 @@ private:
|
||||
void registerAnalyzeActions();
|
||||
void onCurrentEditorChanged();
|
||||
|
||||
QVector<QObject *> createTestObjects() const final;
|
||||
|
||||
class ClangToolsPluginPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
} // ClangTools::Internal
|
||||
|
||||
@@ -493,7 +493,7 @@ void CMakeBuildSystem::combineScanAndParse(bool restoredFromBackup)
|
||||
Tr::tr("<b>CMake configuration failed<b>"
|
||||
"<p>The backup of the previous configuration has been restored.</p>"
|
||||
"<p>Issues and \"Projects > Build\" settings "
|
||||
"show more information about the failure.</p"));
|
||||
"show more information about the failure.</p>"));
|
||||
|
||||
m_reader.resetData();
|
||||
|
||||
@@ -506,11 +506,10 @@ void CMakeBuildSystem::combineScanAndParse(bool restoredFromBackup)
|
||||
} else {
|
||||
updateFallbackProjectData();
|
||||
|
||||
project()->addIssue(
|
||||
CMakeProject::IssueType::Warning,
|
||||
project()->addIssue(CMakeProject::IssueType::Warning,
|
||||
Tr::tr("<b>Failed to load project<b>"
|
||||
"<p>Issues and \"Projects > Build\" settings "
|
||||
"show more information about the failure.</p"));
|
||||
"show more information about the failure.</p>"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,13 +186,21 @@ static CMakeConfig configurationFromPresetProbe(
|
||||
args.emplace_back(configurePreset.generator.value());
|
||||
}
|
||||
if (configurePreset.architecture && configurePreset.architecture.value().value) {
|
||||
if (!configurePreset.architecture->strategy
|
||||
|| configurePreset.architecture->strategy
|
||||
!= PresetsDetails::ValueStrategyPair::Strategy::external) {
|
||||
args.emplace_back("-A");
|
||||
args.emplace_back(configurePreset.architecture.value().value.value());
|
||||
}
|
||||
}
|
||||
if (configurePreset.toolset && configurePreset.toolset.value().value) {
|
||||
if (!configurePreset.toolset->strategy
|
||||
|| configurePreset.toolset->strategy
|
||||
!= PresetsDetails::ValueStrategyPair::Strategy::external) {
|
||||
args.emplace_back("-T");
|
||||
args.emplace_back(configurePreset.toolset.value().value.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (configurePreset.cacheVariables) {
|
||||
const CMakeConfig cache = configurePreset.cacheVariables
|
||||
@@ -493,6 +501,89 @@ void updateConfigWithDirectoryData(CMakeConfig &config, const std::unique_ptr<Di
|
||||
data->qt.qt->qmakeFilePath().toString().toUtf8());
|
||||
}
|
||||
|
||||
ToolChain *findExternalToolchain(const QString &presetArchitecture, const QString &presetToolset)
|
||||
{
|
||||
// A compiler path example. Note that the compiler version is not the same version from MsvcToolChain
|
||||
// ... \MSVC\14.29.30133\bin\Hostx64\x64\cl.exe
|
||||
//
|
||||
// And the CMakePresets.json
|
||||
//
|
||||
// "toolset": {
|
||||
// "value": "v142,host=x64,version=14.29.30133",
|
||||
// "strategy": "external"
|
||||
// },
|
||||
// "architecture": {
|
||||
// "value": "x64",
|
||||
// "strategy": "external"
|
||||
// }
|
||||
|
||||
auto msvcToolchains = ToolChainManager::toolchains([](const ToolChain *tc) {
|
||||
return tc->typeId() == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID;
|
||||
});
|
||||
|
||||
const QSet<Abi::OSFlavor> msvcFlavors = Utils::toSet(Utils::transform(msvcToolchains, [](const ToolChain *tc) {
|
||||
return tc->targetAbi().osFlavor();
|
||||
}));
|
||||
|
||||
return ToolChainManager::toolChain(
|
||||
[presetArchitecture, presetToolset, msvcFlavors](const ToolChain *tc) -> bool {
|
||||
if (tc->typeId() != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID)
|
||||
return false;
|
||||
|
||||
const FilePath compilerPath = tc->compilerCommand();
|
||||
const QString architecture = compilerPath.parentDir().fileName().toLower();
|
||||
const QString host
|
||||
= compilerPath.parentDir().parentDir().fileName().toLower().replace("host", "host=");
|
||||
const QString version
|
||||
= QString("version=%1")
|
||||
.arg(compilerPath.parentDir().parentDir().parentDir().parentDir().fileName());
|
||||
|
||||
static std::pair<QString, Abi::OSFlavor> abiTable[] = {
|
||||
{QStringLiteral("v143"), Abi::WindowsMsvc2022Flavor},
|
||||
{QStringLiteral("v142"), Abi::WindowsMsvc2019Flavor},
|
||||
{QStringLiteral("v141"), Abi::WindowsMsvc2017Flavor},
|
||||
};
|
||||
|
||||
Abi::OSFlavor toolsetAbi = Abi::UnknownFlavor;
|
||||
for (auto abiPair : abiTable) {
|
||||
if (presetToolset.contains(abiPair.first)) {
|
||||
toolsetAbi = abiPair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// User didn't specify any flavor, so pick the highest toolchain available
|
||||
if (toolsetAbi == Abi::UnknownFlavor) {
|
||||
for (auto abiPair : abiTable) {
|
||||
if (msvcFlavors.contains(abiPair.second)) {
|
||||
toolsetAbi = abiPair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toolsetAbi != tc->targetAbi().osFlavor())
|
||||
return false;
|
||||
|
||||
if (presetToolset.contains("host=") && !presetToolset.contains(host))
|
||||
return false;
|
||||
|
||||
// Make sure we match also version=14.29
|
||||
auto versionIndex = presetToolset.indexOf("version=");
|
||||
if (versionIndex != -1 && !version.startsWith(presetToolset.mid(versionIndex)))
|
||||
return false;
|
||||
|
||||
if (presetArchitecture != architecture)
|
||||
return false;
|
||||
|
||||
qCDebug(cmInputLog) << "For external architecture" << presetArchitecture
|
||||
<< "and toolset" << presetToolset
|
||||
<< "the following toolchain was selected:\n"
|
||||
<< compilerPath.toString();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
|
||||
QString *warningMessage) const
|
||||
{
|
||||
@@ -528,18 +619,37 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
|
||||
if (configurePreset.generator)
|
||||
data->generator = configurePreset.generator.value();
|
||||
|
||||
if (configurePreset.architecture && configurePreset.architecture.value().value)
|
||||
data->platform = configurePreset.architecture.value().value.value();
|
||||
|
||||
if (configurePreset.toolset && configurePreset.toolset.value().value)
|
||||
data->toolset = configurePreset.toolset.value().value.value();
|
||||
|
||||
if (configurePreset.binaryDir) {
|
||||
QString binaryDir = configurePreset.binaryDir.value();
|
||||
CMakePresets::Macros::expand(configurePreset, env, projectDirectory(), binaryDir);
|
||||
data->buildDirectory = Utils::FilePath::fromString(binaryDir);
|
||||
}
|
||||
|
||||
const bool architectureExternalStrategy
|
||||
= configurePreset.architecture && configurePreset.architecture->strategy
|
||||
&& configurePreset.architecture->strategy
|
||||
== PresetsDetails::ValueStrategyPair::Strategy::external;
|
||||
|
||||
const bool toolsetExternalStrategy
|
||||
= configurePreset.toolset && configurePreset.toolset->strategy
|
||||
&& configurePreset.toolset->strategy
|
||||
== PresetsDetails::ValueStrategyPair::Strategy::external;
|
||||
|
||||
if (!architectureExternalStrategy && configurePreset.architecture
|
||||
&& configurePreset.architecture.value().value)
|
||||
data->platform = configurePreset.architecture.value().value.value();
|
||||
|
||||
if (!toolsetExternalStrategy && configurePreset.toolset && configurePreset.toolset.value().value)
|
||||
data->toolset = configurePreset.toolset.value().value.value();
|
||||
|
||||
if (architectureExternalStrategy && toolsetExternalStrategy) {
|
||||
const ToolChain *tc
|
||||
= findExternalToolchain(configurePreset.architecture->value.value_or(QString()),
|
||||
configurePreset.toolset->value.value_or(QString()));
|
||||
if (tc)
|
||||
tc->addToEnvironment(env);
|
||||
}
|
||||
|
||||
CMakePresets::Macros::updateToolchainFile(configurePreset,
|
||||
env,
|
||||
projectDirectory(),
|
||||
|
||||
@@ -190,7 +190,7 @@ void CMakeTargetNode::build()
|
||||
|
||||
void CMakeTargetNode::setTargetInformation(const QList<FilePath> &artifacts, const QString &type)
|
||||
{
|
||||
m_tooltip = Tr::tr("Target type: ") + type + "<br>";
|
||||
m_tooltip = Tr::tr("Target type:") + " " + type + "<br>";
|
||||
if (artifacts.isEmpty()) {
|
||||
m_tooltip += Tr::tr("No build artifacts");
|
||||
} else {
|
||||
|
||||
@@ -285,7 +285,7 @@ void FileApiReader::makeBackupConfiguration(bool store)
|
||||
replyPrev.removeRecursively();
|
||||
QTC_CHECK(!replyPrev.exists());
|
||||
if (!reply.renameFile(replyPrev))
|
||||
Core::MessageManager::writeFlashing(Tr::tr("Failed to rename %1 to %2.")
|
||||
Core::MessageManager::writeFlashing(Tr::tr("Failed to rename \"%1\" to \"%2\".")
|
||||
.arg(reply.toString(), replyPrev.toString()));
|
||||
}
|
||||
|
||||
@@ -296,9 +296,9 @@ void FileApiReader::makeBackupConfiguration(bool store)
|
||||
|
||||
if (cmakeCacheTxt.exists())
|
||||
if (!FileUtils::copyIfDifferent(cmakeCacheTxt, cmakeCacheTxtPrev))
|
||||
Core::MessageManager::writeFlashing(Tr::tr("Failed to copy %1 to %2.")
|
||||
Core::MessageManager::writeFlashing(
|
||||
Tr::tr("Failed to copy \"%1\" to \"%2\".")
|
||||
.arg(cmakeCacheTxt.toString(), cmakeCacheTxtPrev.toString()));
|
||||
|
||||
}
|
||||
|
||||
void FileApiReader::writeConfigurationIntoBuildDirectory(const QStringList &configurationArguments)
|
||||
|
||||
@@ -394,7 +394,7 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage
|
||||
{
|
||||
const Utils::expected_str<QByteArray> jsonContents = jsonFile.fileContents();
|
||||
if (!jsonContents) {
|
||||
errorMessage = Tr::tr("Failed to read %1 file").arg(jsonFile.fileName());
|
||||
errorMessage = Tr::tr("Failed to read file \"%1\".").arg(jsonFile.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -410,14 +410,14 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage
|
||||
}
|
||||
|
||||
if (!jsonDoc.isObject()) {
|
||||
errorMessage = Tr::tr( "Invalid %1 file").arg(jsonFile.fileName());
|
||||
errorMessage = Tr::tr("Invalid file \"%1\".").arg(jsonFile.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject root = jsonDoc.object();
|
||||
|
||||
if (!parseVersion(root.value("version"), m_presetsData.version)) {
|
||||
errorMessage = Tr::tr("Invalid \"version\" in %1 file").arg(jsonFile.fileName());
|
||||
errorMessage = Tr::tr("Invalid \"version\" in file \"%1\".").arg(jsonFile.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace CompilationDatabaseProjectManager {
|
||||
namespace Internal {
|
||||
namespace CompilationDatabaseProjectManager::Internal {
|
||||
|
||||
const char CHANGEROOTDIR[] = "CompilationDatabaseProjectManager.ChangeRootDirectory";
|
||||
const char COMPILE_COMMANDS_JSON[] = "compile_commands.json";
|
||||
@@ -78,16 +77,10 @@ void CompilationDatabaseProjectManagerPlugin::initialize()
|
||||
|
||||
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
|
||||
this, onProjectChanged);
|
||||
}
|
||||
|
||||
QVector<QObject *> CompilationDatabaseProjectManagerPlugin::createTestObjects() const
|
||||
{
|
||||
return {
|
||||
#ifdef WITH_TESTS
|
||||
new CompilationDatabaseTests
|
||||
addTest<CompilationDatabaseTests>();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CompilationDatabaseProjectManager
|
||||
} // CompilationDatabaseProjectManager::Internal
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
namespace CompilationDatabaseProjectManager {
|
||||
namespace Internal {
|
||||
namespace CompilationDatabaseProjectManager::Internal {
|
||||
|
||||
class CompilationDatabaseProjectManagerPlugin final : public ExtensionSystem::IPlugin
|
||||
{
|
||||
@@ -16,10 +15,8 @@ class CompilationDatabaseProjectManagerPlugin final : public ExtensionSystem::IP
|
||||
~CompilationDatabaseProjectManagerPlugin();
|
||||
|
||||
void initialize() final;
|
||||
QVector<QObject *> createTestObjects() const final;
|
||||
|
||||
class CompilationDatabaseProjectManagerPluginPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CompilationDatabaseProjectManager
|
||||
} // CompilationDatabaseProjectManager::Internal
|
||||
|
||||
@@ -182,7 +182,7 @@ void filteredFlags(const QString &fileName,
|
||||
|
||||
if (flag.startsWith("--sysroot=")) {
|
||||
if (sysRoot.isEmpty())
|
||||
sysRoot = FilePath::fromString(updatedPathFlag(flag.mid(10), workingDir));
|
||||
sysRoot = FilePath::fromUserInput(updatedPathFlag(flag.mid(10), workingDir));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@ void CorePlugin::warnAboutCrashReporing()
|
||||
|
||||
Utils::InfoBarEntry info(kWarnCrashReportingSetting, warnStr,
|
||||
Utils::InfoBarEntry::GlobalSuppression::Enabled);
|
||||
info.addCustomButton(Tr::tr("Configure..."), [] {
|
||||
info.addCustomButton(ICore::msgShowOptionsDialog(), [] {
|
||||
ICore::infoBar()->removeInfo(kWarnCrashReportingSetting);
|
||||
ICore::infoBar()->globallySuppressInfo(kWarnCrashReportingSetting);
|
||||
ICore::showOptionsDialog(Core::Constants::SETTINGS_ID_SYSTEM);
|
||||
|
||||
@@ -370,7 +370,7 @@ void ClangDiagnosticConfigsWidget::updateValidityWidgets(const QString &errorMes
|
||||
m_infoLabel->setFilled(false);
|
||||
} else {
|
||||
m_infoLabel->setType(InfoLabel::Error);
|
||||
m_infoLabel->setText(Tr::tr("%1").arg(errorMessage));
|
||||
m_infoLabel->setText(errorMessage);
|
||||
m_infoLabel->setFilled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,6 +464,36 @@ void CppEditorPlugin::initialize()
|
||||
d, &CppEditorPluginPrivate::onTaskStarted);
|
||||
connect(ProgressManager::instance(), &ProgressManager::allTasksFinished,
|
||||
d, &CppEditorPluginPrivate::onAllTasksFinished);
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
addTest<CodegenTest>();
|
||||
addTest<CompilerOptionsBuilderTest>();
|
||||
addTest<CompletionTest>();
|
||||
addTest<CppHighlighterTest>();
|
||||
addTest<FunctionUtilsTest>();
|
||||
addTest<HeaderPathFilterTest>();
|
||||
addTest<HeaderSourceTest>();
|
||||
addTest<IncludeGroupsTest>();
|
||||
addTest<LocalSymbolsTest>();
|
||||
addTest<LocatorFilterTest>();
|
||||
addTest<ModelManagerTest>();
|
||||
addTest<PointerDeclarationFormatterTest>();
|
||||
addTest<ProjectFileCategorizerTest>();
|
||||
addTest<ProjectInfoGeneratorTest>();
|
||||
addTest<ProjectPartChooserTest>();
|
||||
addTest<DocumentTrackerTest>();
|
||||
addTest<SourceProcessorTest>();
|
||||
addTest<SymbolSearcherTest>();
|
||||
addTest<TypeHierarchyBuilderTest>();
|
||||
addTest<Tests::AutoCompleterTest>();
|
||||
addTest<Tests::DoxygenTest>();
|
||||
addTest<Tests::FileAndTokenActionsTest>();
|
||||
addTest<Tests::FollowSymbolTest>();
|
||||
addTest<Tests::IncludeHierarchyTest>();
|
||||
addTest<Tests::InsertVirtualMethodsTest>();
|
||||
addTest<Tests::QuickfixTest>();
|
||||
addTest<Tests::SelectionsTest>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CppEditorPlugin::extensionsInitialized()
|
||||
@@ -560,41 +590,6 @@ void CppEditorPluginPrivate::inspectCppCodeModel()
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QObject *> CppEditorPlugin::createTestObjects() const
|
||||
{
|
||||
return {
|
||||
#ifdef WITH_TESTS
|
||||
new CodegenTest,
|
||||
new CompilerOptionsBuilderTest,
|
||||
new CompletionTest,
|
||||
new CppHighlighterTest,
|
||||
new FunctionUtilsTest,
|
||||
new HeaderPathFilterTest,
|
||||
new HeaderSourceTest,
|
||||
new IncludeGroupsTest,
|
||||
new LocalSymbolsTest,
|
||||
new LocatorFilterTest,
|
||||
new ModelManagerTest,
|
||||
new PointerDeclarationFormatterTest,
|
||||
new ProjectFileCategorizerTest,
|
||||
new ProjectInfoGeneratorTest,
|
||||
new ProjectPartChooserTest,
|
||||
new DocumentTrackerTest,
|
||||
new SourceProcessorTest,
|
||||
new SymbolSearcherTest,
|
||||
new TypeHierarchyBuilderTest,
|
||||
new Tests::AutoCompleterTest,
|
||||
new Tests::DoxygenTest,
|
||||
new Tests::FileAndTokenActionsTest,
|
||||
new Tests::FollowSymbolTest,
|
||||
new Tests::IncludeHierarchyTest,
|
||||
new Tests::InsertVirtualMethodsTest,
|
||||
new Tests::QuickfixTest,
|
||||
new Tests::SelectionsTest,
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
void CppEditorPlugin::openTypeHierarchy()
|
||||
{
|
||||
if (currentCppEditorWidget()) {
|
||||
|
||||
@@ -55,7 +55,6 @@ signals:
|
||||
private:
|
||||
void initialize() override;
|
||||
void extensionsInitialized() override;
|
||||
QVector<QObject *> createTestObjects() const override;
|
||||
|
||||
CppEditorPluginPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
@@ -354,8 +354,8 @@ void CppModelManager::showPreprocessedFile(bool inNextSplit)
|
||||
.arg(reason));
|
||||
};
|
||||
static const auto showFallbackWarning = [](const QString &reason) {
|
||||
Core::MessageManager::writeSilently(Tr::tr("%1, falling back to built-in preprocessor.")
|
||||
.arg(reason));
|
||||
Core::MessageManager::writeSilently(
|
||||
Tr::tr("Falling back to built-in preprocessor: %1").arg(reason));
|
||||
};
|
||||
static const auto saveAndOpen = [](const FilePath &filePath, const QByteArray &contents,
|
||||
bool inNextSplit) {
|
||||
|
||||
@@ -2864,7 +2864,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory,
|
||||
GitPlugin::updateCurrentBranch();
|
||||
return true;
|
||||
}
|
||||
VcsOutputWindow::appendError(Tr::tr("Cannot commit %n files\n", nullptr, commitCount));
|
||||
VcsOutputWindow::appendError(Tr::tr("Cannot commit %n files", nullptr, commitCount) + "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
repositoryLabel = new QLabel(Tr::tr("repository"));
|
||||
branchLabel = new QLabel(Tr::tr("branch")); // FIXME: Isn't this overwritten soon?
|
||||
showHeadLabel = new QLabel(Tr::tr("<a href=\"head\">Show HEAD</a>")); // FIXME: Simplify string in tr()
|
||||
showHeadLabel = new QLabel("<a href=\"head\">" + Tr::tr("Show HEAD") + "</a>");
|
||||
|
||||
authorLineEdit = new QLineEdit;
|
||||
authorLineEdit->setObjectName("authorLineEdit");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "haskellbuildconfiguration.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskelltr.h"
|
||||
|
||||
#include <projectexplorer/buildinfo.h>
|
||||
#include <projectexplorer/buildsteplist.h>
|
||||
@@ -35,7 +36,7 @@ HaskellBuildConfigurationFactory::HaskellBuildConfigurationFactory()
|
||||
|
||||
setBuildGenerator([](const Kit *k, const Utils::FilePath &projectPath, bool forSetup) {
|
||||
BuildInfo info;
|
||||
info.typeName = HaskellBuildConfiguration::tr("Release");
|
||||
info.typeName = Tr::tr("Release");
|
||||
if (forSetup) {
|
||||
info.displayName = info.typeName;
|
||||
info.buildDirectory = projectPath.parentDir().pathAppended(".stack-work");
|
||||
@@ -73,7 +74,7 @@ void HaskellBuildConfiguration::setBuildType(BuildConfiguration::BuildType type)
|
||||
}
|
||||
|
||||
HaskellBuildConfigurationWidget::HaskellBuildConfigurationWidget(HaskellBuildConfiguration *bc)
|
||||
: NamedWidget(tr("General"))
|
||||
: NamedWidget(Tr::tr("General"))
|
||||
, m_buildConfiguration(bc)
|
||||
{
|
||||
setLayout(new QVBoxLayout);
|
||||
@@ -85,7 +86,7 @@ HaskellBuildConfigurationWidget::HaskellBuildConfigurationWidget(HaskellBuildCon
|
||||
box->setWidget(details);
|
||||
details->setLayout(new QHBoxLayout);
|
||||
details->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
details->layout()->addWidget(new QLabel(tr("Build directory:")));
|
||||
details->layout()->addWidget(new QLabel(Tr::tr("Build directory:")));
|
||||
|
||||
auto buildDirectoryInput = new Utils::PathChooser;
|
||||
buildDirectoryInput->setExpectedKind(Utils::PathChooser::Directory);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellhighlighter.h"
|
||||
#include "haskellmanager.h"
|
||||
#include "haskelltr.h"
|
||||
|
||||
#include <coreplugin/actionmanager/commandbutton.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
@@ -21,7 +22,7 @@ static QWidget *createEditorWidget()
|
||||
{
|
||||
auto widget = new TextEditor::TextEditorWidget;
|
||||
auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget);
|
||||
ghciButton->setText(HaskellManager::tr("GHCi"));
|
||||
ghciButton->setText(Tr::tr("GHCi"));
|
||||
QObject::connect(ghciButton, &QToolButton::clicked, HaskellManager::instance(), [widget] {
|
||||
HaskellManager::openGhci(widget->textDocument()->filePath());
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "haskellmanager.h"
|
||||
|
||||
#include "haskelltr.h"
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/commandline.h>
|
||||
@@ -92,7 +94,7 @@ void HaskellManager::openGhci(const FilePath &haskellFile)
|
||||
connect(p, &QtcProcess::done, p, [p] {
|
||||
if (p->result() != ProcessResult::FinishedWithSuccess) {
|
||||
Core::MessageManager::writeDisrupting(
|
||||
tr("Failed to run GHCi: \"%1\".").arg(p->errorString()));
|
||||
Tr::tr("Failed to run GHCi: \"%1\".").arg(p->errorString()));
|
||||
}
|
||||
p->deleteLater();
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "haskellmanager.h"
|
||||
#include "haskellproject.h"
|
||||
#include "haskellrunconfiguration.h"
|
||||
#include "haskelltr.h"
|
||||
#include "optionspage.h"
|
||||
#include "stackbuildstep.h"
|
||||
|
||||
@@ -42,7 +43,7 @@ HaskellPlugin::~HaskellPlugin()
|
||||
|
||||
static void registerGhciAction()
|
||||
{
|
||||
QAction *action = new QAction(HaskellManager::tr("Run GHCi"), HaskellManager::instance());
|
||||
QAction *action = new QAction(Tr::tr("Run GHCi"), HaskellManager::instance());
|
||||
Core::ActionManager::registerAction(action, Constants::A_RUN_GHCI);
|
||||
QObject::connect(action, &QAction::triggered, HaskellManager::instance(), [] {
|
||||
if (Core::IDocument *doc = Core::EditorManager::currentDocument())
|
||||
@@ -60,7 +61,7 @@ bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorStrin
|
||||
ProjectExplorer::ProjectManager::registerProjectType<HaskellProject>(
|
||||
Constants::C_HASKELL_PROJECT_MIMETYPE);
|
||||
TextEditor::SnippetProvider::registerGroup(Constants::C_HASKELLSNIPPETSGROUP_ID,
|
||||
tr("Haskell", "SnippetProvider"));
|
||||
Tr::tr("Haskell", "SnippetProvider"));
|
||||
|
||||
connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [] {
|
||||
HaskellManager::writeSettings(Core::ICore::settings());
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellmanager.h"
|
||||
#include "haskellproject.h"
|
||||
#include "haskelltr.h"
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/localenvironmentaspect.h>
|
||||
@@ -28,7 +29,7 @@ HaskellRunConfigurationFactory::HaskellRunConfigurationFactory()
|
||||
HaskellExecutableAspect::HaskellExecutableAspect()
|
||||
{
|
||||
setSettingsKey("Haskell.Executable");
|
||||
setLabelText(tr("Executable"));
|
||||
setLabelText(Tr::tr("Executable"));
|
||||
}
|
||||
|
||||
HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Utils::Id id)
|
||||
|
||||
15
src/plugins/haskell/haskelltr.h
Normal file
15
src/plugins/haskell/haskelltr.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace Haskell {
|
||||
|
||||
struct Tr
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QtC::Haskell)
|
||||
};
|
||||
|
||||
} // namespace Haskell
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellmanager.h"
|
||||
#include "haskelltr.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
@@ -18,9 +19,9 @@ namespace Internal {
|
||||
OptionsPage::OptionsPage()
|
||||
{
|
||||
setId(Constants::OPTIONS_GENERAL);
|
||||
setDisplayName(tr("General"));
|
||||
setDisplayName(Tr::tr("General"));
|
||||
setCategory("J.Z.Haskell");
|
||||
setDisplayCategory(tr("Haskell"));
|
||||
setDisplayCategory(Tr::tr("Haskell"));
|
||||
setCategoryIcon(Utils::Icon(":/haskell/images/category_haskell.png"));
|
||||
}
|
||||
|
||||
@@ -31,15 +32,15 @@ QWidget *OptionsPage::widget()
|
||||
m_widget = new QWidget;
|
||||
auto topLayout = new QVBoxLayout;
|
||||
m_widget->setLayout(topLayout);
|
||||
auto generalBox = new QGroupBox(tr("General"));
|
||||
auto generalBox = new QGroupBox(Tr::tr("General"));
|
||||
topLayout->addWidget(generalBox);
|
||||
topLayout->addStretch(10);
|
||||
auto boxLayout = new QHBoxLayout;
|
||||
generalBox->setLayout(boxLayout);
|
||||
boxLayout->addWidget(new QLabel(tr("Stack executable:")));
|
||||
boxLayout->addWidget(new QLabel(Tr::tr("Stack executable:")));
|
||||
m_stackPath = new PathChooser();
|
||||
m_stackPath->setExpectedKind(PathChooser::ExistingCommand);
|
||||
m_stackPath->setPromptDialogTitle(tr("Choose Stack Executable"));
|
||||
m_stackPath->setPromptDialogTitle(Tr::tr("Choose Stack Executable"));
|
||||
m_stackPath->setFilePath(HaskellManager::stackExecutable());
|
||||
m_stackPath->setCommandVersionArguments({"--version"});
|
||||
boxLayout->addWidget(m_stackPath);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellmanager.h"
|
||||
#include "haskelltr.h"
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/processparameters.h>
|
||||
@@ -29,7 +30,7 @@ QWidget *StackBuildStep::createConfigWidget()
|
||||
|
||||
QString StackBuildStep::trDisplayName()
|
||||
{
|
||||
return tr("Stack Build");
|
||||
return Tr::tr("Stack Build");
|
||||
}
|
||||
|
||||
bool StackBuildStep::init()
|
||||
|
||||
@@ -41,7 +41,7 @@ MacroOptionsWidget::MacroOptionsWidget()
|
||||
m_treeWidget->header()->setSortIndicatorShown(true);
|
||||
m_treeWidget->header()->setStretchLastSection(true);
|
||||
m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description)"), Tr::tr("Shortcut")});
|
||||
m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")});
|
||||
|
||||
m_description = new QLineEdit;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
virtual bool isValidStatus() const = 0;
|
||||
|
||||
virtual bool writeToSettings() const = 0;
|
||||
virtual void readFromSettings() = 0;
|
||||
|
||||
virtual QWidget *widget() = 0;
|
||||
virtual const McuPackageVersionDetector *getVersionDetector() const = 0;
|
||||
|
||||
@@ -244,6 +244,11 @@ bool McuPackage::writeToSettings() const
|
||||
return settingsHandler->write(m_settingsKey, m_path, m_defaultPath);
|
||||
}
|
||||
|
||||
void McuPackage::readFromSettings()
|
||||
{
|
||||
setPath(settingsHandler->getPath(m_settingsKey, QSettings::UserScope, m_defaultPath));
|
||||
}
|
||||
|
||||
QWidget *McuPackage::widget()
|
||||
{
|
||||
auto *widget = new QWidget;
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
QString statusText() const override;
|
||||
|
||||
bool writeToSettings() const override;
|
||||
void readFromSettings() override;
|
||||
|
||||
QWidget *widget() override;
|
||||
const McuPackageVersionDetector *getVersionDetector() const override;
|
||||
|
||||
@@ -90,6 +90,9 @@ McuSupportOptionsWidget::McuSupportOptionsWidget(McuSupportOptions &options,
|
||||
m_qtForMCUsSdkGroupBox = new QGroupBox(Tr::tr("Qt for MCUs SDK"));
|
||||
m_qtForMCUsSdkGroupBox->setFlat(true);
|
||||
auto *layout = new QVBoxLayout(m_qtForMCUsSdkGroupBox);
|
||||
// Re-read the qtForMCUs package from settings to discard un-applied changes from previous sessions
|
||||
m_options.qtForMCUsSdkPackage->readFromSettings();
|
||||
|
||||
layout->addWidget(m_options.qtForMCUsSdkPackage->widget());
|
||||
mainLayout->addWidget(m_qtForMCUsSdkGroupBox);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
MOCK_METHOD(QString, environmentVariableName, (), (const));
|
||||
MOCK_METHOD(bool, isAddToSystemPath, (), (const));
|
||||
MOCK_METHOD(bool, writeToSettings, (), (const));
|
||||
MOCK_METHOD(void, readFromSettings, ());
|
||||
MOCK_METHOD(QStringList, versions, (), (const));
|
||||
|
||||
MOCK_METHOD(QWidget *, widget, ());
|
||||
|
||||
@@ -188,7 +188,7 @@ AppOutputPane::AppOutputPane() :
|
||||
connect(this, &IOutputPane::zoomOutRequested, this, &AppOutputPane::zoomOut);
|
||||
connect(this, &IOutputPane::resetZoomRequested, this, &AppOutputPane::resetZoom);
|
||||
|
||||
m_settingsButton->setToolTip(Tr::tr("Open Settings Page"));
|
||||
m_settingsButton->setToolTip(Core::ICore::msgShowOptionsDialog());
|
||||
m_settingsButton->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon());
|
||||
connect(m_settingsButton, &QToolButton::clicked, this, [] {
|
||||
Core::ICore::showOptionsDialog(OPTIONS_PAGE_ID);
|
||||
|
||||
@@ -64,7 +64,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) :
|
||||
Utils::ProxyAction::proxyActionWithIcon(cancelBuildAction,
|
||||
Utils::Icons::STOP_SMALL_TOOLBAR.icon());
|
||||
m_cancelBuildButton->setDefaultAction(cancelBuildProxyButton);
|
||||
m_settingsButton->setToolTip(Tr::tr("Open Settings Page"));
|
||||
m_settingsButton->setToolTip(Core::ICore::msgShowOptionsDialog());
|
||||
m_settingsButton->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon());
|
||||
|
||||
auto updateFontSettings = [this] {
|
||||
|
||||
@@ -102,7 +102,7 @@ const char BUILD_AND_RUN_SETTINGS_CATEGORY[] = "K.BuildAndRun";
|
||||
const char BUILD_AND_RUN_SETTINGS_PAGE_ID[] = "A.ProjectExplorer.BuildAndRunOptions";
|
||||
|
||||
// Device settings page
|
||||
const char DEVICE_SETTINGS_CATEGORY[] = "XW.Devices";
|
||||
const char DEVICE_SETTINGS_CATEGORY[] = "AM.Devices";
|
||||
const char DEVICE_SETTINGS_PAGE_ID[] = "AA.Device Settings";
|
||||
|
||||
// Task categories
|
||||
@@ -117,12 +117,6 @@ const char TASK_CATEGORY_TASKLIST_ID[] = "Task.Category.TaskListId";
|
||||
const char QT_PROJECT_WIZARD_CATEGORY[] = "H.Project";
|
||||
const char QT_PROJECT_WIZARD_CATEGORY_DISPLAY[] = QT_TRANSLATE_NOOP("QtC::ProjectExplorer", "Other Project");
|
||||
|
||||
const char QT_APPLICATION_WIZARD_CATEGORY[] = "F.Application";
|
||||
const char QT_APPLICATION_WIZARD_CATEGORY_DISPLAY[] = QT_TRANSLATE_NOOP("QtC::ProjectExplorer", "Application");
|
||||
|
||||
const char LIBRARIES_WIZARD_CATEGORY[] = "G.Library";
|
||||
const char LIBRARIES_WIZARD_CATEGORY_DISPLAY[] = QT_TRANSLATE_NOOP("QtC::ProjectExplorer", "Library");
|
||||
|
||||
const char IMPORT_WIZARD_CATEGORY[] = "T.Import";
|
||||
const char IMPORT_WIZARD_CATEGORY_DISPLAY[] = QT_TRANSLATE_NOOP("QtC::ProjectExplorer", "Import Project");
|
||||
|
||||
|
||||
@@ -203,6 +203,11 @@ QmlPreviewPlugin::~QmlPreviewPlugin()
|
||||
void QmlPreviewPlugin::initialize()
|
||||
{
|
||||
d = new QmlPreviewPluginPrivate(this);
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
addTest<QmlPreviewClientTest>();
|
||||
addTest<QmlPreviewPluginTest>();
|
||||
#endif
|
||||
}
|
||||
|
||||
ExtensionSystem::IPlugin::ShutdownFlag QmlPreviewPlugin::aboutToShutdown()
|
||||
@@ -212,16 +217,6 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlPreviewPlugin::aboutToShutdown()
|
||||
return SynchronousShutdown;
|
||||
}
|
||||
|
||||
QVector<QObject *> QmlPreviewPlugin::createTestObjects() const
|
||||
{
|
||||
QVector<QObject *> tests;
|
||||
#ifdef WITH_TESTS
|
||||
tests.append(new QmlPreviewClientTest);
|
||||
tests.append(new QmlPreviewPluginTest);
|
||||
#endif
|
||||
return tests;
|
||||
}
|
||||
|
||||
QString QmlPreviewPlugin::previewedFile() const
|
||||
{
|
||||
return d->m_previewedFile;
|
||||
|
||||
@@ -50,7 +50,6 @@ public:
|
||||
|
||||
void initialize() override;
|
||||
ShutdownFlag aboutToShutdown() override;
|
||||
QVector<QObject *> createTestObjects() const override;
|
||||
|
||||
QString previewedFile() const;
|
||||
void setPreviewedFile(const QString &previewedFile);
|
||||
|
||||
@@ -14,7 +14,7 @@ struct QmlPreviewRunnerSetting
|
||||
QmlPreviewFileLoader fileLoader;
|
||||
QmlPreviewFileClassifier fileClassifier;
|
||||
QmlPreviewFpsHandler fpsHandler;
|
||||
float zoomFactor = 1.0;
|
||||
float zoomFactor = -1.0;
|
||||
QString language;
|
||||
QmlDebugTranslationClientCreator createDebugTranslationClientMethod;
|
||||
};
|
||||
|
||||
@@ -71,6 +71,31 @@ public:
|
||||
bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
addTest<DebugMessagesModelTest>();
|
||||
addTest<FlameGraphModelTest>();
|
||||
addTest<FlameGraphViewTest>();
|
||||
addTest<InputEventsModelTest>();
|
||||
addTest<LocalQmlProfilerRunnerTest>();
|
||||
addTest<MemoryUsageModelTest>();
|
||||
addTest<PixmapCacheModelTest>();
|
||||
addTest<QmlEventTest>();
|
||||
addTest<QmlEventLocationTest>();
|
||||
addTest<QmlEventTypeTest>();
|
||||
addTest<QmlNoteTest>();
|
||||
addTest<QmlProfilerAnimationsModelTest>();
|
||||
addTest<QmlProfilerAttachDialogTest>();
|
||||
addTest<QmlProfilerBindingLoopsRenderPassTest>();
|
||||
addTest<QmlProfilerClientManagerTest>();
|
||||
addTest<QmlProfilerDetailsRewriterTest>();
|
||||
addTest<QmlProfilerToolTest>();
|
||||
addTest<QmlProfilerTraceClientTest>();
|
||||
addTest<QmlProfilerTraceViewTest>();
|
||||
|
||||
addTest<QQmlEngine>(); // Trigger debug connector to be started
|
||||
#endif
|
||||
|
||||
return Utils::HostOsInfo::canCreateOpenGLContext(errorString);
|
||||
}
|
||||
|
||||
@@ -99,33 +124,4 @@ QmlProfilerSettings *QmlProfilerPlugin::globalSettings()
|
||||
return qmlProfilerGlobalSettings();
|
||||
}
|
||||
|
||||
QVector<QObject *> QmlProfiler::Internal::QmlProfilerPlugin::createTestObjects() const
|
||||
{
|
||||
QVector<QObject *> tests;
|
||||
#ifdef WITH_TESTS
|
||||
tests << new DebugMessagesModelTest;
|
||||
tests << new FlameGraphModelTest;
|
||||
tests << new FlameGraphViewTest;
|
||||
tests << new InputEventsModelTest;
|
||||
tests << new LocalQmlProfilerRunnerTest;
|
||||
tests << new MemoryUsageModelTest;
|
||||
tests << new PixmapCacheModelTest;
|
||||
tests << new QmlEventTest;
|
||||
tests << new QmlEventLocationTest;
|
||||
tests << new QmlEventTypeTest;
|
||||
tests << new QmlNoteTest;
|
||||
tests << new QmlProfilerAnimationsModelTest;
|
||||
tests << new QmlProfilerAttachDialogTest;
|
||||
tests << new QmlProfilerBindingLoopsRenderPassTest;
|
||||
tests << new QmlProfilerClientManagerTest;
|
||||
tests << new QmlProfilerDetailsRewriterTest;
|
||||
tests << new QmlProfilerToolTest;
|
||||
tests << new QmlProfilerTraceClientTest;
|
||||
tests << new QmlProfilerTraceViewTest;
|
||||
|
||||
tests << new QQmlEngine; // Trigger debug connector to be started
|
||||
#endif
|
||||
return tests;
|
||||
}
|
||||
|
||||
} // QmlProfiler::Internal
|
||||
|
||||
@@ -21,7 +21,6 @@ private:
|
||||
bool initialize(const QStringList &arguments, QString *errorString) final;
|
||||
void extensionsInitialized() final;
|
||||
ShutdownFlag aboutToShutdown() final;
|
||||
QVector<QObject *> createTestObjects() const final;
|
||||
|
||||
class QmlProfilerPluginPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
@@ -1022,10 +1022,17 @@ LinuxDevice::LinuxDevice()
|
||||
d->m_terminals.removeOne(proc);
|
||||
});
|
||||
|
||||
// Empty command for ssh implies the user's shell, which would be nice in general,
|
||||
// but we can't use that if we modify environment settings, as variables without
|
||||
// command don't work on the ssh commandline.
|
||||
const QString shell = env.toDictionary().size() == 0 ? QString() : QString("/bin/sh");
|
||||
// We recreate the same way that QtcProcess uses to create the actual environment.
|
||||
const Environment finalEnv = (!env.hasChanges() && env.combineWithDeviceEnvironment())
|
||||
? d->getEnvironment()
|
||||
: env;
|
||||
// If we will not set any environment variables, we can leave out the shell executable
|
||||
// as the "ssh ..." call will automatically launch the default shell if there are
|
||||
// no arguments. But if we will set environment variables, we need to explicitly
|
||||
// specify the shell executable.
|
||||
const QString shell = finalEnv.hasChanges() ? finalEnv.value_or("SHELL", "/bin/sh")
|
||||
: QString();
|
||||
|
||||
proc->setCommand({filePath(shell), {}});
|
||||
proc->setTerminalMode(TerminalMode::On);
|
||||
proc->setEnvironment(env);
|
||||
|
||||
@@ -209,7 +209,7 @@ void CodeStyleSelectorWidget::slotImportClicked()
|
||||
else
|
||||
QMessageBox::warning(this,
|
||||
Tr::tr("Import Code Style"),
|
||||
Tr::tr("Cannot import code style from %1")
|
||||
Tr::tr("Cannot import code style from \"%1\".")
|
||||
.arg(fileName.toUserOutput()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ SubmitEditorWidget::SubmitEditorWidget() :
|
||||
d->buttonLayout->setContentsMargins(0, -1, -1, -1);
|
||||
QToolButton *openSettingsButton = new QToolButton;
|
||||
openSettingsButton->setIcon(Utils::Icons::SETTINGS.icon());
|
||||
openSettingsButton->setToolTip(Tr::tr("Open Settings Page..."));
|
||||
openSettingsButton->setToolTip(Core::ICore::msgShowOptionsDialog());
|
||||
connect(openSettingsButton, &QToolButton::clicked, this, [] {
|
||||
Core::ICore::showOptionsDialog(Constants::VCS_COMMON_SETTINGS_ID);
|
||||
});
|
||||
|
||||
@@ -15,3 +15,10 @@ add_qtc_plugin(WebAssembly
|
||||
webassemblyrunconfiguration.cpp webassemblyrunconfiguration.h
|
||||
webassemblytoolchain.cpp webassemblytoolchain.h
|
||||
)
|
||||
|
||||
extend_qtc_plugin(WebAssembly
|
||||
CONDITION WITH_TESTS
|
||||
SOURCES
|
||||
webassembly_test.cpp
|
||||
webassembly_test.h
|
||||
)
|
||||
|
||||
@@ -32,4 +32,13 @@ QtcPlugin {
|
||||
"webassemblytoolchain.cpp",
|
||||
"webassemblytoolchain.h",
|
||||
]
|
||||
|
||||
Group {
|
||||
name: "Unit tests"
|
||||
condition: qtc.testsEnabled
|
||||
files: [
|
||||
"webassembly_test.cpp",
|
||||
"webassembly_test.h",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
120
src/plugins/webassembly/webassembly_test.cpp
Normal file
120
src/plugins/webassembly/webassembly_test.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "webassembly_test.h"
|
||||
|
||||
#include "webassemblyemsdk.h"
|
||||
#include "webassemblyrunconfigurationaspects.h"
|
||||
|
||||
#include <utils/environment.h>
|
||||
|
||||
#include <QTest>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace WebAssembly::Internal {
|
||||
|
||||
void WebAssemblyTest::testEmSdkEnvParsing()
|
||||
{
|
||||
QFETCH(QString, emSdkEnvOutput);
|
||||
QFETCH(int, osType);
|
||||
QFETCH(int, pathCount);
|
||||
QFETCH(QString, emsdk);
|
||||
QFETCH(QString, em_config);
|
||||
|
||||
Environment env{OsType(osType)};
|
||||
WebAssemblyEmSdk::parseEmSdkEnvOutputAndAddToEnv(emSdkEnvOutput, env);
|
||||
|
||||
QVERIFY(env.path().count() == pathCount);
|
||||
QCOMPARE(env.value("EMSDK"), emsdk);
|
||||
QCOMPARE(env.value("EM_CONFIG"), em_config);
|
||||
}
|
||||
|
||||
void WebAssemblyTest::testEmSdkEnvParsing_data()
|
||||
{
|
||||
// Output of "emsdk_env"
|
||||
QTest::addColumn<QString>("emSdkEnvOutput");
|
||||
QTest::addColumn<int>("osType");
|
||||
QTest::addColumn<int>("pathCount");
|
||||
QTest::addColumn<QString>("emsdk");
|
||||
QTest::addColumn<QString>("em_config");
|
||||
|
||||
QTest::newRow("windows") << R"(
|
||||
Adding directories to PATH:
|
||||
PATH += C:\Users\user\dev\emsdk
|
||||
PATH += C:\Users\user\dev\emsdk\upstream\emscripten
|
||||
PATH += C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin
|
||||
PATH += C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit
|
||||
PATH += C:\Users\user\dev\emsdk\java\8.152_64bit\bin
|
||||
|
||||
Setting environment variables:
|
||||
PATH = C:\Users\user\dev\emsdk;C:\Users\user\dev\emsdk\upstream\emscripten;C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin;C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit;C:\Users\user\dev\emsdk\java\8.152_64bit\bin;...other_stuff...
|
||||
EMSDK = C:/Users/user/dev/emsdk
|
||||
EM_CONFIG = C:\Users\user\dev\emsdk\.emscripten
|
||||
EM_CACHE = C:/Users/user/dev/emsdk/upstream/emscripten\cache
|
||||
EMSDK_NODE = C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin\node.exe
|
||||
EMSDK_PYTHON = C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit\python.exe
|
||||
JAVA_HOME = C:\Users\user\dev\emsdk\java\8.152_64bit
|
||||
)" << int(OsTypeWindows) << 6 << "C:/Users/user/dev/emsdk" << "C:\\Users\\user\\dev\\emsdk\\.emscripten";
|
||||
|
||||
QTest::newRow("linux") << R"(
|
||||
Adding directories to PATH:
|
||||
PATH += /home/user/dev/emsdk
|
||||
PATH += /home/user/dev/emsdk/upstream/emscripten
|
||||
PATH += /home/user/dev/emsdk/node/12.18.1_64bit/bin
|
||||
|
||||
Setting environment variables:
|
||||
PATH = /home/user/dev/emsdk:/home/user/dev/emsdk/upstream/emscripten:/home/user/dev/emsdk/node/12.18.1_64bit/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||
EMSDK = /home/user/dev/emsdk
|
||||
EM_CONFIG = /home/user/dev/emsdk/.emscripten
|
||||
EM_CACHE = /home/user/dev/emsdk/upstream/emscripten/cache
|
||||
EMSDK_NODE = /home/user/dev/emsdk/node/12.18.1_64bit/bin/node
|
||||
)" << int(OsTypeLinux) << 3 << "/home/user/dev/emsdk" << "/home/user/dev/emsdk/.emscripten";
|
||||
}
|
||||
|
||||
void WebAssemblyTest::testEmrunBrowserListParsing()
|
||||
{
|
||||
QFETCH(QByteArray, emrunOutput);
|
||||
QFETCH(WebBrowserEntries, expectedBrowsers);
|
||||
|
||||
QCOMPARE(WebBrowserSelectionAspect::parseEmrunOutput(emrunOutput), expectedBrowsers);
|
||||
}
|
||||
|
||||
void WebAssemblyTest::testEmrunBrowserListParsing_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("emrunOutput");
|
||||
QTest::addColumn<WebBrowserEntries>("expectedBrowsers");
|
||||
|
||||
QTest::newRow("emsdk 1.39.8")
|
||||
// Output of "emrun --list_browsers"
|
||||
<< QByteArray(
|
||||
R"(emrun has automatically found the following browsers in the default install locations on the system:
|
||||
|
||||
- firefox: Mozilla Firefox
|
||||
- chrome: Google Chrome
|
||||
|
||||
You can pass the --browser <id> option to launch with the given browser above.
|
||||
Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.
|
||||
|
||||
)")
|
||||
<< WebBrowserEntries({
|
||||
{QLatin1String("firefox"), QLatin1String("Mozilla Firefox")},
|
||||
{QLatin1String("chrome"), QLatin1String("Google Chrome")}});
|
||||
|
||||
QTest::newRow("emsdk 2.0.14")
|
||||
<< QByteArray(
|
||||
R"(emrun has automatically found the following browsers in the default install locations on the system:
|
||||
|
||||
- firefox: Mozilla Firefox 96.0.0.8041
|
||||
- chrome: Google Chrome 97.0.4692.71
|
||||
|
||||
You can pass the --browser <id> option to launch with the given browser above.
|
||||
Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.
|
||||
|
||||
)")
|
||||
<< WebBrowserEntries({
|
||||
{QLatin1String("firefox"), QLatin1String("Mozilla Firefox 96.0.0.8041")},
|
||||
{QLatin1String("chrome"), QLatin1String("Google Chrome 97.0.4692.71")}});
|
||||
}
|
||||
|
||||
} // namespace WebAssembly::Internal
|
||||
21
src/plugins/webassembly/webassembly_test.h
Normal file
21
src/plugins/webassembly/webassembly_test.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qobject.h>
|
||||
|
||||
namespace WebAssembly::Internal {
|
||||
|
||||
class WebAssemblyTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void testEmSdkEnvParsing();
|
||||
void testEmSdkEnvParsing_data();
|
||||
void testEmrunBrowserListParsing();
|
||||
void testEmrunBrowserListParsing_data();
|
||||
};
|
||||
|
||||
} // WebAssembly::Internal
|
||||
@@ -1,25 +1,21 @@
|
||||
// Copyright (C) 2020 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "webassemblyconstants.h"
|
||||
#include "webassemblyemsdk.h"
|
||||
|
||||
#include "webassemblyconstants.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QCache>
|
||||
#include <QSettings>
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
# include <QTest>
|
||||
# include "webassemblyplugin.h"
|
||||
#endif // WITH_TESTS
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace WebAssembly {
|
||||
namespace Internal {
|
||||
namespace WebAssembly::Internal::WebAssemblyEmSdk {
|
||||
|
||||
using EmSdkEnvCache = QCache<QString, QString>;
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(EmSdkEnvCache, emSdkEnvCache, (10))
|
||||
@@ -47,7 +43,7 @@ static QString emSdkEnvOutput(const FilePath &sdkRoot)
|
||||
return *emSdkEnvCache()->object(cacheKey);
|
||||
}
|
||||
|
||||
static void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Environment &env)
|
||||
void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Environment &env)
|
||||
{
|
||||
const QStringList lines = output.split('\n');
|
||||
|
||||
@@ -70,25 +66,25 @@ static void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Environment &e
|
||||
env.appendOrSetPath(FilePath::fromUserInput(emsdkPython).parentDir());
|
||||
}
|
||||
|
||||
bool WebAssemblyEmSdk::isValid(const FilePath &sdkRoot)
|
||||
bool isValid(const FilePath &sdkRoot)
|
||||
{
|
||||
return !version(sdkRoot).isNull();
|
||||
}
|
||||
|
||||
void WebAssemblyEmSdk::addToEnvironment(const FilePath &sdkRoot, Environment &env)
|
||||
void addToEnvironment(const FilePath &sdkRoot, Environment &env)
|
||||
{
|
||||
if (sdkRoot.exists())
|
||||
parseEmSdkEnvOutputAndAddToEnv(emSdkEnvOutput(sdkRoot), env);
|
||||
}
|
||||
|
||||
QVersionNumber WebAssemblyEmSdk::version(const FilePath &sdkRoot)
|
||||
QVersionNumber version(const FilePath &sdkRoot)
|
||||
{
|
||||
if (!sdkRoot.exists())
|
||||
return {};
|
||||
const QString cacheKey = sdkRoot.toString();
|
||||
if (!emSdkVersionCache()->contains(cacheKey)) {
|
||||
Environment env = sdkRoot.deviceEnvironment();
|
||||
WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
|
||||
addToEnvironment(sdkRoot, env);
|
||||
QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"};
|
||||
FilePath script = sdkRoot.withNewPath(scriptFile).searchInDirectories(env.path());
|
||||
const CommandLine command(script, {"-dumpversion"});
|
||||
@@ -103,14 +99,14 @@ QVersionNumber WebAssemblyEmSdk::version(const FilePath &sdkRoot)
|
||||
return *emSdkVersionCache()->object(cacheKey);
|
||||
}
|
||||
|
||||
void WebAssemblyEmSdk::registerEmSdk(const FilePath &sdkRoot)
|
||||
void registerEmSdk(const FilePath &sdkRoot)
|
||||
{
|
||||
QSettings *s = Core::ICore::settings();
|
||||
s->setValue(QLatin1String(Constants::SETTINGS_GROUP) + '/'
|
||||
+ QLatin1String(Constants::SETTINGS_KEY_EMSDK), sdkRoot.toString());
|
||||
}
|
||||
|
||||
FilePath WebAssemblyEmSdk::registeredEmSdk()
|
||||
FilePath registeredEmSdk()
|
||||
{
|
||||
QSettings *s = Core::ICore::settings();
|
||||
const QString path = s->value(QLatin1String(Constants::SETTINGS_GROUP) + '/'
|
||||
@@ -118,73 +114,10 @@ FilePath WebAssemblyEmSdk::registeredEmSdk()
|
||||
return FilePath::fromUserInput(path);
|
||||
}
|
||||
|
||||
void WebAssemblyEmSdk::clearCaches()
|
||||
void clearCaches()
|
||||
{
|
||||
emSdkEnvCache()->clear();
|
||||
emSdkVersionCache()->clear();
|
||||
}
|
||||
|
||||
// Unit tests:
|
||||
#ifdef WITH_TESTS
|
||||
void WebAssemblyPlugin::testEmSdkEnvParsing()
|
||||
{
|
||||
QFETCH(QString, emSdkEnvOutput);
|
||||
QFETCH(int, osType);
|
||||
QFETCH(int, pathCount);
|
||||
QFETCH(QString, emsdk);
|
||||
QFETCH(QString, em_config);
|
||||
|
||||
Environment env{OsType(osType)};
|
||||
parseEmSdkEnvOutputAndAddToEnv(emSdkEnvOutput, env);
|
||||
|
||||
QVERIFY(env.path().count() == pathCount);
|
||||
QCOMPARE(env.value("EMSDK"), emsdk);
|
||||
QCOMPARE(env.value("EM_CONFIG"), em_config);
|
||||
}
|
||||
|
||||
void WebAssemblyPlugin::testEmSdkEnvParsing_data()
|
||||
{
|
||||
// Output of "emsdk_env"
|
||||
QTest::addColumn<QString>("emSdkEnvOutput");
|
||||
QTest::addColumn<int>("osType");
|
||||
QTest::addColumn<int>("pathCount");
|
||||
QTest::addColumn<QString>("emsdk");
|
||||
QTest::addColumn<QString>("em_config");
|
||||
|
||||
QTest::newRow("windows") << R"(
|
||||
Adding directories to PATH:
|
||||
PATH += C:\Users\user\dev\emsdk
|
||||
PATH += C:\Users\user\dev\emsdk\upstream\emscripten
|
||||
PATH += C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin
|
||||
PATH += C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit
|
||||
PATH += C:\Users\user\dev\emsdk\java\8.152_64bit\bin
|
||||
|
||||
Setting environment variables:
|
||||
PATH = C:\Users\user\dev\emsdk;C:\Users\user\dev\emsdk\upstream\emscripten;C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin;C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit;C:\Users\user\dev\emsdk\java\8.152_64bit\bin;...other_stuff...
|
||||
EMSDK = C:/Users/user/dev/emsdk
|
||||
EM_CONFIG = C:\Users\user\dev\emsdk\.emscripten
|
||||
EM_CACHE = C:/Users/user/dev/emsdk/upstream/emscripten\cache
|
||||
EMSDK_NODE = C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin\node.exe
|
||||
EMSDK_PYTHON = C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit\python.exe
|
||||
JAVA_HOME = C:\Users\user\dev\emsdk\java\8.152_64bit
|
||||
)" << int(OsTypeWindows) << 6 << "C:/Users/user/dev/emsdk" << "C:\\Users\\user\\dev\\emsdk\\.emscripten";
|
||||
|
||||
QTest::newRow("linux") << R"(
|
||||
Adding directories to PATH:
|
||||
PATH += /home/user/dev/emsdk
|
||||
PATH += /home/user/dev/emsdk/upstream/emscripten
|
||||
PATH += /home/user/dev/emsdk/node/12.18.1_64bit/bin
|
||||
|
||||
Setting environment variables:
|
||||
PATH = /home/user/dev/emsdk:/home/user/dev/emsdk/upstream/emscripten:/home/user/dev/emsdk/node/12.18.1_64bit/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||
EMSDK = /home/user/dev/emsdk
|
||||
EM_CONFIG = /home/user/dev/emsdk/.emscripten
|
||||
EM_CACHE = /home/user/dev/emsdk/upstream/emscripten/cache
|
||||
EMSDK_NODE = /home/user/dev/emsdk/node/12.18.1_64bit/bin/node
|
||||
)" << int(OsTypeLinux) << 3 << "/home/user/dev/emsdk" << "/home/user/dev/emsdk/.emscripten";
|
||||
}
|
||||
|
||||
#endif // WITH_TESTS
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace WebAssembly
|
||||
} // namespace WebAssembly::Internal::WebAssemblyEmSdk
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QCache>
|
||||
#include <QVersionNumber>
|
||||
|
||||
namespace Utils {
|
||||
@@ -11,19 +10,14 @@ class Environment;
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace WebAssembly {
|
||||
namespace Internal {
|
||||
namespace WebAssembly::Internal::WebAssemblyEmSdk {
|
||||
|
||||
class WebAssemblyEmSdk
|
||||
{
|
||||
public:
|
||||
static bool isValid(const Utils::FilePath &sdkRoot);
|
||||
static void addToEnvironment(const Utils::FilePath &sdkRoot, Utils::Environment &env);
|
||||
static QVersionNumber version(const Utils::FilePath &sdkRoot);
|
||||
static void registerEmSdk(const Utils::FilePath &sdkRoot);
|
||||
static Utils::FilePath registeredEmSdk();
|
||||
static void clearCaches();
|
||||
};
|
||||
bool isValid(const Utils::FilePath &sdkRoot);
|
||||
void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Utils::Environment &env);
|
||||
void addToEnvironment(const Utils::FilePath &sdkRoot, Utils::Environment &env);
|
||||
QVersionNumber version(const Utils::FilePath &sdkRoot);
|
||||
void registerEmSdk(const Utils::FilePath &sdkRoot);
|
||||
Utils::FilePath registeredEmSdk();
|
||||
void clearCaches();
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace WebAssembly
|
||||
} // WebAssembly::Internal::WebAssemblyEmSdk
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// Copyright (C) 2020 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "webassemblyplugin.h"
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include "webassembly_test.h"
|
||||
#endif // WITH_TESTS
|
||||
#include "webassemblyconstants.h"
|
||||
#include "webassemblydevice.h"
|
||||
#include "webassemblyoptionspage.h"
|
||||
#include "webassemblyplugin.h"
|
||||
#include "webassemblyqtversion.h"
|
||||
#include "webassemblyrunconfiguration.h"
|
||||
#include "webassemblytoolchain.h"
|
||||
@@ -54,6 +58,10 @@ WebAssemblyPlugin::~WebAssemblyPlugin()
|
||||
void WebAssemblyPlugin::initialize()
|
||||
{
|
||||
dd = new WebAssemblyPluginPrivate;
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
addTest<WebAssemblyTest>();
|
||||
#endif // WITH_TESTS
|
||||
}
|
||||
|
||||
void WebAssemblyPlugin::extensionsInitialized()
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "webassembly_global.h"
|
||||
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
namespace WebAssembly::Internal {
|
||||
@@ -21,14 +19,6 @@ public:
|
||||
void initialize() override;
|
||||
void extensionsInitialized() override;
|
||||
static void askUserAboutEmSdkSetup();
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
private slots:
|
||||
void testEmSdkEnvParsing();
|
||||
void testEmSdkEnvParsing_data();
|
||||
void testEmrunBrowserListParsing();
|
||||
void testEmrunBrowserListParsing_data();
|
||||
#endif // WITH_TESTS
|
||||
};
|
||||
|
||||
} // WebAssembly::Internal
|
||||
|
||||
@@ -14,11 +14,6 @@
|
||||
#include <QComboBox>
|
||||
#include <QTextStream>
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
# include <QTest>
|
||||
# include "webassemblyplugin.h"
|
||||
#endif // WITH_TESTS
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace WebAssembly {
|
||||
@@ -26,20 +21,6 @@ namespace Internal {
|
||||
|
||||
static const char BROWSER_KEY[] = "WASM.WebBrowserSelectionAspect.Browser";
|
||||
|
||||
static WebBrowserEntries parseEmrunOutput(const QByteArray &output)
|
||||
{
|
||||
WebBrowserEntries result;
|
||||
QTextStream ts(output);
|
||||
QString line;
|
||||
const QRegularExpression regExp(" - (.*):(.*)");
|
||||
while (ts.readLineInto(&line)) {
|
||||
const QRegularExpressionMatch match = regExp.match(line);
|
||||
if (match.hasMatch())
|
||||
result.push_back({match.captured(1), match.captured(2).trimmed()});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static WebBrowserEntries emrunBrowsers(ProjectExplorer::Target *target)
|
||||
{
|
||||
WebBrowserEntries result;
|
||||
@@ -54,7 +35,8 @@ static WebBrowserEntries emrunBrowsers(ProjectExplorer::Target *target)
|
||||
browserLister.start();
|
||||
|
||||
if (browserLister.waitForFinished())
|
||||
result.append(parseEmrunOutput(browserLister.readAllRawStandardOutput()));
|
||||
result.append(WebBrowserSelectionAspect::parseEmrunOutput(
|
||||
browserLister.readAllRawStandardOutput()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -103,58 +85,20 @@ QString WebBrowserSelectionAspect::currentBrowser() const
|
||||
return m_currentBrowser;
|
||||
}
|
||||
|
||||
// Unit tests:
|
||||
#ifdef WITH_TESTS
|
||||
|
||||
void testEmrunBrowserListParsing();
|
||||
void testEmrunBrowserListParsing_data();
|
||||
|
||||
void WebAssemblyPlugin::testEmrunBrowserListParsing()
|
||||
WebBrowserEntries WebBrowserSelectionAspect::parseEmrunOutput(const QByteArray &output)
|
||||
{
|
||||
QFETCH(QByteArray, emrunOutput);
|
||||
QFETCH(WebBrowserEntries, expectedBrowsers);
|
||||
|
||||
QCOMPARE(parseEmrunOutput(emrunOutput), expectedBrowsers);
|
||||
WebBrowserEntries result;
|
||||
QTextStream ts(output);
|
||||
QString line;
|
||||
static const QRegularExpression regExp(R"( - (.*):\s*(.*))"); // ' - firefox: Mozilla Firefox'
|
||||
// ^__1__^ ^______2______^
|
||||
while (ts.readLineInto(&line)) {
|
||||
const QRegularExpressionMatch match = regExp.match(line);
|
||||
if (match.hasMatch())
|
||||
result.push_back({match.captured(1), match.captured(2)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void WebAssemblyPlugin::testEmrunBrowserListParsing_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("emrunOutput");
|
||||
QTest::addColumn<WebBrowserEntries>("expectedBrowsers");
|
||||
|
||||
QTest::newRow("emsdk 1.39.8")
|
||||
// Output of "emrun --list_browsers"
|
||||
<< QByteArray(
|
||||
R"(emrun has automatically found the following browsers in the default install locations on the system:
|
||||
|
||||
- firefox: Mozilla Firefox
|
||||
- chrome: Google Chrome
|
||||
|
||||
You can pass the --browser <id> option to launch with the given browser above.
|
||||
Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.
|
||||
|
||||
)")
|
||||
<< WebBrowserEntries({
|
||||
{QLatin1String("firefox"), QLatin1String("Mozilla Firefox")},
|
||||
{QLatin1String("chrome"), QLatin1String("Google Chrome")}});
|
||||
|
||||
QTest::newRow("emsdk 2.0.14")
|
||||
<< QByteArray(
|
||||
R"(emrun has automatically found the following browsers in the default install locations on the system:
|
||||
|
||||
- firefox: Mozilla Firefox 96.0.0.8041
|
||||
- chrome: Google Chrome 97.0.4692.71
|
||||
|
||||
You can pass the --browser <id> option to launch with the given browser above.
|
||||
Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.
|
||||
|
||||
)")
|
||||
<< WebBrowserEntries({
|
||||
{QLatin1String("firefox"), QLatin1String("Mozilla Firefox 96.0.0.8041")},
|
||||
{QLatin1String("chrome"), QLatin1String("Google Chrome 97.0.4692.71")}});
|
||||
}
|
||||
|
||||
#endif // WITH_TESTS
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Webassembly
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user