Merge remote-tracking branch 'origin/10.0'

Conflicts:
	src/plugins/clangcodemodel/clangcodemodelplugin.cpp

Change-Id: Idb3d6e8fdfd278979f6180dc3795a2138bc2e61d
This commit is contained in:
Eike Ziller
2023-02-16 12:27:28 +01:00
105 changed files with 2390 additions and 1290 deletions

236
dist/changelog/changes-10.0.0.md vendored Normal file
View 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

View File

@@ -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]
*/

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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}

View File

@@ -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.

View File

@@ -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

View File

@@ -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")

View File

@@ -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()

View File

@@ -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 &quot;%1&quot;.</source>
<translation>Nelze importovat styl kódování z &quot;%1&quot;.</translation>
</message>
<message>
<source>Cannot import code style</source>

View File

@@ -4456,8 +4456,8 @@ F.eks., vil &quot;Revision: 15&quot; 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 &quot;%1&quot;.</source>
<translation>Kan ikke importere kodestil fra &quot;%1&quot;.</translation>
</message>
<message>
<source>Export Code Style</source>

View File

@@ -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>&lt;b&gt;CMake configuration failed&lt;b&gt;&lt;p&gt;The backup of the previous configuration has been restored.&lt;/p&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p</source>
<source>&lt;b&gt;CMake configuration failed&lt;b&gt;&lt;p&gt;The backup of the previous configuration has been restored.&lt;/p&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p&gt;</source>
<translation>&lt;b&gt;CMake-Konfiguration ist fehlgeschlagen&lt;b/&gt;&lt;p&gt;Das Backup der vorherigen Konfiguration wurde wiederhergestellt.&lt;/p&gt;&lt;p&gt;Details zu dem Problem finden Sie in &quot;Probleme&quot; und den Einstellungen in &quot;Projekte &gt; Erstellen&quot;.&lt;/p&gt;</translation>
</message>
<message>
<source>&lt;b&gt;Failed to load project&lt;b&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p</source>
<source>&lt;b&gt;Failed to load project&lt;b&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p&gt;</source>
<translation>&lt;b&gt;Laden des Projekts fehlgeschlagen&lt;/b&gt;&lt;p&gt;Details zu dem Problem finden Sie in &quot;Probleme&quot; und den Einstellungen in &quot;Projekte &gt; Erstellen&quot;.&lt;/p&gt;</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 &quot;%1&quot; to &quot;%2&quot;.</source>
<translation>Umbenennen von &quot;%1&quot; nach &quot;%2&quot; 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 &quot;%1&quot; to &quot;%2&quot;.</source>
<translation>Kopieren von &quot;%1&quot; nach &quot;%2&quot; 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 &quot;%1&quot;.</source>
<translation>Die Datei &quot;%1&quot; konnte nicht gelesen werden.</translation>
</message>
<message>
<source>Invalid %1 file</source>
<translation>Ungültige Datei %1</translation>
<source>Invalid file &quot;%1&quot;.</source>
<translation>Ungültige Datei &quot;%1&quot;.</translation>
</message>
<message>
<source>Invalid &quot;version&quot; in %1 file</source>
<translation>Ungültige &quot;version&quot; in Datei %1</translation>
<source>Invalid &quot;version&quot; in file &quot;%1&quot;.</source>
<translation>Ungültige &quot;version&quot; in Datei &quot;%1&quot;.</translation>
</message>
<message>
<source>Invalid &quot;configurePresets&quot; section in %1 file</source>
@@ -7374,8 +7374,7 @@ Jetzt Commit ausführen?</translation>
<translation>Commit &quot;%1&quot; 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>&lt;a href=&quot;head&quot;&gt;Show HEAD&lt;/a&gt;</source>
<translation>&lt;a href=&quot;head&quot;&gt;HEAD anzeigen&lt;/a&gt;</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 &quot;%1&quot;.</source>
<translation>Der Coding-Stil kann nicht von &quot;%1&quot; 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>&amp;New</source>
<translation>&amp;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>&amp;Rename</source>
<translation>&amp;Umbenennen</translation>
</message>
<message>
<source>Rename and &amp;Open</source>
<translation>Umbenennen und ö&amp;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 &quot;%2&quot; 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>

View File

@@ -35420,8 +35420,8 @@ Pour compiler l&apos;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&apos;importer le style de code à partir de %1</translation>
<source>Cannot import code style from &quot;%1&quot;.</source>
<translation>Impossible d&apos;importer le style de code à partir de &quot;%1&quot;.</translation>
</message>
<message>
<source>Cannot import code style</source>

View File

@@ -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 &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -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 &quot;%1&quot;.</source>
<translation>&quot;%1&quot; </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>&amp;New</source>
<translation>(&amp;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>&amp;Rename</source>
<translation>(&amp;R)</translation>
</message>
<message>
<source>Rename and &amp;Open</source>
<translation>(&amp;O)</translation>
</message>
</context>
<context>
<name>ADS::WorkspaceView</name>
<message>
<source>Import Workspace</source>
<translation></translation>

View File

@@ -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 &quot;%1&quot;.</source>
<translation>Nie można zaimportować stylu kodu z &quot;%1&quot;.</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>

View File

@@ -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>&amp;Rename</source>
<translation>&amp;Переименовать</translation>
</message>
<message>
<source>Rename and &amp;Open</source>
<translation>П&amp;ереименовать и открыть</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, &quot;Revision: 15&quot; will leave the branch at revision 15.</sou
<translation>&lt;Заголовки&gt;</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 &quot;%1&quot;.</source>
<translation>Не удалось импортировать стиль кода из &quot;%1&quot;.</translation>
</message>
<message>
<source>Export Code Style</source>

View File

@@ -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 &quot;%1&quot;.</source>
<translation>Неможливо імпортувати стиль коду з &quot;%1&quot;.</translation>
</message>
<message>
<source>Export Code Style</source>

View File

@@ -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>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-workspaces.html&quot;&gt;What is a Workspace?&lt;/a&gt;</source>
<translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-workspaces.html&quot;&gt;什么是工作区?&lt;/a&gt;</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>&amp;Rename</source>
<translation>(&amp;R)</translation>
</message>
<message>
<source>Rename and &amp;Open</source>
<translation>(&amp;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>&lt;b&gt;CMake configuration failed&lt;b&gt;&lt;p&gt;The backup of the previous configuration has been restored.&lt;/p&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p</source>
<source>&lt;b&gt;CMake configuration failed&lt;b&gt;&lt;p&gt;The backup of the previous configuration has been restored.&lt;/p&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Failed to load project&lt;b&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p</source>
<source>&lt;b&gt;Failed to load project&lt;b&gt;&lt;p&gt;Issues and &quot;Projects &gt; Build&quot; settings show more information about the failure.&lt;/p&gt;</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 &quot;Build type&quot; fiel
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to rename %1 to %2.</source>
<source>Failed to rename &quot;%1&quot; to &quot;%2&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to copy %1 to %2.</source>
<source>Failed to copy &quot;%1&quot; to &quot;%2&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to read %1 file</source>
<source>Failed to read file &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid %1 file</source>
<source>Invalid file &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid &quot;version&quot; in %1 file</source>
<source>Invalid &quot;version&quot; in file &quot;%1&quot;.</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 &quot;Qt Debug Information Files&quot; 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>&lt;a href=&quot;head&quot;&gt;Show HEAD&lt;/a&gt;</source>
<source>Show HEAD</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -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 &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -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

View File

@@ -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());
}

View File

@@ -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")

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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() + ")");

View File

@@ -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()
*/

View File

@@ -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:

View File

@@ -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); }

View File

@@ -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;

View File

@@ -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.

View File

@@ -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()

View File

@@ -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

View File

@@ -111,7 +111,9 @@ Project {
"splashscreencontainerwidget.cpp",
"splashscreencontainerwidget.h",
"splashscreenwidget.cpp",
"splashscreenwidget.h"
"splashscreenwidget.h",
"sdkmanageroutputparser.cpp",
"sdkmanageroutputparser.h"
]
Group {

View File

@@ -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),

View 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;
}

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -16,7 +16,6 @@ class ClangFormatPlugin : public ExtensionSystem::IPlugin
~ClangFormatPlugin() override;
void initialize() final;
QVector<QObject *> createTestObjects() const override;
TextEditor::ICodeStylePreferencesFactory *m_factory = nullptr;
};

View File

@@ -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

View File

@@ -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

View File

@@ -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>"));
}
}
}

View File

@@ -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(),

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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()) {

View File

@@ -55,7 +55,6 @@ signals:
private:
void initialize() override;
void extensionsInitialized() override;
QVector<QObject *> createTestObjects() const override;
CppEditorPluginPrivate *d = nullptr;
};

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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");

View File

@@ -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);

View File

@@ -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());
});

View File

@@ -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();
});

View File

@@ -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());

View File

@@ -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)

View 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

View File

@@ -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);

View File

@@ -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()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -66,6 +66,7 @@ public:
QString statusText() const override;
bool writeToSettings() const override;
void readFromSettings() override;
QWidget *widget() override;
const McuPackageVersionDetector *getVersionDetector() const override;

View File

@@ -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);
}

View File

@@ -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, ());

View File

@@ -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);

View File

@@ -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] {

View File

@@ -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");

View File

@@ -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;

View File

@@ -50,7 +50,6 @@ public:
void initialize() override;
ShutdownFlag aboutToShutdown() override;
QVector<QObject *> createTestObjects() const override;
QString previewedFile() const;
void setPreviewedFile(const QString &previewedFile);

View File

@@ -14,7 +14,7 @@ struct QmlPreviewRunnerSetting
QmlPreviewFileLoader fileLoader;
QmlPreviewFileClassifier fileClassifier;
QmlPreviewFpsHandler fpsHandler;
float zoomFactor = 1.0;
float zoomFactor = -1.0;
QString language;
QmlDebugTranslationClientCreator createDebugTranslationClientMethod;
};

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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()));
}
}

View File

@@ -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);
});

View File

@@ -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
)

View File

@@ -32,4 +32,13 @@ QtcPlugin {
"webassemblytoolchain.cpp",
"webassemblytoolchain.h",
]
Group {
name: "Unit tests"
condition: qtc.testsEnabled
files: [
"webassembly_test.cpp",
"webassembly_test.h",
]
}
}

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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