summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/CMakeLists.txt21
-rw-r--r--src/tools/androiddeployqt/CMakeLists.txt14
-rw-r--r--src/tools/androiddeployqt/doc/src/androiddeployqt.qdoc95
-rw-r--r--src/tools/androiddeployqt/main.cpp2018
-rw-r--r--src/tools/androidtestrunner/CMakeLists.txt11
-rw-r--r--src/tools/androidtestrunner/main.cpp780
-rw-r--r--src/tools/bootstrap/CMakeLists.txt132
-rw-r--r--src/tools/cmake_automoc_parser/CMakeLists.txt10
-rw-r--r--src/tools/cmake_automoc_parser/main.cpp148
-rw-r--r--src/tools/configure.cmake35
-rw-r--r--src/tools/macdeployqt/CMakeLists.txt7
-rw-r--r--src/tools/macdeployqt/macdeployqt/CMakeLists.txt22
-rw-r--r--src/tools/macdeployqt/macdeployqt/main.cpp253
-rw-r--r--src/tools/macdeployqt/shared/shared.cpp1606
-rw-r--r--src/tools/macdeployqt/shared/shared.h119
-rw-r--r--src/tools/moc/CMakeLists.txt17
-rw-r--r--src/tools/moc/cbordevice.h31
-rw-r--r--src/tools/moc/collectjson.cpp31
-rw-r--r--src/tools/moc/collectjson.h29
-rw-r--r--src/tools/moc/generator.cpp767
-rw-r--r--src/tools/moc/generator.h36
-rw-r--r--src/tools/moc/keywords.cpp446
-rw-r--r--src/tools/moc/main.cpp109
-rw-r--r--src/tools/moc/moc.cpp605
-rw-r--r--src/tools/moc/moc.h77
-rw-r--r--src/tools/moc/outputrevision.h29
-rw-r--r--src/tools/moc/parser.cpp105
-rw-r--r--src/tools/moc/parser.h40
-rw-r--r--src/tools/moc/ppkeywords.cpp29
-rw-r--r--src/tools/moc/preprocessor.cpp176
-rw-r--r--src/tools/moc/preprocessor.h41
-rw-r--r--src/tools/moc/symbols.h139
-rw-r--r--src/tools/moc/token.cpp29
-rw-r--r--src/tools/moc/token.h32
-rwxr-xr-xsrc/tools/moc/util/generate.sh33
-rw-r--r--src/tools/moc/util/generate_keywords.cpp32
-rw-r--r--src/tools/moc/util/generate_keywords.pro5
-rw-r--r--src/tools/moc/util/licenseheader.cpp.in3
-rw-r--r--src/tools/moc/util/licenseheader.txt28
-rw-r--r--src/tools/moc/utils.h71
-rw-r--r--src/tools/qdbuscpp2xml/CMakeLists.txt27
-rw-r--r--src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp128
-rw-r--r--src/tools/qdbusxml2cpp/CMakeLists.txt27
-rw-r--r--src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp1037
-rw-r--r--src/tools/qlalr/CMakeLists.txt18
-rw-r--r--src/tools/qlalr/compress.cpp39
-rw-r--r--src/tools/qlalr/compress.h29
-rw-r--r--src/tools/qlalr/cppgenerator.cpp141
-rw-r--r--src/tools/qlalr/cppgenerator.h35
-rw-r--r--src/tools/qlalr/dotgraph.cpp29
-rw-r--r--src/tools/qlalr/dotgraph.h29
-rw-r--r--src/tools/qlalr/examples/dummy-xml/ll/dummy-xml-ll.cpp29
-rw-r--r--src/tools/qlalr/examples/dummy-xml/xml.g27
-rw-r--r--src/tools/qlalr/examples/glsl/build.sh29
-rw-r--r--src/tools/qlalr/examples/glsl/glsl-lex.l31
-rw-r--r--src/tools/qlalr/examples/glsl/glsl.g27
-rw-r--r--src/tools/qlalr/examples/lambda/lambda.g27
-rw-r--r--src/tools/qlalr/examples/lambda/main.cpp29
-rw-r--r--src/tools/qlalr/examples/qparser/calc.g27
-rw-r--r--src/tools/qlalr/examples/qparser/calc.l33
-rw-r--r--src/tools/qlalr/examples/qparser/qparser.cpp29
-rw-r--r--src/tools/qlalr/examples/qparser/qparser.h29
-rw-r--r--src/tools/qlalr/grammar.cpp29
-rw-r--r--src/tools/qlalr/grammar_p.h29
-rw-r--r--src/tools/qlalr/lalr.cpp65
-rw-r--r--src/tools/qlalr/lalr.g159
-rw-r--r--src/tools/qlalr/lalr.h44
-rw-r--r--src/tools/qlalr/main.cpp65
-rw-r--r--src/tools/qlalr/parsetable.cpp29
-rw-r--r--src/tools/qlalr/parsetable.h29
-rw-r--r--src/tools/qlalr/recognizer.cpp101
-rw-r--r--src/tools/qlalr/recognizer.h36
-rw-r--r--src/tools/qtpaths/CMakeLists.txt7
-rw-r--r--src/tools/qtpaths/qtpaths.cpp73
-rw-r--r--src/tools/qvkgen/CMakeLists.txt14
-rw-r--r--src/tools/qvkgen/qvkgen.cpp102
-rw-r--r--src/tools/rcc/CMakeLists.txt23
-rw-r--r--src/tools/rcc/main.cpp116
-rw-r--r--src/tools/rcc/rcc.cpp297
-rw-r--r--src/tools/rcc/rcc.h34
-rw-r--r--src/tools/shared/depfile_shared.h29
-rw-r--r--src/tools/shared/shellquote_shared.h82
-rw-r--r--src/tools/syncqt/CMakeLists.txt80
-rw-r--r--src/tools/syncqt/main.cpp1844
-rw-r--r--src/tools/tracegen/CMakeLists.txt13
-rw-r--r--src/tools/tracegen/ctf.cpp376
-rw-r--r--src/tools/tracegen/ctf.h12
-rw-r--r--src/tools/tracegen/etw.cpp155
-rw-r--r--src/tools/tracegen/etw.h40
-rw-r--r--src/tools/tracegen/helpers.cpp109
-rw-r--r--src/tools/tracegen/helpers.h60
-rw-r--r--src/tools/tracegen/lttng.cpp210
-rw-r--r--src/tools/tracegen/lttng.h40
-rw-r--r--src/tools/tracegen/panic.cpp53
-rw-r--r--src/tools/tracegen/panic.h41
-rw-r--r--src/tools/tracegen/provider.cpp326
-rw-r--r--src/tools/tracegen/provider.h85
-rw-r--r--src/tools/tracegen/qtheaders.cpp40
-rw-r--r--src/tools/tracegen/qtheaders.h40
-rw-r--r--src/tools/tracegen/tracegen.cpp55
-rw-r--r--src/tools/tracepointgen/CMakeLists.txt17
-rw-r--r--src/tools/tracepointgen/parser.cpp609
-rw-r--r--src/tools/tracepointgen/parser.h77
-rw-r--r--src/tools/tracepointgen/tracepointgen.cpp69
-rw-r--r--src/tools/tracepointgen/tracepointgen.h42
-rw-r--r--src/tools/uic/CMakeLists.txt17
-rw-r--r--src/tools/uic/cpp/cppwritedeclaration.cpp55
-rw-r--r--src/tools/uic/cpp/cppwritedeclaration.h29
-rw-r--r--src/tools/uic/cpp/cppwriteincludes.cpp53
-rw-r--r--src/tools/uic/cpp/cppwriteincludes.h29
-rw-r--r--src/tools/uic/cpp/cppwriteinitialization.cpp762
-rw-r--r--src/tools/uic/cpp/cppwriteinitialization.h34
-rw-r--r--src/tools/uic/customwidgetsinfo.cpp144
-rw-r--r--src/tools/uic/customwidgetsinfo.h39
-rw-r--r--src/tools/uic/databaseinfo.cpp35
-rw-r--r--src/tools/uic/databaseinfo.h29
-rw-r--r--src/tools/uic/driver.cpp57
-rw-r--r--src/tools/uic/driver.h29
-rw-r--r--src/tools/uic/main.cpp185
-rw-r--r--src/tools/uic/option.h50
-rw-r--r--src/tools/uic/python/pythonwritedeclaration.cpp33
-rw-r--r--src/tools/uic/python/pythonwritedeclaration.h29
-rw-r--r--src/tools/uic/python/pythonwriteimports.cpp147
-rw-r--r--src/tools/uic/python/pythonwriteimports.h33
-rw-r--r--src/tools/uic/qclass_lib_map.h2
-rw-r--r--src/tools/uic/shared/language.cpp254
-rw-r--r--src/tools/uic/shared/language.h45
-rw-r--r--src/tools/uic/shared/writeincludesbase.cpp33
-rw-r--r--src/tools/uic/shared/writeincludesbase.h29
-rw-r--r--src/tools/uic/treewalker.cpp29
-rw-r--r--src/tools/uic/treewalker.h29
-rw-r--r--src/tools/uic/ui4.cpp1292
-rw-r--r--src/tools/uic/ui4.h45
-rw-r--r--src/tools/uic/uic.cpp71
-rw-r--r--src/tools/uic/uic.h29
-rw-r--r--src/tools/uic/utils.h31
-rw-r--r--src/tools/uic/validator.cpp29
-rw-r--r--src/tools/uic/validator.h29
-rw-r--r--src/tools/windeployqt/CMakeLists.txt33
-rw-r--r--src/tools/windeployqt/main.cpp2007
-rw-r--r--src/tools/windeployqt/qmlutils.cpp138
-rw-r--r--src/tools/windeployqt/qmlutils.h40
-rw-r--r--src/tools/windeployqt/qtmoduleinfo.cpp183
-rw-r--r--src/tools/windeployqt/qtmoduleinfo.h51
-rw-r--r--src/tools/windeployqt/qtplugininfo.cpp100
-rw-r--r--src/tools/windeployqt/qtplugininfo.h48
-rw-r--r--src/tools/windeployqt/utils.cpp1022
-rw-r--r--src/tools/windeployqt/utils.h366
148 files changed, 16048 insertions, 7499 deletions
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 13a870ac9e..df0d2c7016 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -1,3 +1,11 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# The configure.cmake here does not get picked up automatically.
+# Manually evaluate tool-related features.
+include("${CMAKE_CURRENT_SOURCE_DIR}/configure.cmake")
+qt_feature_evaluate_features("${CMAKE_CURRENT_SOURCE_DIR}/configure.cmake")
+
add_subdirectory(uic)
if (QT_FEATURE_dbus)
add_subdirectory(qdbuscpp2xml)
@@ -9,10 +17,17 @@ if (QT_FEATURE_commandlineparser)
add_subdirectory(qtpaths)
endif()
-# Only include the following tools when performing a host build
-if(NOT CMAKE_CROSSCOMPILING)
+if(QT_FEATURE_androiddeployqt)
add_subdirectory(androiddeployqt)
- if(QT_FEATURE_gui AND QT_FEATURE_systemsemaphore)
+ if(QT_FEATURE_gui AND QT_FEATURE_process AND QT_FEATURE_systemsemaphore)
add_subdirectory(androidtestrunner)
endif()
endif()
+
+if(QT_FEATURE_macdeployqt)
+ add_subdirectory(macdeployqt)
+endif()
+
+if(QT_FEATURE_windeployqt)
+ add_subdirectory(windeployqt)
+endif()
diff --git a/src/tools/androiddeployqt/CMakeLists.txt b/src/tools/androiddeployqt/CMakeLists.txt
index 98cd79a334..041d883877 100644
--- a/src/tools/androiddeployqt/CMakeLists.txt
+++ b/src/tools/androiddeployqt/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from androiddeployqt.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## androiddeployqt App:
@@ -8,24 +9,25 @@ qt_get_tool_target_name(target_name androiddeployqt)
qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "Qt Android Deployment Tool"
TOOLS_TARGET Core
+ USER_FACING
+ INSTALL_VERSIONED_LINK
SOURCES
main.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_FOREACH
- PUBLIC_LIBRARIES
- Qt::Core # special case
+ QT_NO_QPAIR
+ LIBRARIES
+ Qt::Core
INCLUDE_DIRECTORIES
../shared
)
+qt_internal_return_unless_building_tools()
set_target_properties(${target_name} PROPERTIES
WIN32_EXECUTABLE FALSE
)
-#### Keys ignored in scope 1:.:.:androiddeployqt.pro:<TRUE>:
-# _OPTION = "host_build"
-
## Scopes:
#####################################################################
diff --git a/src/tools/androiddeployqt/doc/src/androiddeployqt.qdoc b/src/tools/androiddeployqt/doc/src/androiddeployqt.qdoc
new file mode 100644
index 0000000000..b94d7f5c04
--- /dev/null
+++ b/src/tools/androiddeployqt/doc/src/androiddeployqt.qdoc
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page android-deploy-qt-tool.html
+ \brief An overview of the androiddeployqt tool and how to use it.
+ \title The androiddeployqt Tool
+
+ \target androiddeployqt
+ Building an Android package involves many steps, so Qt comes with a tool which
+ handles the work for you. The steps handled by the androiddeployqt
+ tool are described in \l{Deploying an Application on Android}.
+
+ \section1 Prerequisites Before Running androiddeployqt
+
+ Before running the tool manually, you need to configure your project with
+ \c CMake or \c qmake to generate \c Makefiles and a \c JSON file (i.e.
+ \c{android-<target_name>-deployment-settings.json}) containing important settings
+ used by \c androiddeployqt.
+
+ \note It is not recommended to modify the androiddeployqt JSON file.
+
+ To prepare the environment for androiddeployqt, configure your project in
+ a separate directory than your source directory. For more information on
+ configuring your project, see \l {Building Qt for Android Projects from Command Line}.
+
+ \section1 Command Line Arguments
+
+ The only required command line arguments when running the tool are
+ \c {--input} and \c {--output}. Other command line arguments are optional but
+ useful. The list below is available by passing the \c {--help} argument to
+ androiddeployqt.
+
+ \quotefromfile main.cpp
+ \skipto Syntax: androiddeployqt --output <destination> [options]
+ \printuntil --help: Displays this information.
+
+ With a \c project_name, to build the application package with \c androiddeployqt
+ without deploying it the device, run the following:
+
+ \badcode
+ androiddeployqt --input <build_dir>/android-project_name-deployment-settings.json \
+ --output <build_dir>/android-build
+ \endcode
+
+ To build and deploy the package to the device:
+
+ \badcode
+ androiddeployqt --input <build_dir>/android-project_name-deployment-settings.json \
+ --output <build_dir>/android-build --install --device <device_serial_id>
+ \endcode
+
+ \section1 Dependencies Detection
+
+ Qt comes with a number of plugins which are loaded at run-time when they are
+ needed. These can handle anything from connecting to SQL databases to loading
+ specific image formats. Detecting plugin dependencies is impossible as the
+ plugins are loaded at run-time, but androiddeployqt tries to guess such
+ dependencies based on the Qt dependencies of your application. If the plugin
+ has any Qt dependencies which are not also dependencies of your application,
+ it will not be included by default. For instance, in order to ensure that
+ the SVG image format plugin is included, you will need to add \l {Qt SVG}
+ module to your project for it to become a dependency of your application:
+
+ \badcode
+ find_package(Qt6 REQUIRED COMPONENTS Svg)
+ ...
+ target_link_libraries(target_name PRIVATE Qt6::Svg)
+ \endcode
+
+ If you are wondering why a particular plugin is not included automatically,
+ you can run androiddeployqt with the \c{--verbose} option to get the list of
+ missing dependencies for each excluded plugin. You can achieve the same in
+ Qt Creator by ticking the \uicontrol {Verbose output} check box in the
+ \uicontrol {Projects} > \uicontrol {Build Steps} > \uicontrol {Build Android APK} >
+ \uicontrol {Advanced Actions}.
+
+ It's also possible to manually specify the dependencies of your application.
+ For more information, see \l {QT_ANDROID_DEPLOYMENT_DEPENDENCIES} CMake variable.
+
+ \note androiddeployqt scans the QML files of the project to collect the QML imports.
+ However, if you are loading QML code as a QString from C++ at runtime, that might
+ not work properly because androiddeployqt won't be aware of it at deploy time.
+ To remedy that, you can add a dummy QML file that imports such QML modules that
+ are referenced at runtime.
+
+ \section1 Deployment in Qt Creator
+
+ Qt Creator uses \c androiddeployqt under the hood, and provides easy
+ and intuitive user interfaces to specify various options. For more information,
+ see \l{Qt Creator: Deploying Applications to Android Devices}.
+
+ For more information about customizing and deploying a Qt for Android app,
+ see \l {Deploying an Application on Android}.
+*/
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp
index e0e1e2c8bc..6125b405b5 100644
--- a/src/tools/androiddeployqt/main.cpp
+++ b/src/tools/androiddeployqt/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QCoreApplication>
#include <QStringList>
@@ -36,13 +11,18 @@
#include <QDebug>
#include <QDataStream>
#include <QXmlStreamReader>
-#include <QDateTime>
#include <QStandardPaths>
#include <QUuid>
-#include <QDirIterator>
+#include <QDirListing>
+#include <QElapsedTimer>
#include <QRegularExpression>
+#include <QSettings>
+#include <QHash>
+#include <QSet>
+#include <QMap>
#include <depfile_shared.h>
+#include <shellquote_shared.h>
#include <algorithm>
@@ -58,46 +38,16 @@
#define QT_POPEN_READ "r"
#endif
-class ActionTimer
-{
- qint64 started;
-public:
- ActionTimer() = default;
- void start()
- {
- started = QDateTime::currentMSecsSinceEpoch();
- }
- int elapsed()
- {
- return int(QDateTime::currentMSecsSinceEpoch() - started);
- }
-};
+using namespace Qt::StringLiterals;
static const bool mustReadOutputAnyway = true; // pclose seems to return the wrong error code unless we read the output
static QStringList dependenciesForDepfile;
-void deleteRecursively(const QString &dirName)
-{
- QDir dir(dirName);
- if (!dir.exists())
- return;
-
- const QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
- for (const QFileInfo &entry : entries) {
- if (entry.isDir())
- deleteRecursively(entry.absoluteFilePath());
- else
- QFile::remove(entry.absoluteFilePath());
- }
-
- QDir().rmdir(dirName);
-}
-
FILE *openProcess(const QString &command)
{
#if defined(Q_OS_WIN32)
- QString processedCommand = QLatin1Char('\"') + command + QLatin1Char('\"');
+ QString processedCommand = u'\"' + command + u'\"';
#else
const QString& processedCommand = command;
#endif
@@ -118,6 +68,24 @@ struct QtDependency
QString absolutePath;
};
+struct QtInstallDirectoryWithTriple
+{
+ QtInstallDirectoryWithTriple(const QString &dir = QString(),
+ const QString &t = QString(),
+ const QHash<QString, QString> &dirs = QHash<QString, QString>()
+ ) :
+ qtInstallDirectory(dir),
+ qtDirectories(dirs),
+ triple(t),
+ enabled(false)
+ {}
+
+ QString qtInstallDirectory;
+ QHash<QString, QString> qtDirectories;
+ QString triple;
+ bool enabled;
+};
+
struct Options
{
Options()
@@ -128,12 +96,11 @@ struct Options
, auxMode(false)
, deploymentMechanism(Bundled)
, releasePackage(false)
- , digestAlg(QLatin1String("SHA-256"))
- , sigAlg(QLatin1String("SHA256withRSA"))
+ , digestAlg("SHA-256"_L1)
+ , sigAlg("SHA256withRSA"_L1)
, internalSf(false)
, sectionsOnly(false)
, protectedAuthenticationPath(false)
- , jarSigner(false)
, installApk(false)
, uninstallApk(false)
, qmlImportScannerBinaryPath()
@@ -141,7 +108,8 @@ struct Options
enum DeploymentMechanism
{
- Bundled
+ Bundled,
+ Unbundled
};
enum TriState {
@@ -155,17 +123,28 @@ struct Options
bool timing;
bool build;
bool auxMode;
- ActionTimer timer;
+ bool noRccBundleCleanup = false;
+ bool copyDependenciesOnly = false;
+ QElapsedTimer timer;
// External tools
QString sdkPath;
QString sdkBuildToolsVersion;
QString ndkPath;
+ QString ndkVersion;
QString jdkPath;
// Build paths
QString qtInstallDirectory;
+ QHash<QString, QString> qtDirectories;
+ QString qtDataDirectory;
+ QString qtLibsDirectory;
+ QString qtLibExecsDirectory;
+ QString qtPluginsDirectory;
+ QString qtQmlDirectory;
+ QString qtHostDirectory;
std::vector<QString> extraPrefixDirs;
+ QStringList androidDeployPlugins;
// Unlike 'extraPrefixDirs', the 'extraLibraryDirs' key doesn't expect the 'lib' subfolder
// when looking for dependencies.
std::vector<QString> extraLibraryDirs;
@@ -185,7 +164,7 @@ struct Options
QString versionName;
QString versionCode;
QByteArray minSdkVersion{"23"};
- QByteArray targetSdkVersion{"30"};
+ QByteArray targetSdkVersion{"34"};
// lib c++ path
QString stdCppPath;
@@ -193,7 +172,7 @@ struct Options
// Build information
QString androidPlatform;
- QHash<QString, QString> architectures;
+ QHash<QString, QtInstallDirectoryWithTriple> architectures;
QString currentArchitecture;
QString toolchainPrefix;
QString ndkHost;
@@ -203,6 +182,7 @@ struct Options
// Package information
DeploymentMechanism deploymentMechanism;
+ QString systemLibsPath;
QString packageName;
QStringList extraLibs;
QHash<QString, QStringList> archExtraLibs;
@@ -225,7 +205,6 @@ struct Options
bool internalSf;
bool sectionsOnly;
bool protectedAuthenticationPath;
- bool jarSigner;
QString apkPath;
// Installation information
@@ -234,11 +213,19 @@ struct Options
QString installLocation;
// Per architecture collected information
- void clear(const QString &arch)
+ void setCurrentQtArchitecture(const QString &arch,
+ const QString &directory,
+ const QHash<QString, QString> &directories)
{
currentArchitecture = arch;
- }
- typedef QPair<QString, QString> BundledFile;
+ qtInstallDirectory = directory;
+ qtDataDirectory = directories["qtDataDirectory"_L1];
+ qtLibsDirectory = directories["qtLibsDirectory"_L1];
+ qtLibExecsDirectory = directories["qtLibExecsDirectory"_L1];
+ qtPluginsDirectory = directories["qtPluginsDirectory"_L1];
+ qtQmlDirectory = directories["qtQmlDirectory"_L1];
+ }
+ using BundledFile = std::pair<QString, QString>;
QHash<QString, QList<BundledFile>> bundledFiles;
QHash<QString, QList<QtDependency>> qtDependencies;
QHash<QString, QStringList> localLibs;
@@ -251,6 +238,7 @@ struct Options
// Override qml import scanner path
QString qmlImportScannerBinaryPath;
+ bool qmlSkipImportScanning = false;
};
static const QHash<QByteArray, QByteArray> elfArchitectures = {
@@ -260,84 +248,52 @@ static const QHash<QByteArray, QByteArray> elfArchitectures = {
{"x86_64", "x86_64"}
};
-// Copy-pasted from qmake/library/ioutil.cpp
-inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
+bool goodToCopy(const Options *options, const QString &file, QStringList *unmetDependencies);
+bool checkCanImportFromRootPaths(const Options *options, const QString &absolutePath,
+ const QString &moduleUrl);
+bool readDependenciesFromElf(Options *options, const QString &fileName,
+ QSet<QString> *usedDependencies, QSet<QString> *remainingDependencies);
+
+QString architectureFromName(const QString &name)
{
- for (int x = arg.length() - 1; x >= 0; --x) {
- ushort c = arg.unicode()[x].unicode();
- if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
- return true;
- }
- return false;
+ QRegularExpression architecture(QStringLiteral("_(armeabi-v7a|arm64-v8a|x86|x86_64).so$"));
+ auto match = architecture.match(name);
+ if (!match.hasMatch())
+ return {};
+ return match.captured(1);
}
-static QString shellQuoteUnix(const QString &arg)
+static QString execSuffixAppended(QString path)
{
- // Chars that should be quoted (TM). This includes:
- static const uchar iqm[] = {
- 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
- 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
- }; // 0-32 \'"$`<>|;&(){}*?#!~[]
-
- if (!arg.length())
- return QLatin1String("\"\"");
-
- QString ret(arg);
- if (hasSpecialChars(ret, iqm)) {
- ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
- ret.prepend(QLatin1Char('\''));
- ret.append(QLatin1Char('\''));
- }
- return ret;
+#if defined(Q_OS_WIN32)
+ path += ".exe"_L1;
+#endif
+ return path;
}
-static QString shellQuoteWin(const QString &arg)
+static QString batSuffixAppended(QString path)
{
- // Chars that should be quoted (TM). This includes:
- // - control chars & space
- // - the shell meta chars "&()<>^|
- // - the potential separators ,;=
- static const uchar iqm[] = {
- 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
- 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
- };
-
- if (!arg.length())
- return QLatin1String("\"\"");
-
- QString ret(arg);
- if (hasSpecialChars(ret, iqm)) {
- // Quotes are escaped and their preceding backslashes are doubled.
- // It's impossible to escape anything inside a quoted string on cmd
- // level, so the outer quoting must be "suspended".
- ret.replace(QRegularExpression(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
- // The argument must not end with a \ since this would be interpreted
- // as escaping the quote -- rather put the \ behind the quote: e.g.
- // rather use "foo"\ than "foo\"
- int i = ret.length();
- while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
- --i;
- ret.insert(i, QLatin1Char('"'));
- ret.prepend(QLatin1Char('"'));
- }
- return ret;
+#if defined(Q_OS_WIN32)
+ path += ".bat"_L1;
+#endif
+ return path;
}
-static QString shellQuote(const QString &arg)
+QString defaultLibexecDir()
{
- if (QDir::separator() == QLatin1Char('\\'))
- return shellQuoteWin(arg);
- else
- return shellQuoteUnix(arg);
+#ifdef Q_OS_WIN32
+ return "bin"_L1;
+#else
+ return "libexec"_L1;
+#endif
}
-QString architectureFromName(const QString &name)
+static QString llvmReadobjPath(const Options &options)
{
- QRegularExpression architecture(QStringLiteral("_(armeabi-v7a|arm64-v8a|x86|x86_64).so$"));
- auto match = architecture.match(name);
- if (!match.hasMatch())
- return {};
- return match.captured(1);
+ return execSuffixAppended("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj"_L1
+ .arg(options.ndkPath,
+ options.toolchainPrefix,
+ options.ndkHost));
}
QString fileArchitecture(const Options &options, const QString &path)
@@ -346,19 +302,13 @@ QString fileArchitecture(const Options &options, const QString &path)
if (!arch.isEmpty())
return arch;
- QString readElf = QLatin1String("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj").arg(options.ndkPath,
- options.toolchainPrefix,
- options.ndkHost);
-#if defined(Q_OS_WIN32)
- readElf += QLatin1String(".exe");
-#endif
-
+ QString readElf = llvmReadobjPath(options);
if (!QFile::exists(readElf)) {
fprintf(stderr, "Command does not exist: %s\n", qPrintable(readElf));
return {};
}
- readElf = QLatin1String("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(path));
+ readElf = "%1 --needed-libs %2"_L1.arg(shellQuote(readElf), shellQuote(path));
FILE *readElfCommand = openProcess(readElf);
if (!readElfCommand) {
@@ -369,7 +319,6 @@ QString fileArchitecture(const Options &options, const QString &path)
char buffer[512];
while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) {
QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer));
- QString library;
line = line.trimmed();
if (line.startsWith("Arch: ")) {
auto it = elfArchitectures.find(line.mid(6));
@@ -398,7 +347,7 @@ void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &
for (const QFileInfo &src : srcEntries)
if (dst.fileName() == src.fileName()) {
if (dst.isDir())
- deleteMissingFiles(options, src.absoluteDir(), dst.absoluteDir());
+ deleteMissingFiles(options, src.absoluteFilePath(), dst.absoluteFilePath());
found = true;
break;
}
@@ -408,7 +357,7 @@ void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &
fprintf(stdout, "%s not found in %s, removing it.\n", qPrintable(dst.fileName()), qPrintable(srcDir.absolutePath()));
if (dst.isDir())
- deleteRecursively(dst.absolutePath());
+ QDir{dst.absolutePath()}.removeRecursively();
else
QFile::remove(dst.absoluteFilePath());
}
@@ -416,7 +365,6 @@ void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &
fflush(stdout);
}
-
Options parseOptions()
{
Options options;
@@ -424,94 +372,100 @@ Options parseOptions()
QStringList arguments = QCoreApplication::arguments();
for (int i=0; i<arguments.size(); ++i) {
const QString &argument = arguments.at(i);
- if (argument.compare(QLatin1String("--output"), Qt::CaseInsensitive) == 0) {
+ if (argument.compare("--output"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.outputDirectory = arguments.at(++i).trimmed();
- } else if (argument.compare(QLatin1String("--input"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--input"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.inputFileName = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--aab"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--aab"_L1, Qt::CaseInsensitive) == 0) {
options.buildAAB = true;
options.build = true;
- options.jarSigner = true;
- } else if (!options.buildAAB && argument.compare(QLatin1String("--no-build"), Qt::CaseInsensitive) == 0) {
+ } else if (!options.buildAAB && argument.compare("--no-build"_L1, Qt::CaseInsensitive) == 0) {
options.build = false;
- } else if (argument.compare(QLatin1String("--install"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--install"_L1, Qt::CaseInsensitive) == 0) {
options.installApk = true;
options.uninstallApk = true;
- } else if (argument.compare(QLatin1String("--reinstall"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--reinstall"_L1, Qt::CaseInsensitive) == 0) {
options.installApk = true;
options.uninstallApk = false;
- } else if (argument.compare(QLatin1String("--android-platform"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--android-platform"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.androidPlatform = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--help"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--help"_L1, Qt::CaseInsensitive) == 0) {
options.helpRequested = true;
- } else if (argument.compare(QLatin1String("--verbose"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--verbose"_L1, Qt::CaseInsensitive) == 0) {
options.verbose = true;
- } else if (argument.compare(QLatin1String("--deployment"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--deployment"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size()) {
options.helpRequested = true;
} else {
QString deploymentMechanism = arguments.at(++i);
- if (deploymentMechanism.compare(QLatin1String("bundled"), Qt::CaseInsensitive) == 0) {
+ if (deploymentMechanism.compare("bundled"_L1, Qt::CaseInsensitive) == 0) {
options.deploymentMechanism = Options::Bundled;
+ } else if (deploymentMechanism.compare("unbundled"_L1,
+ Qt::CaseInsensitive) == 0) {
+ options.deploymentMechanism = Options::Unbundled;
} else {
fprintf(stderr, "Unrecognized deployment mechanism: %s\n", qPrintable(deploymentMechanism));
options.helpRequested = true;
}
}
- } else if (argument.compare(QLatin1String("--device"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--device"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.installLocation = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--release"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--release"_L1, Qt::CaseInsensitive) == 0) {
options.releasePackage = true;
- } else if (argument.compare(QLatin1String("--jdk"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--jdk"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.jdkPath = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--apk"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--apk"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.apkPath = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--depfile"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--depfile"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.depFilePath = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--builddir"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--builddir"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.buildDirectory = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--sign"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--sign"_L1, Qt::CaseInsensitive) == 0) {
if (i + 2 >= arguments.size()) {
const QString keyStore = qEnvironmentVariable("QT_ANDROID_KEYSTORE_PATH");
const QString storeAlias = qEnvironmentVariable("QT_ANDROID_KEYSTORE_ALIAS");
if (keyStore.isEmpty() || storeAlias.isEmpty()) {
options.helpRequested = true;
+ fprintf(stderr, "Package signing path and alias values are not specified.\n");
} else {
fprintf(stdout,
"Using package signing path and alias values found from the "
"environment variables.\n");
- options.releasePackage = true;
options.keyStore = keyStore;
options.keyStoreAlias = storeAlias;
}
- } else {
- options.releasePackage = true;
+ } else if (!arguments.at(i + 1).startsWith("--"_L1) &&
+ !arguments.at(i + 2).startsWith("--"_L1)) {
options.keyStore = arguments.at(++i);
options.keyStoreAlias = arguments.at(++i);
+ } else {
+ options.helpRequested = true;
+ fprintf(stderr, "Package signing path and alias values are not "
+ "specified.\n");
}
// Do not override if the passwords are provided through arguments
@@ -525,58 +479,62 @@ Options parseOptions()
"variable.\n");
options.keyPass = qEnvironmentVariable("QT_ANDROID_KEYSTORE_KEY_PASS");
}
- } else if (argument.compare(QLatin1String("--storepass"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--storepass"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.keyStorePassword = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--storetype"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--storetype"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.storeType = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--keypass"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--keypass"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.keyPass = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--sigfile"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--sigfile"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.sigFile = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--digestalg"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--digestalg"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.digestAlg = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--sigalg"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--sigalg"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.sigAlg = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--tsa"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--tsa"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.tsaUrl = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--tsacert"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--tsacert"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.tsaCert = arguments.at(++i);
- } else if (argument.compare(QLatin1String("--internalsf"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--internalsf"_L1, Qt::CaseInsensitive) == 0) {
options.internalSf = true;
- } else if (argument.compare(QLatin1String("--sectionsonly"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--sectionsonly"_L1, Qt::CaseInsensitive) == 0) {
options.sectionsOnly = true;
- } else if (argument.compare(QLatin1String("--protected"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--protected"_L1, Qt::CaseInsensitive) == 0) {
options.protectedAuthenticationPath = true;
- } else if (argument.compare(QLatin1String("--jarsigner"), Qt::CaseInsensitive) == 0) {
- options.jarSigner = true;
- } else if (argument.compare(QLatin1String("--aux-mode"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--aux-mode"_L1, Qt::CaseInsensitive) == 0) {
options.auxMode = true;
- } else if (argument.compare(QLatin1String("--qml-importscanner-binary"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--qml-importscanner-binary"_L1, Qt::CaseInsensitive) == 0) {
options.qmlImportScannerBinaryPath = arguments.at(++i).trimmed();
+ } else if (argument.compare("--no-rcc-bundle-cleanup"_L1,
+ Qt::CaseInsensitive) == 0) {
+ options.noRccBundleCleanup = true;
+ } else if (argument.compare("--copy-dependencies-only"_L1,
+ Qt::CaseInsensitive) == 0) {
+ options.copyDependenciesOnly = true;
}
}
@@ -584,7 +542,7 @@ Options parseOptions()
options.helpRequested = true;
if (options.inputFileName.isEmpty())
- options.inputFileName = QLatin1String("android-%1-deployment-settings.json").arg(QDir::current().dirName());
+ options.inputFileName = "android-%1-deployment-settings.json"_L1.arg(QDir::current().dirName());
options.timing = qEnvironmentVariableIsSet("ANDROIDDEPLOYQT_TIMING_OUTPUT");
@@ -593,112 +551,121 @@ Options parseOptions()
options.outputDirectory.clear();
} else {
options.outputDirectory = QFileInfo(options.outputDirectory).canonicalFilePath();
- if (!options.outputDirectory.endsWith(QLatin1Char('/')))
- options.outputDirectory += QLatin1Char('/');
+ if (!options.outputDirectory.endsWith(u'/'))
+ options.outputDirectory += u'/';
}
return options;
}
void printHelp()
-{// "012345678901234567890123456789012345678901234567890123456789012345678901"
- fprintf(stderr, "Syntax: %s --output <destination> [options]\n"
- "\n"
- " Creates an Android package in the build directory <destination> and\n"
- " builds it into an .apk file.\n"
- "\n"
- " Optional arguments:\n"
- " --input <inputfile>: Reads <inputfile> for options generated by\n"
- " qmake. A default file name based on the current working\n"
- " directory will be used if nothing else is specified.\n"
- "\n"
- " --deployment <mechanism>: Supported deployment mechanisms:\n"
- " bundled (default): Include Qt files in stand-alone package.\n"
- "\n"
- " --aab: Build an Android App Bundle.\n"
- "\n"
- " --no-build: Do not build the package, it is useful to just install\n"
- " a package previously built.\n"
- "\n"
- " --install: Installs apk to device/emulator. By default this step is\n"
- " not taken. If the application has previously been installed on\n"
- " the device, it will be uninstalled first.\n"
- "\n"
- " --reinstall: Installs apk to device/emulator. By default this step\n"
- " is not taken. If the application has previously been installed on\n"
- " the device, it will be overwritten, but its data will be left\n"
- " intact.\n"
- "\n"
- " --device [device ID]: Use specified device for deployment. Default\n"
- " is the device selected by default by adb.\n"
- "\n"
- " --android-platform <platform>: Builds against the given android\n"
- " platform. By default, the highest available version will be\n"
- " used.\n"
- "\n"
- " --release: Builds a package ready for release. By default, the\n"
- " package will be signed with a debug key.\n"
- "\n"
- " --sign <url/to/keystore> <alias>: Signs the package with the\n"
- " specified keystore, alias and store password. Also implies the\n"
- " --release option.\n"
- " Optional arguments for use with signing:\n"
- " --storepass <password>: Keystore password.\n"
- " --storetype <type>: Keystore type.\n"
- " --keypass <password>: Password for private key (if different\n"
- " from keystore password.)\n"
- " --sigfile <file>: Name of .SF/.DSA file.\n"
- " --digestalg <name>: Name of digest algorithm. Default is\n"
- " \"SHA1\".\n"
- " --sigalg <name>: Name of signature algorithm. Default is\n"
- " \"SHA1withRSA\".\n"
- " --tsa <url>: Location of the Time Stamping Authority.\n"
- " --tsacert <alias>: Public key certificate for TSA.\n"
- " --internalsf: Include the .SF file inside the signature block.\n"
- " --sectionsonly: Don't compute hash of entire manifest.\n"
- " --protected: Keystore has protected authentication path.\n"
- " --jarsigner: Force jarsigner usage, otherwise apksigner will be\n"
- " used if available.\n"
- "\n"
- " NOTE: To conceal the keystore information, the environment variables\n"
- " QT_ANDROID_KEYSTORE_PATH, and QT_ANDROID_KEYSTORE_ALIAS are used to\n"
- " set the values keysotore and alias respectively.\n"
- " Also the environment variables QT_ANDROID_KEYSTORE_STORE_PASS,\n"
- " and QT_ANDROID_KEYSTORE_KEY_PASS are used to set the store and key\n"
- " passwords respectively. This option needs only the --sign parameter.\n"
- "\n"
- " --jdk <path/to/jdk>: Used to find the jarsigner tool when used\n"
- " in combination with the --release argument. By default,\n"
- " an attempt is made to detect the tool using the JAVA_HOME and\n"
- " PATH environment variables, in that order.\n"
- "\n"
- " --qml-import-paths: Specify additional search paths for QML\n"
- " imports.\n"
- "\n"
- " --verbose: Prints out information during processing.\n"
- "\n"
- " --no-generated-assets-cache: Do not pregenerate the entry list for\n"
- " the assets file engine.\n"
- "\n"
- " --aux-mode: Operate in auxiliary mode. This will only copy the\n"
- " dependencies into the build directory and update the XML templates.\n"
- " The project will not be built or installed.\n"
- "\n"
- " --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.\n"
- "\n"
- " --qml-importscanner-binary <path/to/qmlimportscanner>: Override the\n"
- " default qmlimportscanner binary path. By default the\n"
- " qmlimportscanner binary is located using the Qt directory\n"
- " specified in the input file.\n"
- "\n"
- " --depfile <path/to/depfile>: Output a dependency file.\n"
- "\n"
- " --builddir <path/to/build/directory>: build directory. Necessary when\n"
- " generating a depfile because ninja requires relative paths.\n"
- "\n"
- " --help: Displays this information.\n",
- qPrintable(QCoreApplication::arguments().at(0))
- );
+{
+ fprintf(stderr, R"(
+Syntax: androiddeployqt --output <destination> [options]
+
+Creates an Android package in the build directory <destination> and
+builds it into an .apk file.
+
+Optional arguments:
+ --input <inputfile>: Reads <inputfile> for options generated by
+ qmake. A default file name based on the current working
+ directory will be used if nothing else is specified.
+
+ --deployment <mechanism>: Supported deployment mechanisms:
+ bundled (default): Includes Qt files in stand-alone package.
+ unbundled: Assumes native libraries are present on the device
+ and does not include them in the APK.
+
+ --aab: Build an Android App Bundle.
+
+ --no-build: Do not build the package, it is useful to just install
+ a package previously built.
+
+ --install: Installs apk to device/emulator. By default this step is
+ not taken. If the application has previously been installed on
+ the device, it will be uninstalled first.
+
+ --reinstall: Installs apk to device/emulator. By default this step
+ is not taken. If the application has previously been installed on
+ the device, it will be overwritten, but its data will be left
+ intact.
+
+ --device [device ID]: Use specified device for deployment. Default
+ is the device selected by default by adb.
+
+ --android-platform <platform>: Builds against the given android
+ platform. By default, the highest available version will be
+ used.
+
+ --release: Builds a package ready for release. By default, the
+ package will be signed with a debug key.
+
+ --sign <url/to/keystore> <alias>: Signs the package with the
+ specified keystore, alias and store password.
+ Optional arguments for use with signing:
+ --storepass <password>: Keystore password.
+ --storetype <type>: Keystore type.
+ --keypass <password>: Password for private key (if different
+ from keystore password.)
+ --sigfile <file>: Name of .SF/.DSA file.
+ --digestalg <name>: Name of digest algorithm. Default is
+ "SHA-256".
+ --sigalg <name>: Name of signature algorithm. Default is
+ "SHA256withRSA".
+ --tsa <url>: Location of the Time Stamping Authority.
+ --tsacert <alias>: Public key certificate for TSA.
+ --internalsf: Include the .SF file inside the signature block.
+ --sectionsonly: Don't compute hash of entire manifest.
+ --protected: Keystore has protected authentication path.
+ --jarsigner: Deprecated, ignored.
+
+ NOTE: To conceal the keystore information, the environment variables
+ QT_ANDROID_KEYSTORE_PATH, and QT_ANDROID_KEYSTORE_ALIAS are used to
+ set the values keysotore and alias respectively.
+ Also the environment variables QT_ANDROID_KEYSTORE_STORE_PASS,
+ and QT_ANDROID_KEYSTORE_KEY_PASS are used to set the store and key
+ passwords respectively. This option needs only the --sign parameter.
+
+ --jdk <path/to/jdk>: Used to find the jarsigner tool when used
+ in combination with the --release argument. By default,
+ an attempt is made to detect the tool using the JAVA_HOME and
+ PATH environment variables, in that order.
+
+ --qml-import-paths: Specify additional search paths for QML
+ imports.
+
+ --verbose: Prints out information during processing.
+
+ --no-generated-assets-cache: Do not pregenerate the entry list for
+ the assets file engine.
+
+ --aux-mode: Operate in auxiliary mode. This will only copy the
+ dependencies into the build directory and update the XML templates.
+ The project will not be built or installed.
+
+ --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.
+
+ --qml-importscanner-binary <path/to/qmlimportscanner>: Override the
+ default qmlimportscanner binary path. By default the
+ qmlimportscanner binary is located using the Qt directory
+ specified in the input file.
+
+ --depfile <path/to/depfile>: Output a dependency file.
+
+ --builddir <path/to/build/directory>: build directory. Necessary when
+ generating a depfile because ninja requires relative paths.
+
+ --no-rcc-bundle-cleanup: skip cleaning rcc bundle directory after
+ running androiddeployqt. This option simplifies debugging of
+ the resource bundle content, but it should not be used when deploying
+ a project, since it litters the 'assets' directory.
+
+ --copy-dependencies-only: resolve application dependencies and stop
+ deploying process after all libraries and resources that the
+ application depends on have been copied.
+
+ --help: Displays this information.
+)");
}
// Since strings compared will all start with the same letters,
@@ -709,20 +676,20 @@ bool quasiLexicographicalReverseLessThan(const QFileInfo &fi1, const QFileInfo &
QString s1 = fi1.baseName();
QString s2 = fi2.baseName();
- if (s1.length() == s2.length())
+ if (s1.size() == s2.size())
return s1 > s2;
else
- return s1.length() > s2.length();
+ return s1.size() > s2.size();
}
// Files which contain templates that need to be overwritten by build data should be overwritten every
// time.
bool alwaysOverwritableFile(const QString &fileName)
{
- return (fileName.endsWith(QLatin1String("/res/values/libs.xml"))
- || fileName.endsWith(QLatin1String("/AndroidManifest.xml"))
- || fileName.endsWith(QLatin1String("/res/values/strings.xml"))
- || fileName.endsWith(QLatin1String("/src/org/qtproject/qt/android/bindings/QtActivity.java")));
+ return (fileName.endsWith("/res/values/libs.xml"_L1)
+ || fileName.endsWith("/AndroidManifest.xml"_L1)
+ || fileName.endsWith("/res/values/strings.xml"_L1)
+ || fileName.endsWith("/src/org/qtproject/qt/android/bindings/QtActivity.java"_L1));
}
@@ -776,48 +743,47 @@ QString cleanPackageName(QString packageName)
};
for (QChar &c : packageName) {
if (!isLegalChar(c))
- c = QLatin1Char('_');
+ c = u'_';
}
static QStringList keywords;
if (keywords.isEmpty()) {
- keywords << QLatin1String("abstract") << QLatin1String("continue") << QLatin1String("for")
- << QLatin1String("new") << QLatin1String("switch") << QLatin1String("assert")
- << QLatin1String("default") << QLatin1String("if") << QLatin1String("package")
- << QLatin1String("synchronized") << QLatin1String("boolean") << QLatin1String("do")
- << QLatin1String("goto") << QLatin1String("private") << QLatin1String("this")
- << QLatin1String("break") << QLatin1String("double") << QLatin1String("implements")
- << QLatin1String("protected") << QLatin1String("throw") << QLatin1String("byte")
- << QLatin1String("else") << QLatin1String("import") << QLatin1String("public")
- << QLatin1String("throws") << QLatin1String("case") << QLatin1String("enum")
- << QLatin1String("instanceof") << QLatin1String("return") << QLatin1String("transient")
- << QLatin1String("catch") << QLatin1String("extends") << QLatin1String("int")
- << QLatin1String("short") << QLatin1String("try") << QLatin1String("char")
- << QLatin1String("final") << QLatin1String("interface") << QLatin1String("static")
- << QLatin1String("void") << QLatin1String("class") << QLatin1String("finally")
- << QLatin1String("long") << QLatin1String("strictfp") << QLatin1String("volatile")
- << QLatin1String("const") << QLatin1String("float") << QLatin1String("native")
- << QLatin1String("super") << QLatin1String("while");
+ keywords << "abstract"_L1 << "continue"_L1 << "for"_L1
+ << "new"_L1 << "switch"_L1 << "assert"_L1
+ << "default"_L1 << "if"_L1 << "package"_L1
+ << "synchronized"_L1 << "boolean"_L1 << "do"_L1
+ << "goto"_L1 << "private"_L1 << "this"_L1
+ << "break"_L1 << "double"_L1 << "implements"_L1
+ << "protected"_L1 << "throw"_L1 << "byte"_L1
+ << "else"_L1 << "import"_L1 << "public"_L1
+ << "throws"_L1 << "case"_L1 << "enum"_L1
+ << "instanceof"_L1 << "return"_L1 << "transient"_L1
+ << "catch"_L1 << "extends"_L1 << "int"_L1
+ << "short"_L1 << "try"_L1 << "char"_L1
+ << "final"_L1 << "interface"_L1 << "static"_L1
+ << "void"_L1 << "class"_L1 << "finally"_L1
+ << "long"_L1 << "strictfp"_L1 << "volatile"_L1
+ << "const"_L1 << "float"_L1 << "native"_L1
+ << "super"_L1 << "while"_L1;
}
// No keywords
- int index = -1;
- while (index < packageName.length()) {
- int next = packageName.indexOf(QLatin1Char('.'), index + 1);
+ qsizetype index = -1;
+ while (index < packageName.size()) {
+ qsizetype next = packageName.indexOf(u'.', index + 1);
if (next == -1)
- next = packageName.length();
+ next = packageName.size();
QString word = packageName.mid(index + 1, next - index - 1);
if (!word.isEmpty()) {
QChar c = word[0];
- if ((c >= QChar(QLatin1Char('0')) && c<= QChar(QLatin1Char('9')))
- || c == QLatin1Char('_')) {
- packageName.insert(index + 1, QLatin1Char('a'));
+ if ((c >= u'0' && c <= u'9') || c == u'_') {
+ packageName.insert(index + 1, u'a');
index = next + 1;
continue;
}
}
if (keywords.contains(word)) {
- packageName.insert(next, QLatin1String("_"));
+ packageName.insert(next, "_"_L1);
index = next + 1;
} else {
index = next;
@@ -829,7 +795,7 @@ QString cleanPackageName(QString packageName)
QString detectLatestAndroidPlatform(const QString &sdkPath)
{
- QDir dir(sdkPath + QLatin1String("/platforms"));
+ QDir dir(sdkPath + "/platforms"_L1);
if (!dir.exists()) {
fprintf(stderr, "Directory %s does not exist\n", qPrintable(dir.absolutePath()));
return QString();
@@ -843,7 +809,7 @@ QString detectLatestAndroidPlatform(const QString &sdkPath)
std::sort(fileInfos.begin(), fileInfos.end(), quasiLexicographicalReverseLessThan);
- QFileInfo latestPlatform = fileInfos.first();
+ const QFileInfo& latestPlatform = fileInfos.constFirst();
return latestPlatform.baseName();
}
@@ -854,14 +820,83 @@ QString packageNameFromAndroidManifest(const QString &androidManifestPath)
QXmlStreamReader reader(&androidManifestXml);
while (!reader.atEnd()) {
reader.readNext();
- if (reader.isStartElement() && reader.name() == QLatin1String("manifest"))
- return cleanPackageName(
- reader.attributes().value(QLatin1String("package")).toString());
+ if (reader.isStartElement() && reader.name() == "manifest"_L1)
+ return cleanPackageName(reader.attributes().value("package"_L1).toString());
}
}
return {};
}
+bool parseCmakeBoolean(const QJsonValue &value)
+{
+ const QString stringValue = value.toString();
+ return (stringValue.compare(QString::fromUtf8("true"), Qt::CaseInsensitive)
+ || stringValue.compare(QString::fromUtf8("on"), Qt::CaseInsensitive)
+ || stringValue.compare(QString::fromUtf8("yes"), Qt::CaseInsensitive)
+ || stringValue.compare(QString::fromUtf8("y"), Qt::CaseInsensitive)
+ || stringValue.toInt() > 0);
+}
+
+bool readInputFileDirectory(Options *options, QJsonObject &jsonObject, const QString keyName)
+{
+ const QJsonValue qtDirectory = jsonObject.value(keyName);
+ if (qtDirectory.isUndefined()) {
+ for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) {
+ if (keyName == "qtDataDirectory"_L1) {
+ options->architectures[it.key()].qtDirectories[keyName] = "."_L1;
+ break;
+ } else if (keyName == "qtLibsDirectory"_L1) {
+ options->architectures[it.key()].qtDirectories[keyName] = "lib"_L1;
+ break;
+ } else if (keyName == "qtLibExecsDirectory"_L1) {
+ options->architectures[it.key()].qtDirectories[keyName] = defaultLibexecDir();
+ break;
+ } else if (keyName == "qtPluginsDirectory"_L1) {
+ options->architectures[it.key()].qtDirectories[keyName] = "plugins"_L1;
+ break;
+ } else if (keyName == "qtQmlDirectory"_L1) {
+ options->architectures[it.key()].qtDirectories[keyName] = "qml"_L1;
+ break;
+ }
+ }
+ return true;
+ }
+
+ if (qtDirectory.isObject()) {
+ const QJsonObject object = qtDirectory.toObject();
+ for (auto it = object.constBegin(); it != object.constEnd(); ++it) {
+ if (it.value().isUndefined()) {
+ fprintf(stderr,
+ "Invalid '%s' record in deployment settings: %s\n",
+ qPrintable(keyName),
+ qPrintable(it.value().toString()));
+ return false;
+ }
+ if (it.value().isNull())
+ continue;
+ if (!options->architectures.contains(it.key())) {
+ fprintf(stderr, "Architecture %s unknown (%s).", qPrintable(it.key()),
+ qPrintable(options->architectures.keys().join(u',')));
+ return false;
+ }
+ options->architectures[it.key()].qtDirectories[keyName] = it.value().toString();
+ }
+ } else if (qtDirectory.isString()) {
+ // Format for Qt < 6 or when using the tool with Qt >= 6 but in single arch.
+ // We assume Qt > 5.14 where all architectures are in the same directory.
+ const QString directory = qtDirectory.toString();
+ options->architectures["arm64-v8a"_L1].qtDirectories[keyName] = directory;
+ options->architectures["armeabi-v7a"_L1].qtDirectories[keyName] = directory;
+ options->architectures["x86"_L1].qtDirectories[keyName] = directory;
+ options->architectures["x86_64"_L1].qtDirectories[keyName] = directory;
+ } else {
+ fprintf(stderr, "Invalid format for %s in json file %s.\n",
+ qPrintable(keyName), qPrintable(options->inputFileName));
+ return false;
+ }
+ return true;
+}
+
bool readInputFile(Options *options)
{
QFile file(options->inputFileName);
@@ -880,7 +915,7 @@ bool readInputFile(Options *options)
QJsonObject jsonObject = jsonDocument.object();
{
- QJsonValue sdkPath = jsonObject.value(QLatin1String("sdk"));
+ QJsonValue sdkPath = jsonObject.value("sdk"_L1);
if (sdkPath.isUndefined()) {
fprintf(stderr, "No SDK path in json file %s\n", qPrintable(options->inputFileName));
return false;
@@ -893,7 +928,7 @@ bool readInputFile(Options *options)
if (options->androidPlatform.isEmpty())
return false;
} else {
- if (!QDir(options->sdkPath + QLatin1String("/platforms/") + options->androidPlatform).exists()) {
+ if (!QDir(options->sdkPath + "/platforms/"_L1 + options->androidPlatform).exists()) {
fprintf(stderr, "Warning: Android platform '%s' does not exist in SDK.\n",
qPrintable(options->androidPlatform));
}
@@ -902,22 +937,74 @@ bool readInputFile(Options *options)
{
- const QJsonValue value = jsonObject.value(QLatin1String("sdkBuildToolsRevision"));
+ const QJsonValue value = jsonObject.value("sdkBuildToolsRevision"_L1);
if (!value.isUndefined())
options->sdkBuildToolsVersion = value.toString();
}
{
- const QJsonValue qtInstallDirectory = jsonObject.value(QLatin1String("qt"));
+ const QJsonValue qtInstallDirectory = jsonObject.value("qt"_L1);
if (qtInstallDirectory.isUndefined()) {
fprintf(stderr, "No Qt directory in json file %s\n", qPrintable(options->inputFileName));
return false;
}
- options->qtInstallDirectory = qtInstallDirectory.toString();
+
+ if (qtInstallDirectory.isObject()) {
+ const QJsonObject object = qtInstallDirectory.toObject();
+ for (auto it = object.constBegin(); it != object.constEnd(); ++it) {
+ if (it.value().isUndefined()) {
+ fprintf(stderr,
+ "Invalid 'qt' record in deployment settings: %s\n",
+ qPrintable(it.value().toString()));
+ return false;
+ }
+ if (it.value().isNull())
+ continue;
+ options->architectures.insert(it.key(),
+ QtInstallDirectoryWithTriple(it.value().toString()));
+ }
+ } else if (qtInstallDirectory.isString()) {
+ // Format for Qt < 6 or when using the tool with Qt >= 6 but in single arch.
+ // We assume Qt > 5.14 where all architectures are in the same directory.
+ const QString directory = qtInstallDirectory.toString();
+ QtInstallDirectoryWithTriple qtInstallDirectoryWithTriple(directory);
+ options->architectures.insert("arm64-v8a"_L1, qtInstallDirectoryWithTriple);
+ options->architectures.insert("armeabi-v7a"_L1, qtInstallDirectoryWithTriple);
+ options->architectures.insert("x86"_L1, qtInstallDirectoryWithTriple);
+ options->architectures.insert("x86_64"_L1, qtInstallDirectoryWithTriple);
+ // In Qt < 6 rcc and qmlimportscanner are installed in the host and install directories
+ // In Qt >= 6 rcc and qmlimportscanner are only installed in the host directory
+ // So setting the "qtHostDir" is not necessary with Qt < 6.
+ options->qtHostDirectory = directory;
+ } else {
+ fprintf(stderr, "Invalid format for Qt install prefixes in json file %s.\n",
+ qPrintable(options->inputFileName));
+ return false;
+ }
+ }
+
+ if (!readInputFileDirectory(options, jsonObject, "qtDataDirectory"_L1) ||
+ !readInputFileDirectory(options, jsonObject, "qtLibsDirectory"_L1) ||
+ !readInputFileDirectory(options, jsonObject, "qtLibExecsDirectory"_L1) ||
+ !readInputFileDirectory(options, jsonObject, "qtPluginsDirectory"_L1) ||
+ !readInputFileDirectory(options, jsonObject, "qtQmlDirectory"_L1))
+ return false;
+
+ {
+ const QJsonValue qtHostDirectory = jsonObject.value("qtHostDir"_L1);
+ if (!qtHostDirectory.isUndefined()) {
+ if (qtHostDirectory.isString()) {
+ options->qtHostDirectory = qtHostDirectory.toString();
+ } else {
+ fprintf(stderr, "Invalid format for Qt host directory in json file %s.\n",
+ qPrintable(options->inputFileName));
+ return false;
+ }
+ }
}
{
- const auto extraPrefixDirs = jsonObject.value(QLatin1String("extraPrefixDirs")).toArray();
+ const auto extraPrefixDirs = jsonObject.value("extraPrefixDirs"_L1).toArray();
options->extraPrefixDirs.reserve(extraPrefixDirs.size());
for (const QJsonValue prefix : extraPrefixDirs) {
options->extraPrefixDirs.push_back(prefix.toString());
@@ -925,7 +1012,12 @@ bool readInputFile(Options *options)
}
{
- const auto extraLibraryDirs = jsonObject.value(QLatin1String("extraLibraryDirs")).toArray();
+ const auto androidDeployPlugins = jsonObject.value("android-deploy-plugins"_L1).toString();
+ options->androidDeployPlugins = androidDeployPlugins.split(";"_L1, Qt::SkipEmptyParts);
+ }
+
+ {
+ const auto extraLibraryDirs = jsonObject.value("extraLibraryDirs"_L1).toArray();
options->extraLibraryDirs.reserve(extraLibraryDirs.size());
for (const QJsonValue path : extraLibraryDirs) {
options->extraLibraryDirs.push_back(path.toString());
@@ -933,13 +1025,13 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue androidSourcesDirectory = jsonObject.value(QLatin1String("android-package-source-directory"));
+ const QJsonValue androidSourcesDirectory = jsonObject.value("android-package-source-directory"_L1);
if (!androidSourcesDirectory.isUndefined())
options->androidSourceDirectory = androidSourcesDirectory.toString();
}
{
- const QJsonValue applicationArguments = jsonObject.value(QLatin1String("android-application-arguments"));
+ const QJsonValue applicationArguments = jsonObject.value("android-application-arguments"_L1);
if (!applicationArguments.isUndefined())
options->applicationArguments = applicationArguments.toString();
else
@@ -947,7 +1039,7 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue androidVersionName = jsonObject.value(QLatin1String("android-version-name"));
+ const QJsonValue androidVersionName = jsonObject.value("android-version-name"_L1);
if (!androidVersionName.isUndefined())
options->versionName = androidVersionName.toString();
else
@@ -955,7 +1047,7 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue androidVersionCode = jsonObject.value(QLatin1String("android-version-code"));
+ const QJsonValue androidVersionCode = jsonObject.value("android-version-code"_L1);
if (!androidVersionCode.isUndefined())
options->versionCode = androidVersionCode.toString();
else
@@ -963,19 +1055,19 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue ver = jsonObject.value(QLatin1String("android-min-sdk-version"));
+ const QJsonValue ver = jsonObject.value("android-min-sdk-version"_L1);
if (!ver.isUndefined())
options->minSdkVersion = ver.toString().toUtf8();
}
{
- const QJsonValue ver = jsonObject.value(QLatin1String("android-target-sdk-version"));
+ const QJsonValue ver = jsonObject.value("android-target-sdk-version"_L1);
if (!ver.isUndefined())
options->targetSdkVersion = ver.toString().toUtf8();
}
{
- const QJsonObject targetArchitectures = jsonObject.value(QLatin1String("architectures")).toObject();
+ const QJsonObject targetArchitectures = jsonObject.value("architectures"_L1).toObject();
if (targetArchitectures.isEmpty()) {
fprintf(stderr, "No target architecture defined in json file.\n");
return false;
@@ -987,21 +1079,36 @@ bool readInputFile(Options *options)
}
if (it.value().isNull())
continue;
- options->architectures.insert(it.key(), it.value().toString());
+ if (!options->architectures.contains(it.key())) {
+ fprintf(stderr, "Architecture %s unknown (%s).", qPrintable(it.key()),
+ qPrintable(options->architectures.keys().join(u',')));
+ return false;
+ }
+ options->architectures[it.key()].triple = it.value().toString();
+ options->architectures[it.key()].enabled = true;
}
}
{
- const QJsonValue ndk = jsonObject.value(QLatin1String("ndk"));
+ const QJsonValue ndk = jsonObject.value("ndk"_L1);
if (ndk.isUndefined()) {
fprintf(stderr, "No NDK path defined in json file.\n");
return false;
}
options->ndkPath = ndk.toString();
+ const QString ndkPropertiesPath = options->ndkPath + QStringLiteral("/source.properties");
+ const QSettings settings(ndkPropertiesPath, QSettings::IniFormat);
+ const QString ndkVersion = settings.value(QStringLiteral("Pkg.Revision")).toString();
+ if (ndkVersion.isEmpty()) {
+ fprintf(stderr, "Couldn't retrieve the NDK version from \"%s\".\n",
+ qPrintable(ndkPropertiesPath));
+ return false;
+ }
+ options->ndkVersion = ndkVersion;
}
{
- const QJsonValue toolchainPrefix = jsonObject.value(QLatin1String("toolchain-prefix"));
+ const QJsonValue toolchainPrefix = jsonObject.value("toolchain-prefix"_L1);
if (toolchainPrefix.isUndefined()) {
fprintf(stderr, "No toolchain prefix defined in json file.\n");
return false;
@@ -1010,7 +1117,7 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue ndkHost = jsonObject.value(QLatin1String("ndk-host"));
+ const QJsonValue ndkHost = jsonObject.value("ndk-host"_L1);
if (ndkHost.isUndefined()) {
fprintf(stderr, "No NDK host defined in json file.\n");
return false;
@@ -1019,19 +1126,41 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue extraLibs = jsonObject.value(QLatin1String("android-extra-libs"));
+ const QJsonValue extraLibs = jsonObject.value("android-extra-libs"_L1);
if (!extraLibs.isUndefined())
- options->extraLibs = extraLibs.toString().split(QLatin1Char(','), Qt::SkipEmptyParts);
+ options->extraLibs = extraLibs.toString().split(u',', Qt::SkipEmptyParts);
+ }
+
+ {
+ const QJsonValue qmlSkipImportScanning = jsonObject.value("qml-skip-import-scanning"_L1);
+ if (!qmlSkipImportScanning.isUndefined())
+ options->qmlSkipImportScanning = qmlSkipImportScanning.toBool();
}
{
- const QJsonValue extraPlugins = jsonObject.value(QLatin1String("android-extra-plugins"));
+ const QJsonValue extraPlugins = jsonObject.value("android-extra-plugins"_L1);
if (!extraPlugins.isUndefined())
- options->extraPlugins = extraPlugins.toString().split(QLatin1Char(','));
+ options->extraPlugins = extraPlugins.toString().split(u',');
}
{
- const QJsonValue stdcppPath = jsonObject.value(QLatin1String("stdcpp-path"));
+ const QJsonValue systemLibsPath =
+ jsonObject.value("android-system-libs-prefix"_L1);
+ if (!systemLibsPath.isUndefined())
+ options->systemLibsPath = systemLibsPath.toString();
+ }
+
+ {
+ const QJsonValue noDeploy = jsonObject.value("android-no-deploy-qt-libs"_L1);
+ if (!noDeploy.isUndefined()) {
+ bool useUnbundled = parseCmakeBoolean(noDeploy);
+ options->deploymentMechanism = useUnbundled ? Options::Unbundled :
+ Options::Bundled;
+ }
+ }
+
+ {
+ const QJsonValue stdcppPath = jsonObject.value("stdcpp-path"_L1);
if (stdcppPath.isUndefined()) {
fprintf(stderr, "No stdcpp-path defined in json file.\n");
return false;
@@ -1040,7 +1169,7 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue qmlRootPath = jsonObject.value(QLatin1String("qml-root-path"));
+ const QJsonValue qmlRootPath = jsonObject.value("qml-root-path"_L1);
if (qmlRootPath.isString()) {
options->rootPaths.push_back(qmlRootPath.toString());
} else if (qmlRootPath.isArray()) {
@@ -1050,30 +1179,30 @@ bool readInputFile(Options *options)
options->rootPaths.push_back(path.toString());
}
} else {
- options->rootPaths.push_back(options->inputFileName);
+ options->rootPaths.push_back(QFileInfo(options->inputFileName).absolutePath());
}
}
{
- const QJsonValue qmlImportPaths = jsonObject.value(QLatin1String("qml-import-paths"));
+ const QJsonValue qmlImportPaths = jsonObject.value("qml-import-paths"_L1);
if (!qmlImportPaths.isUndefined())
- options->qmlImportPaths = qmlImportPaths.toString().split(QLatin1Char(','));
+ options->qmlImportPaths = qmlImportPaths.toString().split(u',');
}
{
- const QJsonValue qmlImportScannerBinaryPath = jsonObject.value(QLatin1String("qml-importscanner-binary"));
+ const QJsonValue qmlImportScannerBinaryPath = jsonObject.value("qml-importscanner-binary"_L1);
if (!qmlImportScannerBinaryPath.isUndefined())
options->qmlImportScannerBinaryPath = qmlImportScannerBinaryPath.toString();
}
{
- const QJsonValue rccBinaryPath = jsonObject.value(QLatin1String("rcc-binary"));
+ const QJsonValue rccBinaryPath = jsonObject.value("rcc-binary"_L1);
if (!rccBinaryPath.isUndefined())
options->rccBinaryPath = rccBinaryPath.toString();
}
{
- const QJsonValue applicationBinary = jsonObject.value(QLatin1String("application-binary"));
+ const QJsonValue applicationBinary = jsonObject.value("application-binary"_L1);
if (applicationBinary.isUndefined()) {
fprintf(stderr, "No application binary defined in json file.\n");
return false;
@@ -1081,7 +1210,9 @@ bool readInputFile(Options *options)
options->applicationBinary = applicationBinary.toString();
if (options->build) {
for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) {
- auto appBinaryPath = QLatin1String("%1/libs/%2/lib%3_%2.so").arg(options->outputDirectory, it.key(), options->applicationBinary);
+ if (!it->enabled)
+ continue;
+ auto appBinaryPath = "%1/libs/%2/lib%3_%2.so"_L1.arg(options->outputDirectory, it.key(), options->applicationBinary);
if (!QFile::exists(appBinaryPath)) {
fprintf(stderr, "Cannot find application binary in build dir %s.\n", qPrintable(appBinaryPath));
return false;
@@ -1091,21 +1222,21 @@ bool readInputFile(Options *options)
}
{
- const QJsonValue deploymentDependencies = jsonObject.value(QLatin1String("deployment-dependencies"));
+ using ItFlag = QDirListing::IteratorFlag;
+ const QJsonValue deploymentDependencies = jsonObject.value("deployment-dependencies"_L1);
if (!deploymentDependencies.isUndefined()) {
QString deploymentDependenciesString = deploymentDependencies.toString();
- const auto dependencies = QStringView{deploymentDependenciesString}.split(QLatin1Char(','));
+ const auto dependencies = QStringView{deploymentDependenciesString}.split(u',');
for (const auto &dependency : dependencies) {
QString path = options->qtInstallDirectory + QChar::fromLatin1('/');
path += dependency;
if (QFileInfo(path).isDir()) {
- QDirIterator iterator(path, QDirIterator::Subdirectories);
- while (iterator.hasNext()) {
- if (iterator.nextFileInfo().isFile()) {
- QString subPath = iterator.filePath();
+ for (const auto &dirEntry : QDirListing(path, ItFlag::Recursive)) {
+ if (dirEntry.isFile()) {
+ const QString subPath = dirEntry.filePath();
auto arch = fileArchitecture(*options, subPath);
if (!arch.isEmpty()) {
- options->qtDependencies[arch].append(QtDependency(subPath.mid(options->qtInstallDirectory.length() + 1),
+ options->qtDependencies[arch].append(QtDependency(subPath.mid(options->qtInstallDirectory.size() + 1),
subPath));
} else if (options->verbose) {
fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(subPath));
@@ -1114,34 +1245,51 @@ bool readInputFile(Options *options)
}
}
} else {
- auto arch = fileArchitecture(*options, path);
- if (!arch.isEmpty()) {
- options->qtDependencies[arch].append(QtDependency(dependency.toString(), path));
- } else if (options->verbose) {
- fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(path));
- fflush(stderr);
+ auto qtDependency = [options](const QStringView &dependency,
+ const QString &arch) {
+ const auto installDir = options->architectures[arch].qtInstallDirectory;
+ const auto absolutePath = "%1/%2"_L1.arg(installDir, dependency.toString());
+ return QtDependency(dependency.toString(), absolutePath);
+ };
+
+ if (dependency.endsWith(QLatin1String(".so"))) {
+ auto arch = fileArchitecture(*options, path);
+ if (!arch.isEmpty()) {
+ options->qtDependencies[arch].append(qtDependency(dependency, arch));
+ } else if (options->verbose) {
+ fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(path));
+ fflush(stderr);
+ }
+ } else {
+ for (auto arch : options->architectures.keys())
+ options->qtDependencies[arch].append(qtDependency(dependency, arch));
}
}
}
}
}
{
- const QJsonValue qrcFiles = jsonObject.value(QLatin1String("qrcFiles"));
- options->qrcFiles = qrcFiles.toString().split(QLatin1Char(','), Qt::SkipEmptyParts);
+ const QJsonValue qrcFiles = jsonObject.value("qrcFiles"_L1);
+ options->qrcFiles = qrcFiles.toString().split(u',', Qt::SkipEmptyParts);
}
{
- const QJsonValue zstdCompressionFlag = jsonObject.value(QLatin1String("zstdCompression"));
+ const QJsonValue zstdCompressionFlag = jsonObject.value("zstdCompression"_L1);
if (zstdCompressionFlag.isBool()) {
options->isZstdCompressionEnabled = zstdCompressionFlag.toBool();
}
}
- options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + QLatin1String("/AndroidManifest.xml"));
+ options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + "/AndroidManifest.xml"_L1);
if (options->packageName.isEmpty())
- options->packageName = cleanPackageName(QLatin1String("org.qtproject.example.%1").arg(options->applicationBinary));
+ options->packageName = cleanPackageName("org.qtproject.example.%1"_L1.arg(options->applicationBinary));
return true;
}
+bool isDeployment(const Options *options, Options::DeploymentMechanism deployment)
+{
+ return options->deploymentMechanism == deployment;
+}
+
bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, const Options &options, bool forceOverwrite = false)
{
const QFileInfoList entries = sourceDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
@@ -1153,7 +1301,7 @@ bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, co
return false;
}
- if (!copyFiles(dir, QDir(destinationDirectory.path() + QLatin1Char('/') + dir.dirName()), options, forceOverwrite))
+ if (!copyFiles(dir, QDir(destinationDirectory.path() + u'/' + dir.dirName()), options, forceOverwrite))
return false;
} else {
QString destination = destinationDirectory.absoluteFilePath(entry.fileName());
@@ -1169,8 +1317,8 @@ void cleanTopFolders(const Options &options, const QDir &srcDir, const QString &
{
const auto dirs = srcDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs);
for (const QFileInfo &dir : dirs) {
- if (dir.fileName() != QLatin1String("libs"))
- deleteMissingFiles(options, dir.absoluteDir(), QDir(dstDir + dir.fileName()));
+ if (dir.fileName() != "libs"_L1)
+ deleteMissingFiles(options, dir.absoluteFilePath(), QDir(dstDir + dir.fileName()));
}
}
@@ -1179,13 +1327,15 @@ void cleanAndroidFiles(const Options &options)
if (!options.androidSourceDirectory.isEmpty())
cleanTopFolders(options, QDir(options.androidSourceDirectory), options.outputDirectory);
- cleanTopFolders(options, QDir(options.qtInstallDirectory + QLatin1String("/src/android/templates")),
+ cleanTopFolders(options,
+ QDir(options.qtInstallDirectory + u'/' +
+ options.qtDataDirectory + "/src/android/templates"_L1),
options.outputDirectory);
}
bool copyAndroidTemplate(const Options &options, const QString &androidTemplate, const QString &outDirPrefix = QString())
{
- QDir sourceDirectory(options.qtInstallDirectory + androidTemplate);
+ QDir sourceDirectory(options.qtInstallDirectory + u'/' + options.qtDataDirectory + androidTemplate);
if (!sourceDirectory.exists()) {
fprintf(stderr, "Cannot find template directory %s\n", qPrintable(sourceDirectory.absolutePath()));
return false;
@@ -1203,7 +1353,8 @@ bool copyAndroidTemplate(const Options &options, const QString &androidTemplate,
bool copyGradleTemplate(const Options &options)
{
- QDir sourceDirectory(options.qtInstallDirectory + QLatin1String("/src/3rdparty/gradle"));
+ QDir sourceDirectory(options.qtInstallDirectory + u'/' +
+ options.qtDataDirectory + "/src/3rdparty/gradle"_L1);
if (!sourceDirectory.exists()) {
fprintf(stderr, "Cannot find template directory %s\n", qPrintable(sourceDirectory.absolutePath()));
return false;
@@ -1226,7 +1377,7 @@ bool copyAndroidTemplate(const Options &options)
if (!copyGradleTemplate(options))
return false;
- if (!copyAndroidTemplate(options, QLatin1String("/src/android/templates")))
+ if (!copyAndroidTemplate(options, "/src/android/templates"_L1))
return false;
return true;
@@ -1254,8 +1405,16 @@ bool copyAndroidExtraLibs(Options *options)
if (options->extraLibs.isEmpty())
return true;
- if (options->verbose)
- fprintf(stdout, "Copying %zd external libraries to package.\n", size_t(options->extraLibs.size()));
+ if (options->verbose) {
+ switch (options->deploymentMechanism) {
+ case Options::Bundled:
+ fprintf(stdout, "Copying %zd external libraries to package.\n", size_t(options->extraLibs.size()));
+ break;
+ case Options::Unbundled:
+ fprintf(stdout, "Skip copying of external libraries.\n");
+ break;
+ };
+ }
for (const QString &extraLib : options->extraLibs) {
QFileInfo extraLibInfo(extraLib);
@@ -1268,19 +1427,21 @@ bool copyAndroidExtraLibs(Options *options)
fprintf(stdout, "Skipping \"%s\", architecture mismatch.\n", qPrintable(extraLib));
continue;
}
- if (!extraLibInfo.fileName().startsWith(QLatin1String("lib")) || extraLibInfo.suffix() != QLatin1String("so")) {
+ if (!extraLibInfo.fileName().startsWith("lib"_L1) || extraLibInfo.suffix() != "so"_L1) {
fprintf(stderr, "The file name of external library %s must begin with \"lib\" and end with the suffix \".so\".\n",
qPrintable(extraLib));
return false;
}
QString destinationFile(options->outputDirectory
- + QLatin1String("/libs/")
+ + "/libs/"_L1
+ options->currentArchitecture
- + QLatin1Char('/')
+ + u'/'
+ extraLibInfo.fileName());
- if (!copyFileIfNewer(extraLib, destinationFile, *options))
+ if (isDeployment(options, Options::Bundled)
+ && !copyFileIfNewer(extraLib, destinationFile, *options)) {
return false;
+ }
options->archExtraLibs[options->currentArchitecture] += extraLib;
}
@@ -1318,18 +1479,21 @@ bool copyAndroidExtraResources(Options *options)
}
QDir resourceDir(extraResource);
- QString assetsDir = options->outputDirectory + QLatin1String("/assets/") + resourceDir.dirName() + QLatin1Char('/');
- QString libsDir = options->outputDirectory + QLatin1String("/libs/") + options->currentArchitecture + QLatin1Char('/');
+ QString assetsDir = options->outputDirectory + "/assets/"_L1 +
+ resourceDir.dirName() + u'/';
+ QString libsDir = options->outputDirectory + "/libs/"_L1 + options->currentArchitecture + u'/';
const QStringList files = allFilesInside(resourceDir, resourceDir);
for (const QString &resourceFile : files) {
QString originFile(resourceDir.filePath(resourceFile));
QString destinationFile;
- if (!resourceFile.endsWith(QLatin1String(".so"))) {
+ if (!resourceFile.endsWith(".so"_L1)) {
destinationFile = assetsDir + resourceFile;
} else {
- if (!checkArchitecture(*options, originFile))
+ if (isDeployment(options, Options::Unbundled)
+ || !checkArchitecture(*options, originFile)) {
continue;
+ }
destinationFile = libsDir + resourceFile;
options->archExtraPlugins[options->currentArchitecture] += resourceFile;
}
@@ -1362,7 +1526,7 @@ bool updateFile(const QString &fileName, const QHash<QString, QString> &replacem
forever {
int index = contents.indexOf(it.key().toUtf8());
if (index >= 0) {
- contents.replace(index, it.key().length(), it.value().toUtf8());
+ contents.replace(index, it.key().size(), it.value().toUtf8());
hasReplacements = true;
} else {
break;
@@ -1390,7 +1554,7 @@ bool updateLibsXml(Options *options)
if (options->verbose)
fprintf(stdout, " -- res/values/libs.xml\n");
- QString fileName = options->outputDirectory + QLatin1String("/res/values/libs.xml");
+ QString fileName = options->outputDirectory + "/res/values/libs.xml"_L1;
if (!QFile::exists(fileName)) {
fprintf(stderr, "Cannot find %s in prepared packaged. This file is required.\n", qPrintable(fileName));
return false;
@@ -1401,23 +1565,40 @@ bool updateLibsXml(Options *options)
QString extraLibs;
for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) {
- QString libsPath = QLatin1String("libs/") + it.key() + QLatin1Char('/');
+ if (!it->enabled)
+ continue;
- qtLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), options->stdCppName);
+ qtLibs += " <item>%1;%2</item>\n"_L1.arg(it.key(), options->stdCppName);
for (const Options::BundledFile &bundledFile : options->bundledFiles[it.key()]) {
- if (bundledFile.second.startsWith(QLatin1String("lib/"))) {
+ if (bundledFile.second.startsWith("lib/lib"_L1)) {
+ if (!bundledFile.second.endsWith(".so"_L1)) {
+ fprintf(stderr,
+ "The bundled library %s doesn't end with .so. Android only supports "
+ "versionless libraries ending with the .so suffix.\n",
+ qPrintable(bundledFile.second));
+ return false;
+ }
QString s = bundledFile.second.mid(sizeof("lib/lib") - 1);
s.chop(sizeof(".so") - 1);
- qtLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), s);
+ qtLibs += " <item>%1;%2</item>\n"_L1.arg(it.key(), s);
}
}
if (!options->archExtraLibs[it.key()].isEmpty()) {
for (const QString &extraLib : options->archExtraLibs[it.key()]) {
QFileInfo extraLibInfo(extraLib);
- QString name = extraLibInfo.fileName().mid(sizeof("lib") - 1);
- name.chop(sizeof(".so") - 1);
- extraLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), name);
+ if (extraLibInfo.fileName().startsWith("lib"_L1)) {
+ if (!extraLibInfo.fileName().endsWith(".so"_L1)) {
+ fprintf(stderr,
+ "The library %s doesn't end with .so. Android only supports "
+ "versionless libraries ending with the .so suffix.\n",
+ qPrintable(extraLibInfo.fileName()));
+ return false;
+ }
+ QString name = extraLibInfo.fileName().mid(sizeof("lib") - 1);
+ name.chop(sizeof(".so") - 1);
+ extraLibs += " <item>%1;%2</item>\n"_L1.arg(it.key(), name);
+ }
}
}
@@ -1427,25 +1608,21 @@ bool updateLibsXml(Options *options)
if (localLibs.isEmpty()) {
QString plugin;
for (const QtDependency &qtDependency : options->qtDependencies[it.key()]) {
- if (qtDependency.relativePath.endsWith(QLatin1String("libqtforandroid.so"))
- || qtDependency.relativePath.endsWith(QLatin1String("libqtforandroidGL.so"))) {
- if (!plugin.isEmpty() && plugin != qtDependency.relativePath) {
- fprintf(stderr, "Both platform plugins libqtforandroid.so and libqtforandroidGL.so included in package. Please include only one.\n");
- return false;
- }
-
+ if (qtDependency.relativePath.contains("libplugins_platforms_qtforandroid_"_L1))
plugin = qtDependency.relativePath;
- }
- if (qtDependency.relativePath.contains(QLatin1String("libQt5OpenGL"))
- || qtDependency.relativePath.contains(QLatin1String("libQt5Quick"))) {
+
+ if (qtDependency.relativePath.contains(
+ QString::asprintf("libQt%dOpenGL", QT_VERSION_MAJOR))
+ || qtDependency.relativePath.contains(
+ QString::asprintf("libQt%dQuick", QT_VERSION_MAJOR))) {
options->usesOpenGL |= true;
- break;
}
}
if (plugin.isEmpty()) {
fflush(stdout);
- fprintf(stderr, "No platform plugin, neither libqtforandroid.so or libqtforandroidGL.so, included in package. Please include one.\n");
+ fprintf(stderr, "No platform plugin (libplugins_platforms_qtforandroid.so) included"
+ " in the deployment. Make sure the app links to Qt Gui library.\n");
fflush(stderr);
return false;
}
@@ -1457,10 +1634,10 @@ bool updateLibsXml(Options *options)
// remove all paths
for (auto &lib : localLibs) {
- if (lib.endsWith(QLatin1String(".so")))
- lib = lib.mid(lib.lastIndexOf(QLatin1Char('/')) + 1);
+ if (lib.endsWith(".so"_L1))
+ lib = lib.mid(lib.lastIndexOf(u'/') + 1);
}
- allLocalLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), localLibs.join(QLatin1Char(':')));
+ allLocalLibs += " <item>%1;%2</item>\n"_L1.arg(it.key(), localLibs.join(u':'));
}
options->initClasses.removeDuplicates();
@@ -1469,14 +1646,15 @@ bool updateLibsXml(Options *options)
replacements[QStringLiteral("<!-- %%INSERT_QT_LIBS%% -->")] += qtLibs.trimmed();
replacements[QStringLiteral("<!-- %%INSERT_LOCAL_LIBS%% -->")] = allLocalLibs.trimmed();
replacements[QStringLiteral("<!-- %%INSERT_EXTRA_LIBS%% -->")] = extraLibs.trimmed();
- const QString initClasses = options->initClasses.join(QLatin1Char(':'));
+ const QString initClasses = options->initClasses.join(u':');
replacements[QStringLiteral("<!-- %%INSERT_INIT_CLASSES%% -->")] = initClasses;
- // Bundle and use libs from the apk because currently we don't have a way avoid
- // duplicating them.
- replacements[QStringLiteral("<!-- %%BUNDLE_LOCAL_QT_LIBS%% -->")] = QLatin1String("1");
- replacements[QStringLiteral("<!-- %%USE_LOCAL_QT_LIBS%% -->")] = QLatin1String("1");
-
+ // Set BUNDLE_LOCAL_QT_LIBS based on the deployment used
+ replacements[QStringLiteral("<!-- %%BUNDLE_LOCAL_QT_LIBS%% -->")]
+ = isDeployment(options, Options::Unbundled) ? "0"_L1 : "1"_L1;
+ replacements[QStringLiteral("<!-- %%USE_LOCAL_QT_LIBS%% -->")] = "1"_L1;
+ replacements[QStringLiteral("<!-- %%SYSTEM_LIBS_PREFIX%% -->")] =
+ isDeployment(options, Options::Unbundled) ? options->systemLibsPath : QStringLiteral("");
if (!updateFile(fileName, replacements))
return false;
@@ -1492,7 +1670,7 @@ bool updateStringsXml(const Options &options)
QHash<QString, QString> replacements;
replacements[QStringLiteral("<!-- %%INSERT_APP_NAME%% -->")] = options.applicationBinary;
- QString fileName = options.outputDirectory + QLatin1String("/res/values/strings.xml");
+ QString fileName = options.outputDirectory + "/res/values/strings.xml"_L1;
if (!QFile::exists(fileName)) {
if (options.verbose)
fprintf(stdout, " -- Create strings.xml since it's missing.\n");
@@ -1524,22 +1702,22 @@ bool updateAndroidManifest(Options &options)
replacements[QStringLiteral("-- %%INSERT_APP_LIB_NAME%% --")] = options.applicationBinary;
replacements[QStringLiteral("-- %%INSERT_VERSION_NAME%% --")] = options.versionName;
replacements[QStringLiteral("-- %%INSERT_VERSION_CODE%% --")] = options.versionCode;
- replacements[QStringLiteral("package=\"org.qtproject.example\"")] = QLatin1String("package=\"%1\"").arg(options.packageName);
+ replacements[QStringLiteral("package=\"org.qtproject.example\"")] = "package=\"%1\""_L1.arg(options.packageName);
QString permissions;
- for (const QString &permission : qAsConst(options.permissions))
- permissions += QLatin1String(" <uses-permission android:name=\"%1\" />\n").arg(permission);
+ for (const QString &permission : std::as_const(options.permissions))
+ permissions += " <uses-permission android:name=\"%1\" />\n"_L1.arg(permission);
replacements[QStringLiteral("<!-- %%INSERT_PERMISSIONS -->")] = permissions.trimmed();
QString features;
- for (const QString &feature : qAsConst(options.features))
- features += QLatin1String(" <uses-feature android:name=\"%1\" android:required=\"false\" />\n").arg(feature);
+ for (const QString &feature : std::as_const(options.features))
+ features += " <uses-feature android:name=\"%1\" android:required=\"false\" />\n"_L1.arg(feature);
if (options.usesOpenGL)
- features += QLatin1String(" <uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\" />");
+ features += " <uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\" />"_L1;
replacements[QStringLiteral("<!-- %%INSERT_FEATURES -->")] = features.trimmed();
- QString androidManifestPath = options.outputDirectory + QLatin1String("/AndroidManifest.xml");
+ QString androidManifestPath = options.outputDirectory + "/AndroidManifest.xml"_L1;
if (!updateFile(androidManifestPath, replacements))
return false;
@@ -1557,28 +1735,27 @@ bool updateAndroidManifest(Options &options)
reader.readNext();
if (reader.isStartElement()) {
- if (reader.name() == QLatin1String("manifest")) {
- if (!reader.attributes().hasAttribute(QLatin1String("package"))) {
+ if (reader.name() == "manifest"_L1) {
+ if (!reader.attributes().hasAttribute("package"_L1)) {
fprintf(stderr, "Invalid android manifest file: %s\n", qPrintable(androidManifestPath));
return false;
}
- options.packageName = reader.attributes().value(QLatin1String("package")).toString();
- } else if (reader.name() == QLatin1String("uses-sdk")) {
- if (reader.attributes().hasAttribute(QLatin1String("android:minSdkVersion")))
- if (reader.attributes().value(QLatin1String("android:minSdkVersion")).toInt() < 23) {
+ options.packageName = reader.attributes().value("package"_L1).toString();
+ } else if (reader.name() == "uses-sdk"_L1) {
+ if (reader.attributes().hasAttribute("android:minSdkVersion"_L1))
+ if (reader.attributes().value("android:minSdkVersion"_L1).toInt() < 23) {
fprintf(stderr, "Invalid minSdkVersion version, minSdkVersion must be >= 23\n");
return false;
}
- } else if ((reader.name() == QLatin1String("application") ||
- reader.name() == QLatin1String("activity")) &&
- reader.attributes().hasAttribute(QLatin1String("android:label")) &&
- reader.attributes().value(QLatin1String("android:label")) == QLatin1String("@string/app_name")) {
+ } else if ((reader.name() == "application"_L1 ||
+ reader.name() == "activity"_L1) &&
+ reader.attributes().hasAttribute("android:label"_L1) &&
+ reader.attributes().value("android:label"_L1) == "@string/app_name"_L1) {
checkOldAndroidLabelString = true;
- } else if (reader.name() == QLatin1String("meta-data")) {
- const auto name = reader.attributes().value(QLatin1String("android:name"));
- const auto value = reader.attributes().value(QLatin1String("android:value"));
- if (name == QLatin1String("android.app.lib_name")
- && value.contains(QLatin1Char(' '))) {
+ } else if (reader.name() == "meta-data"_L1) {
+ const auto name = reader.attributes().value("android:name"_L1);
+ const auto value = reader.attributes().value("android:value"_L1);
+ if (name == "android.app.lib_name"_L1 && value.contains(u' ')) {
fprintf(stderr, "The Activity's android.app.lib_name should not contain"
" spaces.\n");
return false;
@@ -1621,20 +1798,40 @@ static QString absoluteFilePath(const Options *options, const QString &relativeF
// Use extraLibraryDirs as the extra library lookup folder if it is expected to find a file in
// any $prefix/lib folder.
// Library directories from a build tree(extraLibraryDirs) have the higher priority.
- if (relativeFileName.startsWith(QLatin1String("lib/"))) {
+ if (relativeFileName.startsWith("lib/"_L1)) {
for (const auto &dir : options->extraLibraryDirs) {
- const QString path = dir + QLatin1Char('/') + relativeFileName.mid(sizeof("lib/") - 1);
+ const QString path = dir + u'/' + relativeFileName.mid(sizeof("lib/") - 1);
if (QFile::exists(path))
return path;
}
}
for (const auto &prefix : options->extraPrefixDirs) {
- const QString path = prefix + QLatin1Char('/') + relativeFileName;
+ const QString path = prefix + u'/' + relativeFileName;
if (QFile::exists(path))
return path;
}
- return options->qtInstallDirectory + QLatin1Char('/') + relativeFileName;
+
+ if (relativeFileName.endsWith("-android-dependencies.xml"_L1)) {
+ for (const auto &dir : options->extraLibraryDirs) {
+ const QString path = dir + u'/' + relativeFileName;
+ if (QFile::exists(path))
+ return path;
+ }
+ return options->qtInstallDirectory + u'/' + options->qtLibsDirectory +
+ u'/' + relativeFileName;
+ }
+
+ if (relativeFileName.startsWith("jar/"_L1)) {
+ return options->qtInstallDirectory + u'/' + options->qtDataDirectory +
+ u'/' + relativeFileName;
+ }
+
+ if (relativeFileName.startsWith("lib/"_L1)) {
+ return options->qtInstallDirectory + u'/' + options->qtLibsDirectory +
+ u'/' + relativeFileName.mid(sizeof("lib/") - 1);
+ }
+ return options->qtInstallDirectory + u'/' + relativeFileName;
}
QList<QtDependency> findFilesRecursively(const Options &options, const QFileInfo &info, const QString &rootPath)
@@ -1656,19 +1853,63 @@ QList<QtDependency> findFilesRecursively(const Options &options, const QFileInfo
return ret;
} else {
- return QList<QtDependency>() << QtDependency(info.absoluteFilePath().mid(rootPath.length()), info.absoluteFilePath());
+ return QList<QtDependency>() << QtDependency(info.absoluteFilePath().mid(rootPath.size()), info.absoluteFilePath());
}
}
QList<QtDependency> findFilesRecursively(const Options &options, const QString &fileName)
{
+ // We try to find the fileName in extraPrefixDirs first. The function behaves differently
+ // depending on what the fileName points to. If fileName is a file then we try to find the
+ // first occurrence in extraPrefixDirs and return this file. If fileName is directory function
+ // iterates over it and looks for deployment artifacts in each 'extraPrefixDirs' entry.
+ // Also we assume that if the fileName is recognized as a directory once it will be directory
+ // for every 'extraPrefixDirs' entry.
+ QList<QtDependency> deps;
for (const auto &prefix : options.extraPrefixDirs) {
- QFileInfo info(prefix + QLatin1Char('/') + fileName);
- if (info.exists())
- return findFilesRecursively(options, info, prefix + QLatin1Char('/'));
+ QFileInfo info(prefix + u'/' + fileName);
+ if (info.exists()) {
+ if (info.isDir())
+ deps.append(findFilesRecursively(options, info, prefix + u'/'));
+ else
+ return findFilesRecursively(options, info, prefix + u'/');
+ }
+ }
+
+ // Usually android deployment settings contain Qt install directory in extraPrefixDirs.
+ if (std::find(options.extraPrefixDirs.begin(), options.extraPrefixDirs.end(),
+ options.qtInstallDirectory) == options.extraPrefixDirs.end()) {
+ QFileInfo info(options.qtInstallDirectory + "/"_L1 + fileName);
+ QFileInfo rootPath(options.qtInstallDirectory + "/"_L1);
+ deps.append(findFilesRecursively(options, info, rootPath.absolutePath()));
+ }
+ return deps;
+}
+
+void readDependenciesFromFiles(Options *options, const QList<QtDependency> &files,
+ QSet<QString> &usedDependencies,
+ QSet<QString> &remainingDependencies)
+{
+ for (const QtDependency &fileName : files) {
+ if (usedDependencies.contains(fileName.absolutePath))
+ continue;
+
+ if (fileName.absolutePath.endsWith(".so"_L1)) {
+ if (!readDependenciesFromElf(options, fileName.absolutePath, &usedDependencies,
+ &remainingDependencies)) {
+ fprintf(stdout, "Skipping file dependency: %s\n",
+ qPrintable(fileName.relativePath));
+ continue;
+ }
+ }
+ usedDependencies.insert(fileName.absolutePath);
+
+ if (options->verbose) {
+ fprintf(stdout, "Appending file dependency: %s\n", qPrintable(fileName.relativePath));
+ }
+
+ options->qtDependencies[options->currentArchitecture].append(fileName);
}
- QFileInfo info(options.qtInstallDirectory + QLatin1Char('/') + fileName);
- return findFilesRecursively(options, info, options.qtInstallDirectory + QLatin1Char('/'));
}
bool readAndroidDependencyXml(Options *options,
@@ -1676,7 +1917,7 @@ bool readAndroidDependencyXml(Options *options,
QSet<QString> *usedDependencies,
QSet<QString> *remainingDependencies)
{
- QString androidDependencyName = absoluteFilePath(options, QLatin1String("/lib/%1-android-dependencies.xml").arg(moduleName));
+ QString androidDependencyName = absoluteFilePath(options, "%1-android-dependencies.xml"_L1.arg(moduleName));
QFile androidDependencyFile(androidDependencyName);
if (androidDependencyFile.exists()) {
@@ -1693,35 +1934,27 @@ bool readAndroidDependencyXml(Options *options,
reader.readNext();
if (reader.isStartElement()) {
- if (reader.name() == QLatin1String("bundled")) {
- if (!reader.attributes().hasAttribute(QLatin1String("file"))) {
+ if (reader.name() == "bundled"_L1) {
+ if (!reader.attributes().hasAttribute("file"_L1)) {
fprintf(stderr, "Invalid android dependency file: %s\n", qPrintable(androidDependencyName));
return false;
}
- QString file = reader.attributes().value(QLatin1String("file")).toString();
+ QString file = reader.attributes().value("file"_L1).toString();
- // Special case, since this is handled by qmlimportscanner instead
- if (!options->rootPaths.empty()
- && (file == QLatin1String("qml") || file == QLatin1String("qml/")))
+ if (reader.attributes().hasAttribute("type"_L1)
+ && reader.attributes().value("type"_L1) == "plugin_dir"_L1
+ && !options->androidDeployPlugins.isEmpty()) {
continue;
+ }
const QList<QtDependency> fileNames = findFilesRecursively(*options, file);
- for (const QtDependency &fileName : fileNames) {
- if (usedDependencies->contains(fileName.absolutePath))
- continue;
-
- usedDependencies->insert(fileName.absolutePath);
-
- if (options->verbose)
- fprintf(stdout, "Appending dependency from xml: %s\n", qPrintable(fileName.relativePath));
-
- options->qtDependencies[options->currentArchitecture].append(fileName);
- }
- } else if (reader.name() == QLatin1String("jar")) {
- int bundling = reader.attributes().value(QLatin1String("bundling")).toInt();
- QString fileName = QDir::cleanPath(reader.attributes().value(QLatin1String("file")).toString());
- if (bundling == (options->deploymentMechanism == Options::Bundled)) {
+ readDependenciesFromFiles(options, fileNames, *usedDependencies,
+ *remainingDependencies);
+ } else if (reader.name() == "jar"_L1) {
+ int bundling = reader.attributes().value("bundling"_L1).toInt();
+ QString fileName = QDir::cleanPath(reader.attributes().value("file"_L1).toString());
+ if (bundling) {
QtDependency dependency(fileName, absoluteFilePath(options, fileName));
if (!usedDependencies->contains(dependency.absolutePath)) {
options->qtDependencies[options->currentArchitecture].append(dependency);
@@ -1729,13 +1962,13 @@ bool readAndroidDependencyXml(Options *options,
}
}
- if (reader.attributes().hasAttribute(QLatin1String("initClass"))) {
- options->initClasses.append(reader.attributes().value(QLatin1String("initClass")).toString());
+ if (reader.attributes().hasAttribute("initClass"_L1)) {
+ options->initClasses.append(reader.attributes().value("initClass"_L1).toString());
}
- } else if (reader.name() == QLatin1String("lib")) {
- QString fileName = QDir::cleanPath(reader.attributes().value(QLatin1String("file")).toString());
- if (reader.attributes().hasAttribute(QLatin1String("replaces"))) {
- QString replaces = reader.attributes().value(QLatin1String("replaces")).toString();
+ } else if (reader.name() == "lib"_L1) {
+ QString fileName = QDir::cleanPath(reader.attributes().value("file"_L1).toString());
+ if (reader.attributes().hasAttribute("replaces"_L1)) {
+ QString replaces = reader.attributes().value("replaces"_L1).toString();
for (int i=0; i<options->localLibs.size(); ++i) {
if (options->localLibs[options->currentArchitecture].at(i) == replaces) {
options->localLibs[options->currentArchitecture][i] = fileName;
@@ -1745,14 +1978,14 @@ bool readAndroidDependencyXml(Options *options,
} else if (!fileName.isEmpty()) {
options->localLibs[options->currentArchitecture].append(fileName);
}
- if (fileName.endsWith(QLatin1String(".so")) && checkArchitecture(*options, fileName)) {
+ if (fileName.endsWith(".so"_L1) && checkArchitecture(*options, fileName)) {
remainingDependencies->insert(fileName);
}
- } else if (reader.name() == QLatin1String("permission")) {
- QString name = reader.attributes().value(QLatin1String("name")).toString();
+ } else if (reader.name() == "permission"_L1) {
+ QString name = reader.attributes().value("name"_L1).toString();
options->permissions.append(name);
- } else if (reader.name() == QLatin1String("feature")) {
- QString name = reader.attributes().value(QLatin1String("name")).toString();
+ } else if (reader.name() == "feature"_L1) {
+ QString name = reader.attributes().value("name"_L1).toString();
options->features.append(name);
}
}
@@ -1773,19 +2006,13 @@ bool readAndroidDependencyXml(Options *options,
QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
{
- QString readElf = QLatin1String("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj").arg(options.ndkPath,
- options.toolchainPrefix,
- options.ndkHost);
-#if defined(Q_OS_WIN32)
- readElf += QLatin1String(".exe");
-#endif
-
+ QString readElf = llvmReadobjPath(options);
if (!QFile::exists(readElf)) {
fprintf(stderr, "Command does not exist: %s\n", qPrintable(readElf));
return QStringList();
}
- readElf = QLatin1String("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName));
+ readElf = "%1 --needed-libs %2"_L1.arg(shellQuote(readElf), shellQuote(fileName));
FILE *readElfCommand = openProcess(readElf);
if (!readElfCommand) {
@@ -1807,6 +2034,7 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
if (it == elfArchitectures.constEnd() || *it != options.currentArchitecture.toLatin1()) {
if (options.verbose)
fprintf(stdout, "Skipping \"%s\", architecture mismatch\n", qPrintable(fileName));
+ pclose(readElfCommand);
return {};
}
}
@@ -1816,7 +2044,7 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
if (!line.startsWith("lib"))
continue;
library = QString::fromLatin1(line);
- QString libraryName = QLatin1String("lib/") + library;
+ QString libraryName = "lib/"_L1 + library;
if (QFile::exists(absoluteFilePath(&options, libraryName)))
ret += libraryName;
}
@@ -1860,7 +2088,7 @@ bool readDependenciesFromElf(Options *options,
dependenciesToCheck.append(dependency);
}
- for (const QString &dependency : qAsConst(dependenciesToCheck)) {
+ for (const QString &dependency : std::as_const(dependenciesToCheck)) {
QString qtBaseName = dependency.mid(sizeof("lib/lib") - 1);
qtBaseName = qtBaseName.left(qtBaseName.size() - (sizeof(".so") - 1));
if (!readAndroidDependencyXml(options, qtBaseName, usedDependencies, remainingDependencies)) {
@@ -1871,41 +2099,38 @@ bool readDependenciesFromElf(Options *options,
return true;
}
-QString defaultLibexecDir()
-{
-#ifdef Q_OS_WIN32
- return QStringLiteral("bin");
-#else
- return QStringLiteral("libexec");
-#endif
-}
-
-bool goodToCopy(const Options *options, const QString &file, QStringList *unmetDependencies);
-bool checkQmlFileInRootPaths(const Options *options, const QString &absolutePath);
-
bool scanImports(Options *options, QSet<QString> *usedDependencies)
{
if (options->verbose)
fprintf(stdout, "Scanning for QML imports.\n");
QString qmlImportScanner;
- if (!options->qmlImportScannerBinaryPath.isEmpty())
+ if (!options->qmlImportScannerBinaryPath.isEmpty()) {
qmlImportScanner = options->qmlImportScannerBinaryPath;
- else
- qmlImportScanner = options->qtInstallDirectory + QLatin1String("/bin/qmlimportscanner");
-#if defined(Q_OS_WIN32)
- qmlImportScanner += QLatin1String(".exe");
-#endif
+ } else {
+ qmlImportScanner = execSuffixAppended(options->qtLibExecsDirectory +
+ "/qmlimportscanner"_L1);
+ }
QStringList importPaths;
- importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml"));
+ // In Conan's case, qtInstallDirectory will point only to qtbase installed files, which
+ // lacks a qml directory. We don't want to pass it as an import path if it doesn't exist
+ // because it will cause qmlimportscanner to fail.
+ // This also covers the case when only qtbase is installed in a regular Qt build.
+ const QString mainImportPath = options->qtInstallDirectory + u'/' + options->qtQmlDirectory;
+ if (QFile::exists(mainImportPath))
+ importPaths += shellQuote(mainImportPath);
+
+ // These are usually provided by CMake in the deployment json file from paths specified
+ // in CMAKE_FIND_ROOT_PATH. They might not have qml modules.
for (const QString &prefix : options->extraPrefixDirs)
- if (QDir().exists(prefix + QLatin1String("/qml")))
- importPaths += shellQuote(prefix + QLatin1String("/qml"));
+ if (QFile::exists(prefix + "/qml"_L1))
+ importPaths += shellQuote(prefix + "/qml"_L1);
- for (const QString &qmlImportPath : qAsConst(options->qmlImportPaths)) {
- if (QDir().exists(qmlImportPath)) {
+ // These are provided by both CMake and qmake.
+ for (const QString &qmlImportPath : std::as_const(options->qmlImportPaths)) {
+ if (QFile::exists(qmlImportPath)) {
importPaths += shellQuote(qmlImportPath);
} else {
fprintf(stderr, "Warning: QML import path %s does not exist.\n",
@@ -1932,7 +2157,7 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
if (!QFile::exists(qmlImportScanner)) {
fprintf(stderr, "%s: qmlimportscanner not found at %s\n",
- qmlImportExists ? QLatin1String("Error").data() : QLatin1String("Warning").data(),
+ qmlImportExists ? "Error"_L1.data() : "Warning"_L1.data(),
qPrintable(qmlImportScanner));
return true;
}
@@ -1940,23 +2165,23 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
for (auto rootPath : options->rootPaths) {
rootPath = QFileInfo(rootPath).absoluteFilePath();
- if (!rootPath.endsWith(QLatin1Char('/')))
- rootPath += QLatin1Char('/');
+ if (!rootPath.endsWith(u'/'))
+ rootPath += u'/';
// After checking for qml folder imports we can add rootPath
if (!rootPath.isEmpty())
importPaths += shellQuote(rootPath);
- qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath));
+ qmlImportScanner += " -rootPath %1"_L1.arg(shellQuote(rootPath));
}
if (!options->qrcFiles.isEmpty()) {
- qmlImportScanner += QLatin1String(" -qrcFiles");
+ qmlImportScanner += " -qrcFiles"_L1;
for (const QString &qrcFile : options->qrcFiles)
- qmlImportScanner += QLatin1Char(' ') + shellQuote(qrcFile);
+ qmlImportScanner += u' ' + shellQuote(qrcFile);
}
- qmlImportScanner += QLatin1String(" -importPath %1").arg(importPaths.join(QLatin1Char(' ')));
+ qmlImportScanner += " -importPath %1"_L1.arg(importPaths.join(u' '));
if (options->verbose) {
fprintf(stdout, "Running qmlimportscanner with the following command: %s\n",
@@ -1977,6 +2202,7 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
QJsonDocument jsonDocument = QJsonDocument::fromJson(output);
if (jsonDocument.isNull()) {
fprintf(stderr, "Invalid json output from qmlimportscanner.\n");
+ pclose(qmlImportScannerCommand);
return false;
}
@@ -1985,17 +2211,18 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
QJsonValue value = jsonArray.at(i);
if (!value.isObject()) {
fprintf(stderr, "Invalid format of qmlimportscanner output.\n");
+ pclose(qmlImportScannerCommand);
return false;
}
QJsonObject object = value.toObject();
- QString path = object.value(QLatin1String("path")).toString();
+ QString path = object.value("path"_L1).toString();
if (path.isEmpty()) {
fprintf(stderr, "Warning: QML import could not be resolved in any of the import paths: %s\n",
- qPrintable(object.value(QLatin1String("name")).toString()));
+ qPrintable(object.value("name"_L1).toString()));
} else {
if (options->verbose)
- fprintf(stdout, " -- Adding '%s' as QML dependency\n", path.toLocal8Bit().constData());
+ fprintf(stdout, " -- Adding '%s' as QML dependency\n", qPrintable(path));
QFileInfo info(path);
@@ -2007,24 +2234,22 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
}
QString absolutePath = info.absolutePath();
- if (!absolutePath.endsWith(QLatin1Char('/')))
- absolutePath += QLatin1Char('/');
+ if (!absolutePath.endsWith(u'/'))
+ absolutePath += u'/';
- if (checkQmlFileInRootPaths(options, absolutePath)) {
+ const QUrl url(object.value("name"_L1).toString());
+
+ const QString moduleUrlPath = u"/"_s + url.toString().replace(u'.', u'/');
+ if (checkCanImportFromRootPaths(options, info.absolutePath(), moduleUrlPath)) {
if (options->verbose)
fprintf(stdout, " -- Skipping because path is in QML root path.\n");
continue;
}
QString importPathOfThisImport;
- for (const QString &importPath : qAsConst(importPaths)) {
-#if defined(Q_OS_WIN32)
- Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
-#else
- Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
-#endif
+ for (const QString &importPath : std::as_const(importPaths)) {
QString cleanImportPath = QDir::cleanPath(importPath);
- if (info.absoluteFilePath().startsWith(cleanImportPath, caseSensitivity)) {
+ if (QFile::exists(cleanImportPath + moduleUrlPath)) {
importPathOfThisImport = importPath;
break;
}
@@ -2032,43 +2257,83 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
if (importPathOfThisImport.isEmpty()) {
fprintf(stderr, "Import found outside of import paths: %s.\n", qPrintable(info.absoluteFilePath()));
+ pclose(qmlImportScannerCommand);
return false;
}
- QDir dir(importPathOfThisImport);
- importPathOfThisImport = dir.absolutePath() + QLatin1Char('/');
-
- const QList<QtDependency> fileNames = findFilesRecursively(*options, info, importPathOfThisImport);
- for (QtDependency fileName : fileNames) {
- if (usedDependencies->contains(fileName.absolutePath))
- continue;
-
- usedDependencies->insert(fileName.absolutePath);
-
+ importPathOfThisImport = QDir(importPathOfThisImport).absolutePath() + u'/';
+ QList<QtDependency> qmlImportsDependencies;
+ auto collectQmlDependency = [&usedDependencies, &qmlImportsDependencies,
+ &importPathOfThisImport](const QString &filePath) {
+ if (!usedDependencies->contains(filePath)) {
+ usedDependencies->insert(filePath);
+ qmlImportsDependencies += QtDependency(
+ "qml/"_L1 + filePath.mid(importPathOfThisImport.size()),
+ filePath);
+ }
+ };
+
+ QString plugin = object.value("plugin"_L1).toString();
+ bool pluginIsOptional = object.value("pluginIsOptional"_L1).toBool();
+ QFileInfo pluginFileInfo = QFileInfo(
+ path + u'/' + "lib"_L1 + plugin + u'_'
+ + options->currentArchitecture + ".so"_L1);
+ QString pluginFilePath = pluginFileInfo.absoluteFilePath();
+ QSet<QString> remainingDependencies;
+ if (pluginFileInfo.exists() && checkArchitecture(*options, pluginFilePath)
+ && readDependenciesFromElf(options, pluginFilePath, usedDependencies,
+ &remainingDependencies)) {
+ collectQmlDependency(pluginFilePath);
+ } else if (!pluginIsOptional) {
if (options->verbose)
- fprintf(stdout, " -- Appending dependency found by qmlimportscanner: %s\n", qPrintable(fileName.absolutePath));
+ fprintf(stdout, " -- Skipping because the required plugin is missing.\n");
+ continue;
+ }
- // Put all imports in default import path in assets
- fileName.relativePath.prepend(QLatin1String("qml/"));
- options->qtDependencies[options->currentArchitecture].append(fileName);
+ QFileInfo qmldirFileInfo = QFileInfo(path + u'/' + "qmldir"_L1);
+ if (qmldirFileInfo.exists()) {
+ collectQmlDependency(qmldirFileInfo.absoluteFilePath());
+ }
- if (fileName.absolutePath.endsWith(QLatin1String(".so")) && checkArchitecture(*options, fileName.absolutePath)) {
- QSet<QString> remainingDependencies;
- if (!readDependenciesFromElf(options, fileName.absolutePath, usedDependencies, &remainingDependencies))
- return false;
+ QString prefer = object.value("prefer"_L1).toString();
+ // If the preferred location of Qml files points to the Qt resources, this means
+ // that all Qml files has been embedded into plugin and we should not copy them to the
+ // android rcc bundle
+ if (!prefer.startsWith(":/"_L1)) {
+ QVariantList qmlFiles =
+ object.value("components"_L1).toArray().toVariantList();
+ qmlFiles.append(object.value("scripts"_L1).toArray().toVariantList());
+ bool qmlFilesMissing = false;
+ for (const auto &qmlFileEntry : qmlFiles) {
+ QFileInfo fileInfo(qmlFileEntry.toString());
+ if (!fileInfo.exists()) {
+ qmlFilesMissing = true;
+ break;
+ }
+ collectQmlDependency(fileInfo.absoluteFilePath());
+ }
+ if (qmlFilesMissing) {
+ if (options->verbose)
+ fprintf(stdout,
+ " -- Skipping because the required qml files are missing.\n");
+ continue;
}
}
+
+ options->qtDependencies[options->currentArchitecture].append(qmlImportsDependencies);
}
}
+ pclose(qmlImportScannerCommand);
return true;
}
-bool checkQmlFileInRootPaths(const Options *options, const QString &absolutePath)
+bool checkCanImportFromRootPaths(const Options *options, const QString &absolutePath,
+ const QString &moduleUrlPath)
{
for (auto rootPath : options->rootPaths) {
- if (absolutePath.startsWith(rootPath))
+ if ((rootPath + moduleUrlPath) == absolutePath)
return true;
}
return false;
@@ -2097,8 +2362,8 @@ bool runCommand(const Options &options, const QString &command)
bool createRcc(const Options &options)
{
- auto assetsDir = QLatin1String("%1/assets").arg(options.outputDirectory);
- if (!QDir{QLatin1String("%1/android_rcc_bundle").arg(assetsDir)}.exists()) {
+ auto assetsDir = "%1/assets"_L1.arg(options.outputDirectory);
+ if (!QDir{"%1/android_rcc_bundle"_L1.arg(assetsDir)}.exists()) {
fprintf(stdout, "Skipping createRCC\n");
return true;
}
@@ -2111,43 +2376,40 @@ bool createRcc(const Options &options)
if (!options.rccBinaryPath.isEmpty()) {
rcc = options.rccBinaryPath;
} else {
- rcc = options.qtInstallDirectory + QLatin1Char('/') + defaultLibexecDir()
- + QLatin1String("/rcc");
+ rcc = execSuffixAppended(options.qtLibExecsDirectory + "/rcc"_L1);
}
-#if defined(Q_OS_WIN32)
- rcc += QLatin1String(".exe");
-#endif
-
if (!QFile::exists(rcc)) {
fprintf(stderr, "rcc not found: %s\n", qPrintable(rcc));
return false;
}
auto currentDir = QDir::currentPath();
- if (!QDir::setCurrent(QLatin1String("%1/android_rcc_bundle").arg(assetsDir))) {
- fprintf(stderr, "Cannot set current dir to: %s\n", qPrintable(QLatin1String("%1/android_rcc_bundle").arg(assetsDir)));
+ if (!QDir::setCurrent("%1/android_rcc_bundle"_L1.arg(assetsDir))) {
+ fprintf(stderr, "Cannot set current dir to: %s\n", qPrintable("%1/android_rcc_bundle"_L1.arg(assetsDir)));
return false;
}
- bool res = runCommand(options, QLatin1String("%1 --project -o %2").arg(rcc, shellQuote(QLatin1String("%1/android_rcc_bundle.qrc").arg(assetsDir))));
+ bool res = runCommand(options, "%1 --project -o %2"_L1.arg(rcc, shellQuote("%1/android_rcc_bundle.qrc"_L1.arg(assetsDir))));
if (!res)
return false;
- QLatin1String noZstd;
+ QLatin1StringView noZstd;
if (!options.isZstdCompressionEnabled)
- noZstd = QLatin1String("--no-zstd");
+ noZstd = "--no-zstd"_L1;
- QFile::rename(QLatin1String("%1/android_rcc_bundle.qrc").arg(assetsDir), QLatin1String("%1/android_rcc_bundle/android_rcc_bundle.qrc").arg(assetsDir));
+ QFile::rename("%1/android_rcc_bundle.qrc"_L1.arg(assetsDir), "%1/android_rcc_bundle/android_rcc_bundle.qrc"_L1.arg(assetsDir));
- res = runCommand(options, QLatin1String("%1 %2 %3 --binary -o %4 android_rcc_bundle.qrc").arg(rcc, shellQuote(QLatin1String("--root=/android_rcc_bundle/")),
- noZstd,
- shellQuote(QLatin1String("%1/android_rcc_bundle.rcc").arg(assetsDir))));
+ res = runCommand(options, "%1 %2 %3 --binary -o %4 android_rcc_bundle.qrc"_L1.arg(rcc, shellQuote("--root=/android_rcc_bundle/"_L1),
+ noZstd,
+ shellQuote("%1/android_rcc_bundle.rcc"_L1.arg(assetsDir))));
if (!QDir::setCurrent(currentDir)) {
fprintf(stderr, "Cannot set current dir to: %s\n", qPrintable(currentDir));
return false;
}
- QFile::remove(QLatin1String("%1/android_rcc_bundle.qrc").arg(assetsDir));
- QDir{QLatin1String("%1/android_rcc_bundle").arg(assetsDir)}.removeRecursively();
+ if (!options.noRccBundleCleanup) {
+ QFile::remove("%1/android_rcc_bundle.qrc"_L1.arg(assetsDir));
+ QDir{"%1/android_rcc_bundle"_L1.arg(assetsDir)}.removeRecursively();
+ }
return res;
}
@@ -2167,9 +2429,17 @@ bool readDependencies(Options *options)
QSet<QString> remainingDependencies;
// Add dependencies of application binary first
- if (!readDependenciesFromElf(options, QLatin1String("%1/libs/%2/lib%3_%2.so").arg(options->outputDirectory, options->currentArchitecture, options->applicationBinary), &usedDependencies, &remainingDependencies))
+ if (!readDependenciesFromElf(options, "%1/libs/%2/lib%3_%2.so"_L1.arg(options->outputDirectory, options->currentArchitecture, options->applicationBinary), &usedDependencies, &remainingDependencies))
return false;
+ QList<QtDependency> pluginDeps;
+ for (const auto &pluginPath : options->androidDeployPlugins) {
+ pluginDeps.append(findFilesRecursively(*options, QFileInfo(pluginPath),
+ options->qtInstallDirectory + "/"_L1));
+ }
+
+ readDependenciesFromFiles(options, pluginDeps, usedDependencies, remainingDependencies);
+
while (!remainingDependencies.isEmpty()) {
QSet<QString>::iterator start = remainingDependencies.begin();
QString fileName = absoluteFilePath(options, *start);
@@ -2183,7 +2453,7 @@ bool readDependencies(Options *options)
} else {
fprintf(stdout, "Skipping %s due to unmet dependencies: %s\n",
qPrintable(fileName),
- qPrintable(unmetDependencies.join(QLatin1Char(','))));
+ qPrintable(unmetDependencies.join(u',')));
}
}
@@ -2193,18 +2463,17 @@ bool readDependencies(Options *options)
if (!goodToCopy(options, absoluteFilePath(options, *it), &unmetDependencies)) {
fprintf(stdout, "Skipping %s due to unmet dependencies: %s\n",
qPrintable(*it),
- qPrintable(unmetDependencies.join(QLatin1Char(','))));
+ qPrintable(unmetDependencies.join(u',')));
it = options->localLibs[options->currentArchitecture].erase(it);
} else {
++it;
}
}
- if ((!options->rootPaths.empty() || options->qrcFiles.isEmpty()) &&
- !scanImports(options, &usedDependencies))
- return false;
-
- return true;
+ if (options->qmlSkipImportScanning
+ || (options->rootPaths.empty() && options->qrcFiles.isEmpty()))
+ return true;
+ return scanImports(options, &usedDependencies);
}
bool containsApplicationBinary(Options *options)
@@ -2215,18 +2484,17 @@ bool containsApplicationBinary(Options *options)
if (options->verbose)
fprintf(stdout, "Checking if application binary is in package.\n");
- QFileInfo applicationBinary(options->applicationBinary);
- QString applicationFileName = QLatin1String("lib%1_%2.so").arg(options->applicationBinary,
- options->currentArchitecture);
+ QString applicationFileName = "lib%1_%2.so"_L1.arg(options->applicationBinary,
+ options->currentArchitecture);
- QString applicationPath = QLatin1String("%1/libs/%2/%3").arg(options->outputDirectory,
- options->currentArchitecture,
- applicationFileName);
+ QString applicationPath = "%1/libs/%2/%3"_L1.arg(options->outputDirectory,
+ options->currentArchitecture,
+ applicationFileName);
if (!QFile::exists(applicationPath)) {
#if defined(Q_OS_WIN32)
- QLatin1String makeTool("mingw32-make"); // Only Mingw host builds supported on Windows currently
+ const auto makeTool = "mingw32-make"_L1; // Only Mingw host builds supported on Windows currently
#else
- QLatin1String makeTool("make");
+ const auto makeTool = "make"_L1;
#endif
fprintf(stderr, "Application binary is not in output directory: %s. Please run '%s install INSTALL_ROOT=%s' first.\n",
qPrintable(applicationFileName),
@@ -2239,20 +2507,16 @@ bool containsApplicationBinary(Options *options)
FILE *runAdb(const Options &options, const QString &arguments)
{
- QString adb = options.sdkPath + QLatin1String("/platform-tools/adb");
-#if defined(Q_OS_WIN32)
- adb += QLatin1String(".exe");
-#endif
-
+ QString adb = execSuffixAppended(options.sdkPath + "/platform-tools/adb"_L1);
if (!QFile::exists(adb)) {
fprintf(stderr, "Cannot find adb tool: %s\n", qPrintable(adb));
return 0;
}
QString installOption;
if (!options.installLocation.isEmpty())
- installOption = QLatin1String(" -s ") + shellQuote(options.installLocation);
+ installOption = " -s "_L1 + shellQuote(options.installLocation);
- adb = QLatin1String("%1%2 %3").arg(shellQuote(adb), installOption, arguments);
+ adb = "%1%2 %3"_L1.arg(shellQuote(adb), installOption, arguments);
if (options.verbose)
fprintf(stdout, "Running command \"%s\"\n", adb.toLocal8Bit().constData());
@@ -2268,7 +2532,7 @@ FILE *runAdb(const Options &options, const QString &arguments)
bool goodToCopy(const Options *options, const QString &file, QStringList *unmetDependencies)
{
- if (!file.endsWith(QLatin1String(".so")))
+ if (!file.endsWith(".so"_L1))
return true;
if (!checkArchitecture(*options, file))
@@ -2291,7 +2555,11 @@ bool copyQtFiles(Options *options)
if (options->verbose) {
switch (options->deploymentMechanism) {
case Options::Bundled:
- fprintf(stdout, "Copying %zd dependencies from Qt into package.\n", size_t(options->qtDependencies.size()));
+ fprintf(stdout, "Copying %zd dependencies from Qt into package.\n", size_t(options->qtDependencies[options->currentArchitecture].size()));
+ break;
+ case Options::Unbundled:
+ fprintf(stdout, "Copying dependencies from Qt into the package build folder,"
+ "skipping native libraries.\n");
break;
};
}
@@ -2300,23 +2568,19 @@ bool copyQtFiles(Options *options)
return true;
- QString libsDirectory = QLatin1String("libs/");
+ QString libsDirectory = "libs/"_L1;
// Copy other Qt dependencies
- auto assetsDestinationDirectory = QLatin1String("assets/android_rcc_bundle/");
- for (const QtDependency &qtDependency : qAsConst(options->qtDependencies[options->currentArchitecture])) {
+ auto assetsDestinationDirectory = "assets/android_rcc_bundle/"_L1;
+ for (const QtDependency &qtDependency : std::as_const(options->qtDependencies[options->currentArchitecture])) {
QString sourceFileName = qtDependency.absolutePath;
QString destinationFileName;
-
- if (qtDependency.relativePath.endsWith(QLatin1String(".so"))) {
- QString garbledFileName;
- if (QDir::fromNativeSeparators(qtDependency.relativePath).startsWith(QLatin1String("lib/"))) {
- garbledFileName = qtDependency.relativePath.mid(sizeof("lib/") - 1);
- } else {
- garbledFileName = qtDependency.relativePath.mid(qtDependency.relativePath.lastIndexOf(QLatin1Char('/')) + 1);
- }
- destinationFileName = libsDirectory + options->currentArchitecture + QLatin1Char('/') + garbledFileName;
- } else if (QDir::fromNativeSeparators(qtDependency.relativePath).startsWith(QLatin1String("jar/"))) {
+ bool isSharedLibrary = qtDependency.relativePath.endsWith(".so"_L1);
+ if (isSharedLibrary) {
+ QString garbledFileName = qtDependency.relativePath.mid(
+ qtDependency.relativePath.lastIndexOf(u'/') + 1);
+ destinationFileName = libsDirectory + options->currentArchitecture + u'/' + garbledFileName;
+ } else if (QDir::fromNativeSeparators(qtDependency.relativePath).startsWith("jar/"_L1)) {
destinationFileName = libsDirectory + qtDependency.relativePath.mid(sizeof("jar/") - 1);
} else {
destinationFileName = assetsDestinationDirectory + qtDependency.relativePath;
@@ -2337,19 +2601,18 @@ bool copyQtFiles(Options *options)
} else {
fprintf(stdout, " -- Skipping %s. It has unmet dependencies: %s.\n",
qPrintable(sourceFileName),
- qPrintable(unmetDependencies.join(QLatin1Char(','))));
+ qPrintable(unmetDependencies.join(u',')));
}
continue;
}
- if (options->deploymentMechanism == Options::Bundled
+ if ((isDeployment(options, Options::Bundled) || !isSharedLibrary)
&& !copyFileIfNewer(sourceFileName,
- options->outputDirectory + QLatin1Char('/') + destinationFileName,
+ options->outputDirectory + u'/' + destinationFileName,
*options)) {
return false;
}
-
- options->bundledFiles[options->currentArchitecture] += qMakePair(destinationFileName, qtDependency.relativePath);
+ options->bundledFiles[options->currentArchitecture] += std::make_pair(destinationFileName, qtDependency.relativePath);
}
return true;
@@ -2359,7 +2622,7 @@ QStringList getLibraryProjectsInOutputFolder(const Options &options)
{
QStringList ret;
- QFile file(options.outputDirectory + QLatin1String("/project.properties"));
+ QFile file(options.outputDirectory + "/project.properties"_L1);
if (file.open(QIODevice::ReadOnly)) {
while (!file.atEnd()) {
QByteArray line = file.readLine().trimmed();
@@ -2368,7 +2631,7 @@ QStringList getLibraryProjectsInOutputFolder(const Options &options)
if (equalSignIndex >= 0) {
QString path = QString::fromLocal8Bit(line.mid(equalSignIndex + 1));
- QFileInfo info(options.outputDirectory + QLatin1Char('/') + path);
+ QFileInfo info(options.outputDirectory + u'/' + path);
if (QDir::isRelativePath(path)
&& info.exists()
&& info.isDir()
@@ -2383,63 +2646,6 @@ QStringList getLibraryProjectsInOutputFolder(const Options &options)
return ret;
}
-bool createAndroidProject(const Options &options)
-{
- if (options.verbose)
- fprintf(stdout, "Running Android tool to create package definition.\n");
-
- QString androidToolExecutable = options.sdkPath + QLatin1String("/tools/android");
-#if defined(Q_OS_WIN32)
- androidToolExecutable += QLatin1String(".bat");
-#endif
-
- if (!QFile::exists(androidToolExecutable)) {
- fprintf(stderr, "Cannot find Android tool: %s\n", qPrintable(androidToolExecutable));
- return false;
- }
-
- QString androidTool = QLatin1String("%1 update project --path %2 --target %3 --name QtApp")
- .arg(shellQuote(androidToolExecutable))
- .arg(shellQuote(options.outputDirectory))
- .arg(shellQuote(options.androidPlatform));
-
- if (options.verbose)
- fprintf(stdout, " -- Command: %s\n", qPrintable(androidTool));
-
- FILE *androidToolCommand = openProcess(androidTool);
- if (androidToolCommand == 0) {
- fprintf(stderr, "Cannot run command '%s'\n", qPrintable(androidTool));
- return false;
- }
-
- pclose(androidToolCommand);
-
- // If the project has subprojects inside the current folder, we need to also run android update on these.
- const QStringList libraryProjects = getLibraryProjectsInOutputFolder(options);
- for (const QString &libraryProject : libraryProjects) {
- if (options.verbose)
- fprintf(stdout, "Updating subproject %s\n", qPrintable(libraryProject));
-
- androidTool = QLatin1String("%1 update lib-project --path %2 --target %3")
- .arg(shellQuote(androidToolExecutable))
- .arg(shellQuote(libraryProject))
- .arg(shellQuote(options.androidPlatform));
-
- if (options.verbose)
- fprintf(stdout, " -- Command: %s\n", qPrintable(androidTool));
-
- FILE *androidToolCommand = popen(androidTool.toLocal8Bit().constData(), QT_POPEN_READ);
- if (androidToolCommand == 0) {
- fprintf(stderr, "Cannot run command '%s'\n", qPrintable(androidTool));
- return false;
- }
-
- pclose(androidToolCommand);
- }
-
- return true;
-}
-
QString findInPath(const QString &fileName)
{
const QString path = QString::fromLocal8Bit(qgetenv("PATH"));
@@ -2451,9 +2657,9 @@ QString findInPath(const QString &fileName)
const QStringList paths = path.split(separator);
for (const QString &path : paths) {
- QFileInfo fileInfo(path + QLatin1Char('/') + fileName);
+ QFileInfo fileInfo(path + u'/' + fileName);
if (fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable())
- return path + QLatin1Char('/') + fileName;
+ return path + u'/' + fileName;
}
return QString();
@@ -2483,15 +2689,16 @@ static GradleProperties readGradleProperties(const QString &path)
static bool mergeGradleProperties(const QString &path, GradleProperties properties)
{
- QFile::remove(path + QLatin1Char('~'));
- QFile::rename(path, path + QLatin1Char('~'));
+ const QString oldPathStr = path + u'~';
+ QFile::remove(oldPathStr);
+ QFile::rename(path, oldPathStr);
QFile file(path);
if (!file.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
fprintf(stderr, "Can't open file: %s for writing\n", qPrintable(file.fileName()));
return false;
}
- QFile oldFile(path + QLatin1Char('~'));
+ QFile oldFile(oldPathStr);
if (oldFile.open(QIODevice::ReadOnly)) {
while (!oldFile.atEnd()) {
QByteArray line(oldFile.readLine());
@@ -2504,9 +2711,10 @@ static bool mergeGradleProperties(const QString &path, GradleProperties properti
continue;
}
}
- file.write(line);
+ file.write(line.trimmed() + '\n');
}
oldFile.close();
+ QFile::remove(oldPathStr);
}
for (GradleProperties::const_iterator it = properties.begin(); it != properties.end(); ++it)
@@ -2520,11 +2728,11 @@ static bool mergeGradleProperties(const QString &path, GradleProperties properti
void checkAndWarnGradleLongPaths(const QString &outputDirectory)
{
QStringList longFileNames;
- QDirIterator it(outputDirectory, QStringList(QStringLiteral("*.java")), QDir::Files,
- QDirIterator::Subdirectories);
- while (it.hasNext()) {
- if (it.next().size() >= MAX_PATH)
- longFileNames.append(it.next());
+ using F = QDirListing::IteratorFlag;
+ for (const auto &dirEntry : QDirListing(outputDirectory, QStringList(u"*.java"_s),
+ QDir::Files, F::Recursive)) {
+ if (dirEntry.size() >= MAX_PATH)
+ longFileNames.append(dirEntry.filePath());
}
if (!longFileNames.isEmpty()) {
@@ -2532,38 +2740,110 @@ void checkAndWarnGradleLongPaths(const QString &outputDirectory)
"The maximum path length that can be processed by Gradle on Windows is %d characters.\n"
"Consider moving your project to reduce its path length.\n"
"The following files have too long paths:\n%s.\n",
- MAX_PATH, qPrintable(longFileNames.join(QLatin1Char('\n'))));
+ MAX_PATH, qPrintable(longFileNames.join(u'\n')));
}
}
#endif
+struct GradleFlags {
+ bool setsLegacyPackaging = false;
+ bool usesIntegerCompileSdkVersion = false;
+};
+
+GradleFlags gradleBuildFlags(const QString &path)
+{
+ GradleFlags flags;
+
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly))
+ return flags;
+
+ auto isComment = [](const QByteArray &line) {
+ const auto trimmed = line.trimmed();
+ return trimmed.startsWith("//") || trimmed.startsWith('*') || trimmed.startsWith("/*");
+ };
+
+ const auto lines = file.readAll().split('\n');
+ for (const auto &line : lines) {
+ if (isComment(line))
+ continue;
+ if (line.contains("useLegacyPackaging")) {
+ flags.setsLegacyPackaging = true;
+ } else if (line.contains("compileSdkVersion androidCompileSdkVersion.toInteger()")) {
+ flags.usesIntegerCompileSdkVersion = true;
+ }
+ }
+
+ return flags;
+}
+
bool buildAndroidProject(const Options &options)
{
GradleProperties localProperties;
localProperties["sdk.dir"] = QDir::fromNativeSeparators(options.sdkPath).toUtf8();
- localProperties["ndk.dir"] = QDir::fromNativeSeparators(options.ndkPath).toUtf8();
-
- if (!mergeGradleProperties(options.outputDirectory + QLatin1String("local.properties"), localProperties))
+ const QString localPropertiesPath = options.outputDirectory + "local.properties"_L1;
+ if (!mergeGradleProperties(localPropertiesPath, localProperties))
return false;
- QString gradlePropertiesPath = options.outputDirectory + QLatin1String("gradle.properties");
+ const QString gradlePropertiesPath = options.outputDirectory + "gradle.properties"_L1;
GradleProperties gradleProperties = readGradleProperties(gradlePropertiesPath);
- gradleProperties["android.bundle.enableUncompressedNativeLibs"] = "false";
+
+ const QString gradleBuildFilePath = options.outputDirectory + "build.gradle"_L1;
+ GradleFlags gradleFlags = gradleBuildFlags(gradleBuildFilePath);
+ if (!gradleFlags.setsLegacyPackaging)
+ gradleProperties["android.bundle.enableUncompressedNativeLibs"] = "false";
+
gradleProperties["buildDir"] = "build";
- gradleProperties["qt5AndroidDir"] = (options.qtInstallDirectory + QLatin1String("/src/android/java")).toUtf8();
- gradleProperties["androidCompileSdkVersion"] = options.androidPlatform.split(QLatin1Char('-')).last().toLocal8Bit();
+ gradleProperties["qtAndroidDir"] =
+ (options.qtInstallDirectory + u'/' + options.qtDataDirectory +
+ "/src/android/java"_L1)
+ .toUtf8();
+ // The following property "qt5AndroidDir" is only for compatibility.
+ // Projects using a custom build.gradle file may use this variable.
+ // ### Qt7: Remove the following line
+ gradleProperties["qt5AndroidDir"] =
+ (options.qtInstallDirectory + u'/' + options.qtDataDirectory +
+ "/src/android/java"_L1)
+ .toUtf8();
+
+ QByteArray sdkPlatformVersion;
+ // Provide the integer version only if build.gradle explicitly converts to Integer,
+ // to avoid regression to existing projects that build for sdk platform of form android-xx.
+ if (gradleFlags.usesIntegerCompileSdkVersion) {
+ const QByteArray tmp = options.androidPlatform.split(u'-').last().toLocal8Bit();
+ bool ok;
+ tmp.toInt(&ok);
+ if (ok) {
+ sdkPlatformVersion = tmp;
+ } else {
+ fprintf(stderr, "Warning: Gradle expects SDK platform version to be an integer, "
+ "but the set version is not convertible to an integer.");
+ }
+ }
+
+ if (sdkPlatformVersion.isEmpty())
+ sdkPlatformVersion = options.androidPlatform.toLocal8Bit();
+
+ gradleProperties["androidCompileSdkVersion"] = sdkPlatformVersion;
gradleProperties["qtMinSdkVersion"] = options.minSdkVersion;
gradleProperties["qtTargetSdkVersion"] = options.targetSdkVersion;
+ gradleProperties["androidNdkVersion"] = options.ndkVersion.toUtf8();
if (gradleProperties["androidBuildToolsVersion"].isEmpty())
gradleProperties["androidBuildToolsVersion"] = options.sdkBuildToolsVersion.toLocal8Bit();
-
+ QString abiList;
+ for (auto it = options.architectures.constBegin(); it != options.architectures.constEnd(); ++it) {
+ if (!it->enabled)
+ continue;
+ if (abiList.size())
+ abiList.append(u",");
+ abiList.append(it.key());
+ }
+ gradleProperties["qtTargetAbiList"] = abiList.toLocal8Bit();// armeabi-v7a or arm64-v8a or ...
if (!mergeGradleProperties(gradlePropertiesPath, gradleProperties))
return false;
-#if defined(Q_OS_WIN32)
- QString gradlePath(options.outputDirectory + QLatin1String("gradlew.bat"));
-#else
- QString gradlePath(options.outputDirectory + QLatin1String("gradlew"));
+ QString gradlePath = batSuffixAppended(options.outputDirectory + "gradlew"_L1);
+#ifndef Q_OS_WIN32
{
QFile f(gradlePath);
if (!f.setPermissions(f.permissions() | QFileDevice::ExeUser))
@@ -2577,12 +2857,12 @@ bool buildAndroidProject(const Options &options)
return false;
}
- QString commandLine = QLatin1String("%1 %2").arg(shellQuote(gradlePath), options.releasePackage ? QLatin1String(" assembleRelease") : QLatin1String(" assembleDebug"));
+ QString commandLine = "%1 %2"_L1.arg(shellQuote(gradlePath), options.releasePackage ? " assembleRelease"_L1 : " assembleDebug"_L1);
if (options.buildAAB)
- commandLine += QLatin1String(" bundle");
+ commandLine += " bundle"_L1;
if (options.verbose)
- commandLine += QLatin1String(" --info");
+ commandLine += " --info"_L1;
FILE *gradleCommand = openProcess(commandLine);
if (gradleCommand == 0) {
@@ -2622,7 +2902,7 @@ bool uninstallApk(const Options &options)
fprintf(stdout, "Uninstalling old Android package %s if present.\n", qPrintable(options.packageName));
- FILE *adbCommand = runAdb(options, QLatin1String(" uninstall ") + shellQuote(options.packageName));
+ FILE *adbCommand = runAdb(options, " uninstall "_L1 + shellQuote(options.packageName));
if (adbCommand == 0)
return false;
@@ -2653,32 +2933,32 @@ enum PackageType {
QString packagePath(const Options &options, PackageType pt)
{
QString path(options.outputDirectory);
- path += QLatin1String("/build/outputs/%1/").arg(pt >= UnsignedAPK ? QStringLiteral("apk") : QStringLiteral("bundle"));
- QString buildType(options.releasePackage ? QLatin1String("release/") : QLatin1String("debug/"));
+ path += "/build/outputs/%1/"_L1.arg(pt >= UnsignedAPK ? QStringLiteral("apk") : QStringLiteral("bundle"));
+ QString buildType(options.releasePackage ? "release/"_L1 : "debug/"_L1);
if (QDir(path + buildType).exists())
path += buildType;
- path += QDir(options.outputDirectory).dirName() + QLatin1Char('-');
+ path += QDir(options.outputDirectory).dirName() + u'-';
if (options.releasePackage) {
- path += QLatin1String("release-");
+ path += "release-"_L1;
if (pt >= UnsignedAPK) {
if (pt == UnsignedAPK)
- path += QLatin1String("un");
- path += QLatin1String("signed.apk");
+ path += "un"_L1;
+ path += "signed.apk"_L1;
} else {
path.chop(1);
- path += QLatin1String(".aab");
+ path += ".aab"_L1;
}
} else {
- path += QLatin1String("debug");
+ path += "debug"_L1;
if (pt >= UnsignedAPK) {
if (pt == SignedAPK)
- path += QLatin1String("-signed");
- path += QLatin1String(".apk");
+ path += "-signed"_L1;
+ path += ".apk"_L1;
} else {
- path += QLatin1String(".aab");
+ path += ".aab"_L1;
}
}
- return shellQuote(path);
+ return path;
}
bool installApk(const Options &options)
@@ -2691,10 +2971,9 @@ bool installApk(const Options &options)
if (options.verbose)
fprintf(stdout, "Installing Android package to device.\n");
- FILE *adbCommand = runAdb(options,
- QLatin1String(" install -r ")
+ FILE *adbCommand = runAdb(options, " install -r "_L1
+ packagePath(options, options.keyStore.isEmpty() ? UnsignedAPK
- : SignedAPK));
+ : SignedAPK));
if (adbCommand == 0)
return false;
@@ -2726,10 +3005,14 @@ bool copyPackage(const Options &options)
bool copyStdCpp(Options *options)
{
+ if (isDeployment(options, Options::Unbundled))
+ return true;
if (options->verbose)
fprintf(stdout, "Copying STL library\n");
- QString stdCppPath = QLatin1String("%1/%2/lib%3.so").arg(options->stdCppPath, options->architectures[options->currentArchitecture], options->stdCppName);
+ const QString triple = options->architectures[options->currentArchitecture].triple;
+ const QString stdCppPath = "%1/%2/lib%3.so"_L1.arg(options->stdCppPath, triple,
+ options->stdCppName);
if (!QFile::exists(stdCppPath)) {
fprintf(stderr, "STL library does not exist at %s\n", qPrintable(stdCppPath));
fflush(stdout);
@@ -2737,13 +3020,29 @@ bool copyStdCpp(Options *options)
return false;
}
- const QString destinationFile = QLatin1String("%1/libs/%2/lib%3.so").arg(options->outputDirectory,
- options->currentArchitecture,
- options->stdCppName);
+ const QString destinationFile = "%1/libs/%2/lib%3.so"_L1.arg(options->outputDirectory,
+ options->currentArchitecture,
+ options->stdCppName);
return copyFileIfNewer(stdCppPath, destinationFile, *options);
}
-bool jarSignerSignPackage(const Options &options)
+static QString zipalignPath(const Options &options, bool *ok)
+{
+ *ok = true;
+ QString zipAlignTool = execSuffixAppended(options.sdkPath + "/tools/zipalign"_L1);
+ if (!QFile::exists(zipAlignTool)) {
+ zipAlignTool = execSuffixAppended(options.sdkPath + "/build-tools/"_L1 +
+ options.sdkBuildToolsVersion + "/zipalign"_L1);
+ if (!QFile::exists(zipAlignTool)) {
+ fprintf(stderr, "zipalign tool not found: %s\n", qPrintable(zipAlignTool));
+ *ok = false;
+ }
+ }
+
+ return zipAlignTool;
+}
+
+bool signAAB(const Options &options)
{
if (options.verbose)
fprintf(stdout, "Signing Android package.\n");
@@ -2753,61 +3052,55 @@ bool jarSignerSignPackage(const Options &options)
if (jdkPath.isEmpty())
jdkPath = QString::fromLocal8Bit(qgetenv("JAVA_HOME"));
-#if defined(Q_OS_WIN32)
- QString jarSignerTool = QLatin1String("jarsigner.exe");
-#else
- QString jarSignerTool = QLatin1String("jarsigner");
-#endif
-
- if (jdkPath.isEmpty() || !QFile::exists(jdkPath + QLatin1String("/bin/") + jarSignerTool))
+ QString jarSignerTool = execSuffixAppended("jarsigner"_L1);
+ if (jdkPath.isEmpty() || !QFile::exists(jdkPath + "/bin/"_L1 + jarSignerTool))
jarSignerTool = findInPath(jarSignerTool);
else
- jarSignerTool = jdkPath + QLatin1String("/bin/") + jarSignerTool;
+ jarSignerTool = jdkPath + "/bin/"_L1 + jarSignerTool;
if (!QFile::exists(jarSignerTool)) {
fprintf(stderr, "Cannot find jarsigner in JAVA_HOME or PATH. Please use --jdk option to pass in the correct path to JDK.\n");
return false;
}
- jarSignerTool = QLatin1String("%1 -sigalg %2 -digestalg %3 -keystore %4")
+ jarSignerTool = "%1 -sigalg %2 -digestalg %3 -keystore %4"_L1
.arg(shellQuote(jarSignerTool), shellQuote(options.sigAlg), shellQuote(options.digestAlg), shellQuote(options.keyStore));
if (!options.keyStorePassword.isEmpty())
- jarSignerTool += QLatin1String(" -storepass %1").arg(shellQuote(options.keyStorePassword));
+ jarSignerTool += " -storepass %1"_L1.arg(shellQuote(options.keyStorePassword));
if (!options.storeType.isEmpty())
- jarSignerTool += QLatin1String(" -storetype %1").arg(shellQuote(options.storeType));
+ jarSignerTool += " -storetype %1"_L1.arg(shellQuote(options.storeType));
if (!options.keyPass.isEmpty())
- jarSignerTool += QLatin1String(" -keypass %1").arg(shellQuote(options.keyPass));
+ jarSignerTool += " -keypass %1"_L1.arg(shellQuote(options.keyPass));
if (!options.sigFile.isEmpty())
- jarSignerTool += QLatin1String(" -sigfile %1").arg(shellQuote(options.sigFile));
+ jarSignerTool += " -sigfile %1"_L1.arg(shellQuote(options.sigFile));
if (!options.signedJar.isEmpty())
- jarSignerTool += QLatin1String(" -signedjar %1").arg(shellQuote(options.signedJar));
+ jarSignerTool += " -signedjar %1"_L1.arg(shellQuote(options.signedJar));
if (!options.tsaUrl.isEmpty())
- jarSignerTool += QLatin1String(" -tsa %1").arg(shellQuote(options.tsaUrl));
+ jarSignerTool += " -tsa %1"_L1.arg(shellQuote(options.tsaUrl));
if (!options.tsaCert.isEmpty())
- jarSignerTool += QLatin1String(" -tsacert %1").arg(shellQuote(options.tsaCert));
+ jarSignerTool += " -tsacert %1"_L1.arg(shellQuote(options.tsaCert));
if (options.internalSf)
- jarSignerTool += QLatin1String(" -internalsf");
+ jarSignerTool += " -internalsf"_L1;
if (options.sectionsOnly)
- jarSignerTool += QLatin1String(" -sectionsonly");
+ jarSignerTool += " -sectionsonly"_L1;
if (options.protectedAuthenticationPath)
- jarSignerTool += QLatin1String(" -protected");
+ jarSignerTool += " -protected"_L1;
- auto signPackage = [&](const QString &file) {
+ auto jarSignPackage = [&](const QString &file) {
fprintf(stdout, "Signing file %s\n", qPrintable(file));
fflush(stdout);
- QString command = jarSignerTool + QLatin1String(" %1 %2")
- .arg(file)
- .arg(shellQuote(options.keyStoreAlias));
+ QString command = jarSignerTool + " %1 %2"_L1.arg(shellQuote(file))
+ .arg(shellQuote(options.keyStoreAlias));
FILE *jarSignerCommand = openProcess(command);
if (jarSignerCommand == 0) {
@@ -2831,81 +3124,22 @@ bool jarSignerSignPackage(const Options &options)
return true;
};
- if (!signPackage(packagePath(options, UnsignedAPK)))
- return false;
- if (options.buildAAB && !signPackage(packagePath(options, AAB)))
- return false;
-
- QString zipAlignTool = options.sdkPath + QLatin1String("/tools/zipalign");
-#if defined(Q_OS_WIN32)
- zipAlignTool += QLatin1String(".exe");
-#endif
-
- if (!QFile::exists(zipAlignTool)) {
- zipAlignTool = options.sdkPath + QLatin1String("/build-tools/") + options.sdkBuildToolsVersion + QLatin1String("/zipalign");
-#if defined(Q_OS_WIN32)
- zipAlignTool += QLatin1String(".exe");
-#endif
- if (!QFile::exists(zipAlignTool)) {
- fprintf(stderr, "zipalign tool not found: %s\n", qPrintable(zipAlignTool));
- return false;
- }
- }
-
- zipAlignTool = QLatin1String("%1%2 -f 4 %3 %4")
- .arg(shellQuote(zipAlignTool),
- options.verbose ? QLatin1String(" -v") : QLatin1String(),
- packagePath(options, UnsignedAPK),
- packagePath(options, SignedAPK));
-
- FILE *zipAlignCommand = openProcess(zipAlignTool);
- if (zipAlignCommand == 0) {
- fprintf(stderr, "Couldn't run zipalign.\n");
- return false;
- }
-
- char buffer[512];
- while (fgets(buffer, sizeof(buffer), zipAlignCommand) != 0)
- fprintf(stdout, "%s", buffer);
-
- int errorCode = pclose(zipAlignCommand);
- if (errorCode != 0) {
- fprintf(stderr, "zipalign command failed.\n");
- if (!options.verbose)
- fprintf(stderr, " -- Run with --verbose for more information.\n");
+ if (options.buildAAB && !jarSignPackage(packagePath(options, AAB)))
return false;
- }
-
- return QFile::remove(packagePath(options, UnsignedAPK));
+ return true;
}
bool signPackage(const Options &options)
{
- QString apksignerTool = options.sdkPath + QLatin1String("/build-tools/") + options.sdkBuildToolsVersion + QLatin1String("/apksigner");
-#if defined(Q_OS_WIN32)
- apksignerTool += QLatin1String(".bat");
-#endif
-
- if (options.jarSigner || !QFile::exists(apksignerTool))
- return jarSignerSignPackage(options);
-
- // APKs signed with apksigner must not be changed after they're signed, therefore we need to zipalign it before we sign it.
+ const QString apksignerTool = batSuffixAppended(options.sdkPath + "/build-tools/"_L1 +
+ options.sdkBuildToolsVersion + "/apksigner"_L1);
+ // APKs signed with apksigner must not be changed after they're signed,
+ // therefore we need to zipalign it before we sign it.
- QString zipAlignTool = options.sdkPath + QLatin1String("/tools/zipalign");
-#if defined(Q_OS_WIN32)
- zipAlignTool += QLatin1String(".exe");
-#endif
-
- if (!QFile::exists(zipAlignTool)) {
- zipAlignTool = options.sdkPath + QLatin1String("/build-tools/") + options.sdkBuildToolsVersion + QLatin1String("/zipalign");
-#if defined(Q_OS_WIN32)
- zipAlignTool += QLatin1String(".exe");
-#endif
- if (!QFile::exists(zipAlignTool)) {
- fprintf(stderr, "zipalign tool not found: %s\n", qPrintable(zipAlignTool));
- return false;
- }
- }
+ bool ok;
+ QString zipAlignTool = zipalignPath(options, &ok);
+ if (!ok)
+ return false;
auto zipalignRunner = [](const QString &zipAlignCommandLine) {
FILE *zipAlignCommand = openProcess(zipAlignCommandLine);
@@ -2921,10 +3155,11 @@ bool signPackage(const Options &options)
return pclose(zipAlignCommand) == 0;
};
- const QString verifyZipAlignCommandLine = QLatin1String("%1%2 -c 4 %3")
- .arg(shellQuote(zipAlignTool),
- options.verbose ? QLatin1String(" -v") : QLatin1String(),
- packagePath(options, UnsignedAPK));
+ const QString verifyZipAlignCommandLine =
+ "%1%2 -c 4 %3"_L1
+ .arg(shellQuote(zipAlignTool),
+ options.verbose ? " -v"_L1 : QLatin1StringView(),
+ shellQuote(packagePath(options, UnsignedAPK)));
if (zipalignRunner(verifyZipAlignCommandLine)) {
if (options.verbose)
@@ -2941,11 +3176,12 @@ bool signPackage(const Options &options)
if (options.verbose)
fprintf(stdout, "APK not aligned, aligning it for signing.\n");
- const QString zipAlignCommandLine = QLatin1String("%1%2 -f 4 %3 %4")
- .arg(shellQuote(zipAlignTool),
- options.verbose ? QLatin1String(" -v") : QLatin1String(),
- packagePath(options, UnsignedAPK),
- packagePath(options, SignedAPK));
+ const QString zipAlignCommandLine =
+ "%1%2 -f 4 %3 %4"_L1
+ .arg(shellQuote(zipAlignTool),
+ options.verbose ? " -v"_L1 : QLatin1StringView(),
+ shellQuote(packagePath(options, UnsignedAPK)),
+ shellQuote(packagePath(options, SignedAPK)));
if (!zipalignRunner(zipAlignCommandLine)) {
fprintf(stderr, "zipalign command failed.\n");
@@ -2955,23 +3191,22 @@ bool signPackage(const Options &options)
}
}
- QString apkSignCommand = QLatin1String("%1 sign --ks %2")
+ QString apkSignCommand = "%1 sign --ks %2"_L1
.arg(shellQuote(apksignerTool), shellQuote(options.keyStore));
if (!options.keyStorePassword.isEmpty())
- apkSignCommand += QLatin1String(" --ks-pass pass:%1").arg(shellQuote(options.keyStorePassword));
+ apkSignCommand += " --ks-pass pass:%1"_L1.arg(shellQuote(options.keyStorePassword));
if (!options.keyStoreAlias.isEmpty())
- apkSignCommand += QLatin1String(" --ks-key-alias %1").arg(shellQuote(options.keyStoreAlias));
+ apkSignCommand += " --ks-key-alias %1"_L1.arg(shellQuote(options.keyStoreAlias));
if (!options.keyPass.isEmpty())
- apkSignCommand += QLatin1String(" --key-pass pass:%1").arg(shellQuote(options.keyPass));
+ apkSignCommand += " --key-pass pass:%1"_L1.arg(shellQuote(options.keyPass));
if (options.verbose)
- apkSignCommand += QLatin1String(" --verbose");
+ apkSignCommand += " --verbose"_L1;
- apkSignCommand += QLatin1String(" %1")
- .arg(packagePath(options, SignedAPK));
+ apkSignCommand += " %1"_L1.arg(shellQuote(packagePath(options, SignedAPK)));
auto apkSignerRunner = [](const QString &command, bool verbose) {
FILE *apkSigner = openProcess(command);
@@ -2998,8 +3233,12 @@ bool signPackage(const Options &options)
if (!apkSignerRunner(apkSignCommand, options.verbose))
return false;
- const QString apkVerifyCommand = QLatin1String("%1 verify --verbose %2")
- .arg(shellQuote(apksignerTool), packagePath(options, SignedAPK));
+ const QString apkVerifyCommand =
+ "%1 verify --verbose %2"_L1
+ .arg(shellQuote(apksignerTool), shellQuote(packagePath(options, SignedAPK)));
+
+ if (options.buildAAB && !signAAB(options))
+ return false;
// Verify the package and remove the unsigned apk
return apkSignerRunner(apkVerifyCommand, true) && QFile::remove(packagePath(options, UnsignedAPK));
@@ -3018,7 +3257,7 @@ enum ErrorCode
CannotCopyAndroidExtraLibs = 10,
CannotCopyAndroidSources = 11,
CannotUpdateAndroidFiles = 12,
- CannotCreateAndroidProject = 13,
+ CannotCreateAndroidProject = 13, // Not used anymore
CannotBuildAndroidProject = 14,
CannotSignPackage = 15,
CannotInstallApk = 16,
@@ -3032,12 +3271,19 @@ bool writeDependencyFile(const Options &options)
if (options.verbose)
fprintf(stdout, "Writing dependency file.\n");
- QFile depFile(options.depFilePath);
-
- QString relativeApkPath = QDir(options.buildDirectory).relativeFilePath(options.apkPath);
+ QString relativeTargetPath;
+ if (options.copyDependenciesOnly) {
+ // When androiddeploy Qt is running in copyDependenciesOnly mode we need to use
+ // the timestamp file as the target to collect dependencies.
+ QString timestampAbsPath = QFileInfo(options.depFilePath).absolutePath() + "/timestamp"_L1;
+ relativeTargetPath = QDir(options.buildDirectory).relativeFilePath(timestampAbsPath);
+ } else {
+ relativeTargetPath = QDir(options.buildDirectory).relativeFilePath(options.apkPath);
+ }
+ QFile depFile(options.depFilePath);
if (depFile.open(QIODevice::WriteOnly)) {
- depFile.write(escapeAndEncodeDependencyPath(relativeApkPath));
+ depFile.write(escapeAndEncodeDependencyPath(relativeTargetPath));
depFile.write(": ");
for (const auto &file : dependenciesForDepfile) {
@@ -3066,10 +3312,9 @@ int main(int argc, char *argv[])
return CannotReadInputFile;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Read input file\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Read input file\n", options.timer.nsecsElapsed());
fprintf(stdout,
-// "012345678901234567890123456789012345678901234567890123456789012345678901"
"Generating Android Package\n"
" Input file: %s\n"
" Output directory: %s\n"
@@ -3085,61 +3330,81 @@ int main(int argc, char *argv[])
: "No"
);
- if (options.build && !options.auxMode) {
- cleanAndroidFiles(options);
- if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Cleaned Android file\n", options.timer.elapsed());
+ bool androidTemplatetCopied = false;
- if (!copyAndroidTemplate(options))
- return CannotCopyAndroidTemplate;
+ for (auto it = options.architectures.constBegin(); it != options.architectures.constEnd(); ++it) {
+ if (!it->enabled)
+ continue;
+ options.setCurrentQtArchitecture(it.key(),
+ it.value().qtInstallDirectory,
+ it.value().qtDirectories);
- if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Copied Android template\n", options.timer.elapsed());
- }
+ // All architectures have a copy of the gradle files but only one set needs to be copied.
+ if (!androidTemplatetCopied && options.build && !options.copyDependenciesOnly) {
+ cleanAndroidFiles(options);
+ if (Q_UNLIKELY(options.timing))
+ fprintf(stdout, "[TIMING] %lld ns: Cleaned Android file\n", options.timer.nsecsElapsed());
- for (auto it = options.architectures.constBegin(); it != options.architectures.constEnd(); ++it) {
- options.clear(it.key());
+ if (!copyAndroidTemplate(options))
+ return CannotCopyAndroidTemplate;
+
+ if (Q_UNLIKELY(options.timing))
+ fprintf(stdout, "[TIMING] %lld ns: Copied Android template\n", options.timer.nsecsElapsed());
+ androidTemplatetCopied = true;
+ }
if (!readDependencies(&options))
return CannotReadDependencies;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Read dependencies\n", options.timer.nsecsElapsed());
if (!copyQtFiles(&options))
return CannotCopyQtFiles;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Copied Qt files\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Copied Qt files\n", options.timer.nsecsElapsed());
if (!copyAndroidExtraLibs(&options))
return CannotCopyAndroidExtraLibs;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Copied extra libs\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ms: Copied extra libs\n", options.timer.nsecsElapsed());
if (!copyAndroidExtraResources(&options))
return CannotCopyAndroidExtraResources;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Copied extra resources\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Copied extra resources\n", options.timer.nsecsElapsed());
- if (!options.auxMode) {
- if (!copyStdCpp(&options))
- return CannotCopyGnuStl;
-
- if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Copied GNU STL\n", options.timer.elapsed());
- }
+ if (!copyStdCpp(&options))
+ return CannotCopyGnuStl;
- if (!containsApplicationBinary(&options))
+ if (Q_UNLIKELY(options.timing))
+ fprintf(stdout, "[TIMING] %lld ns: Copied GNU STL\n", options.timer.nsecsElapsed());
+
+ // If Unbundled deployment is used, remove app lib as we don't want it packaged inside the APK
+ if (options.deploymentMechanism == Options::Unbundled) {
+ QString appLibPath = "%1/libs/%2/lib%3_%2.so"_L1.
+ arg(options.outputDirectory,
+ options.currentArchitecture,
+ options.applicationBinary);
+ QFile::remove(appLibPath);
+ } else if (!containsApplicationBinary(&options)) {
return CannotFindApplicationBinary;
+ }
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Checked for application binary\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Checked for application binary\n", options.timer.nsecsElapsed());
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Bundled Qt libs\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Bundled Qt libs\n", options.timer.nsecsElapsed());
+ }
+
+ if (options.copyDependenciesOnly) {
+ if (!options.depFilePath.isEmpty())
+ writeDependencyFile(options);
+ return 0;
}
if (!createRcc(options))
@@ -3151,28 +3416,27 @@ int main(int argc, char *argv[])
return 0;
}
-
if (options.build) {
if (!copyAndroidSources(options))
return CannotCopyAndroidSources;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Copied android sources\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Copied android sources\n", options.timer.nsecsElapsed());
if (!updateAndroidFiles(options))
return CannotUpdateAndroidFiles;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Updated files\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Updated files\n", options.timer.nsecsElapsed());
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Created project\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Created project\n", options.timer.nsecsElapsed());
if (!buildAndroidProject(options))
return CannotBuildAndroidProject;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Built project\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Built project\n", options.timer.nsecsElapsed());
if (!options.keyStore.isEmpty() && !signPackage(options))
return CannotSignPackage;
@@ -3181,14 +3445,14 @@ int main(int argc, char *argv[])
return CannotCopyApk;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Signed package\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Signed package\n", options.timer.nsecsElapsed());
}
if (options.installApk && !installApk(options))
return CannotInstallApk;
if (Q_UNLIKELY(options.timing))
- fprintf(stdout, "[TIMING] %d ms: Installed APK\n", options.timer.elapsed());
+ fprintf(stdout, "[TIMING] %lld ns: Installed APK\n", options.timer.nsecsElapsed());
if (!options.depFilePath.isEmpty())
writeDependencyFile(options);
diff --git a/src/tools/androidtestrunner/CMakeLists.txt b/src/tools/androidtestrunner/CMakeLists.txt
index c53b9125ca..7935753cff 100644
--- a/src/tools/androidtestrunner/CMakeLists.txt
+++ b/src/tools/androidtestrunner/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from androidtestrunner.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## androidtestrunner App:
@@ -14,16 +15,14 @@ qt_internal_add_tool(${target_name}
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_FOREACH
- PUBLIC_LIBRARIES
- Qt::Gui
+ LIBRARIES
+ Qt::Core
)
+qt_internal_return_unless_building_tools()
set_target_properties(${target_name} PROPERTIES
WIN32_EXECUTABLE FALSE
)
-#### Keys ignored in scope 1:.:.:androidtestrunner.pro:<TRUE>:
-# _OPTION = "host_build"
-
## Scopes:
#####################################################################
diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp
index 3e59148cff..baad678ec0 100644
--- a/src/tools/androidtestrunner/main.cpp
+++ b/src/tools/androidtestrunner/main.cpp
@@ -1,203 +1,200 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QCoreApplication>
-#include <QDir>
-#include <QHash>
-#include <QRegularExpression>
-#include <QSystemSemaphore>
-#include <QXmlStreamReader>
-
-#include <algorithm>
-#include <chrono>
+// Copyright (C) 2019 BogDan Vatra <bogdan@kde.org>
+// 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 <QtCore/QCoreApplication>
+#include <QtCore/QDeadlineTimer>
+#include <QtCore/QDir>
+#include <QtCore/QHash>
+#include <QtCore/QProcess>
+#include <QtCore/QProcessEnvironment>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QSystemSemaphore>
+#include <QtCore/QThread>
+#include <QtCore/QXmlStreamReader>
+
+#include <atomic>
+#include <csignal>
#include <functional>
-#include <thread>
-
-#ifdef Q_CC_MSVC
-#define popen _popen
-#define QT_POPEN_READ "rb"
-#define pclose _pclose
+#if defined(Q_OS_WIN32)
+#include <process.h>
#else
-#define QT_POPEN_READ "r"
+#include <unistd.h>
#endif
-static auto junitChecker = [](const QByteArray &data) -> bool {
+using namespace Qt::StringLiterals;
+
+static bool checkJunit(const QByteArray &data) {
QXmlStreamReader reader{data};
while (!reader.atEnd()) {
reader.readNext();
- if (reader.isStartElement() && reader.name() == QStringLiteral("testcase") &&
- reader.attributes().value(QStringLiteral("result")).toString() == QStringLiteral("fail")) {
+
+ if (!reader.isStartElement())
+ continue;
+
+ if (reader.name() == "error"_L1)
return false;
+
+ const QString type = reader.attributes().value("type"_L1).toString();
+ if (reader.name() == "failure"_L1) {
+ if (type == "fail"_L1 || type == "xpass"_L1)
+ return false;
}
}
+
+ // Fail if there's an error after reading through all the xml output
+ return !reader.hasError();
+}
+
+static bool checkTxt(const QByteArray &data) {
+ if (data.indexOf("\nFAIL! : "_L1) >= 0)
+ return false;
+ if (data.indexOf("\nXPASS : "_L1) >= 0)
+ return false;
+ // Look for "********* Finished testing of tst_QTestName *********"
+ static const QRegularExpression testTail("\\*+ +Finished testing of .+ +\\*+"_L1);
+ return testTail.match(QLatin1StringView(data)).hasMatch();
+}
+
+static bool checkCsv(const QByteArray &data) {
+ // The csv format is only suitable for benchmarks,
+ // so this is not much useful to determine test failure/success.
+ // FIXME: warn the user early on about this.
+ Q_UNUSED(data);
return true;
-};
+}
+
+static bool checkXml(const QByteArray &data) {
+ QXmlStreamReader reader{data};
+ while (!reader.atEnd()) {
+ reader.readNext();
+ const QString type = reader.attributes().value("type"_L1).toString();
+ const bool isIncident = (reader.name() == "Incident"_L1);
+ if (reader.isStartElement() && isIncident) {
+ if (type == "fail"_L1 || type == "xpass"_L1)
+ return false;
+ }
+ }
+
+ // Fail if there's an error after reading through all the xml output
+ return !reader.hasError();
+}
+
+static bool checkLightxml(const QByteArray &data) {
+ // lightxml intentionally skips the root element, which technically makes it
+ // not valid XML. We'll add that ourselves for the purpose of validation.
+ QByteArray newData = data;
+ newData.prepend("<root>");
+ newData.append("</root>");
+ return checkXml(newData);
+}
+
+static bool checkTeamcity(const QByteArray &data) {
+ if (data.indexOf("' message='Failure! |[Loc: ") >= 0)
+ return false;
+ const QList<QByteArray> lines = data.trimmed().split('\n');
+ if (lines.isEmpty())
+ return false;
+ return lines.last().startsWith("##teamcity[testSuiteFinished "_L1);
+}
+
+static bool checkTap(const QByteArray &data) {
+ // This will still report blacklisted fails because QTest with TAP
+ // is not putting any data about that.
+ if (data.indexOf("\nnot ok ") >= 0)
+ return false;
+
+ static const QRegularExpression testTail("ok [0-9]* - cleanupTestCase\\(\\)"_L1);
+ return testTail.match(QLatin1StringView(data)).hasMatch();
+}
struct Options
{
bool helpRequested = false;
bool verbose = false;
bool skipAddInstallRoot = false;
- std::chrono::seconds timeout{300}; // 5minutes
+ int timeoutSecs = 600; // 10 minutes
QString buildPath;
- QString adbCommand{QStringLiteral("adb")};
+ QString adbCommand{"adb"_L1};
QString makeCommand;
QString package;
QString activity;
QStringList testArgsList;
QHash<QString, QString> outFiles;
- QString testArgs;
+ QStringList amStarttestArgs;
QString apkPath;
- QHash<QString, std::function<bool(const QByteArray &)>> checkFiles = {
- {QStringLiteral("txt"), [](const QByteArray &data) -> bool {
- return data.indexOf("\nFAIL! : ") < 0;
- }},
- {QStringLiteral("csv"), [](const QByteArray &/*data*/) -> bool {
- // It seems csv is broken
- return true;
- }},
- {QStringLiteral("xml"), [](const QByteArray &data) -> bool {
- QXmlStreamReader reader{data};
- while (!reader.atEnd()) {
- reader.readNext();
- if (reader.isStartElement() && reader.name() == QStringLiteral("Incident") &&
- reader.attributes().value(QStringLiteral("type")).toString() == QStringLiteral("fail")) {
- return false;
- }
- }
- return true;
- }},
- {QStringLiteral("lightxml"), [](const QByteArray &data) -> bool {
- return data.indexOf("\n<Incident type=\"fail\" ") < 0;
- }},
- {QStringLiteral("xunitxml"), junitChecker},
- {QStringLiteral("junitxml"), junitChecker},
- {QStringLiteral("teamcity"), [](const QByteArray &data) -> bool {
- return data.indexOf("' message='Failure! |[Loc: ") < 0;
- }},
- {QStringLiteral("tap"), [](const QByteArray &data) -> bool {
- return data.indexOf("\nnot ok ") < 0;
- }},
+ QString ndkStackPath;
+ bool showLogcatOutput = false;
+ const QHash<QString, std::function<bool(const QByteArray &)>> checkFiles = {
+ {"txt"_L1, checkTxt},
+ {"csv"_L1, checkCsv},
+ {"xml"_L1, checkXml},
+ {"lightxml"_L1, checkLightxml},
+ {"xunitxml"_L1, checkJunit},
+ {"junitxml"_L1, checkJunit},
+ {"teamcity"_L1, checkTeamcity},
+ {"tap"_L1, checkTap},
};
};
static Options g_options;
-static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = false)
+struct TestInfo
+{
+ int sdkVersion = -1;
+ int pid = -1;
+
+ std::atomic<bool> isPackageInstalled { false };
+ std::atomic<bool> isTestRunnerInterrupted { false };
+};
+
+static TestInfo g_testInfo;
+
+static bool execCommand(const QString &program, const QStringList &args,
+ QByteArray *output = nullptr, bool verbose = false)
{
- if (verbose)
- fprintf(stdout, "Execute %s.\n", command.toUtf8().constData());
- FILE *process = popen(command.toUtf8().constData(), QT_POPEN_READ);
+ const auto command = program + " "_L1 + args.join(u' ');
- if (!process) {
- fprintf(stderr, "Cannot execute command %s.\n", qPrintable(command));
+ if (verbose && g_options.verbose)
+ qDebug("Execute %s.", command.toUtf8().constData());
+
+ QProcess process;
+ process.start(program, args);
+ if (!process.waitForStarted()) {
+ qCritical("Cannot execute command %s.", qPrintable(command));
return false;
}
- char buffer[512];
- while (fgets(buffer, sizeof(buffer), process)) {
- if (output)
- output->append(buffer);
- if (verbose)
- fprintf(stdout, "%s", buffer);
- }
- return pclose(process) == 0;
-}
-// Copy-pasted from qmake/library/ioutil.cpp
-inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
-{
- for (int x = arg.length() - 1; x >= 0; --x) {
- ushort c = arg.unicode()[x].unicode();
- if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
- return true;
+ // If the command is not adb, for example, make or ninja, it can take more that
+ // QProcess::waitForFinished() 30 secs, so for that use a higher timeout.
+ const int FinishTimeout = program.endsWith("adb"_L1) ? 30000 : g_options.timeoutSecs * 1000;
+ if (!process.waitForFinished(FinishTimeout)) {
+ qCritical("Execution of command %s timed out.", qPrintable(command));
+ return false;
}
- return false;
-}
-static QString shellQuoteUnix(const QString &arg)
-{
- // Chars that should be quoted (TM). This includes:
- static const uchar iqm[] = {
- 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
- 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
- }; // 0-32 \'"$`<>|;&(){}*?#!~[]
+ const auto stdOut = process.readAllStandardOutput();
+ if (output)
+ output->append(stdOut);
- if (!arg.length())
- return QStringLiteral("\"\"");
+ if (verbose && g_options.verbose)
+ qDebug() << stdOut.constData();
- QString ret(arg);
- if (hasSpecialChars(ret, iqm)) {
- ret.replace(QLatin1Char('\''), QStringLiteral("'\\''"));
- ret.prepend(QLatin1Char('\''));
- ret.append(QLatin1Char('\''));
- }
- return ret;
+ return process.exitCode() == 0;
}
-static QString shellQuoteWin(const QString &arg)
+static bool execAdbCommand(const QStringList &args, QByteArray *output = nullptr,
+ bool verbose = true)
{
- // Chars that should be quoted (TM). This includes:
- // - control chars & space
- // - the shell meta chars "&()<>^|
- // - the potential separators ,;=
- static const uchar iqm[] = {
- 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
- 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
- };
-
- if (!arg.length())
- return QStringLiteral("\"\"");
-
- QString ret(arg);
- if (hasSpecialChars(ret, iqm)) {
- // Quotes are escaped and their preceding backslashes are doubled.
- // It's impossible to escape anything inside a quoted string on cmd
- // level, so the outer quoting must be "suspended".
- ret.replace(QRegularExpression(QStringLiteral("(\\\\*)\"")), QStringLiteral("\"\\1\\1\\^\"\""));
- // The argument must not end with a \ since this would be interpreted
- // as escaping the quote -- rather put the \ behind the quote: e.g.
- // rather use "foo"\ than "foo\"
- int i = ret.length();
- while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
- --i;
- ret.insert(i, QLatin1Char('"'));
- ret.prepend(QLatin1Char('"'));
- }
- return ret;
+ return execCommand(g_options.adbCommand, args, output, verbose);
}
-static QString shellQuote(const QString &arg)
+static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = true)
{
- if (QDir::separator() == QLatin1Char('\\'))
- return shellQuoteWin(arg);
- else
- return shellQuoteUnix(arg);
+ auto args = QProcess::splitCommand(command);
+ const auto program = args.first();
+ args.removeOne(program);
+ return execCommand(program, args, output, verbose);
}
static bool parseOptions()
@@ -206,43 +203,50 @@ static bool parseOptions()
int i = 1;
for (; i < arguments.size(); ++i) {
const QString &argument = arguments.at(i);
- if (argument.compare(QStringLiteral("--adb"), Qt::CaseInsensitive) == 0) {
+ if (argument.compare("--adb"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.adbCommand = arguments.at(++i);
- } else if (argument.compare(QStringLiteral("--path"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--path"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.buildPath = arguments.at(++i);
- } else if (argument.compare(QStringLiteral("--make"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--make"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.makeCommand = arguments.at(++i);
- } else if (argument.compare(QStringLiteral("--apk"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--apk"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.apkPath = arguments.at(++i);
- } else if (argument.compare(QStringLiteral("--activity"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--activity"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.activity = arguments.at(++i);
- } else if (argument.compare(QStringLiteral("--skip-install-root"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--skip-install-root"_L1, Qt::CaseInsensitive) == 0) {
g_options.skipAddInstallRoot = true;
- } else if (argument.compare(QStringLiteral("--timeout"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--show-logcat"_L1, Qt::CaseInsensitive) == 0) {
+ g_options.showLogcatOutput = true;
+ } else if (argument.compare("--ndk-stack"_L1, Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.ndkStackPath = arguments.at(++i);
+ } else if (argument.compare("--timeout"_L1, Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
- g_options.timeout = std::chrono::seconds{arguments.at(++i).toInt()};
- } else if (argument.compare(QStringLiteral("--help"), Qt::CaseInsensitive) == 0) {
+ g_options.timeoutSecs = arguments.at(++i).toInt();
+ } else if (argument.compare("--help"_L1, Qt::CaseInsensitive) == 0) {
g_options.helpRequested = true;
- } else if (argument.compare(QStringLiteral("--verbose"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--verbose"_L1, Qt::CaseInsensitive) == 0) {
g_options.verbose = true;
- } else if (argument.compare(QStringLiteral("--"), Qt::CaseInsensitive) == 0) {
+ } else if (argument.compare("--"_L1, Qt::CaseInsensitive) == 0) {
++i;
break;
} else {
@@ -257,17 +261,24 @@ static bool parseOptions()
QString serial = qEnvironmentVariable("ANDROID_DEVICE_SERIAL");
if (!serial.isEmpty())
- g_options.adbCommand += QStringLiteral(" -s %1").arg(serial);
+ g_options.adbCommand += " -s %1"_L1.arg(serial);
+
+ if (g_options.ndkStackPath.isEmpty()) {
+ const QString ndkPath = qEnvironmentVariable("ANDROID_NDK_ROOT");
+ const QString ndkStackPath = ndkPath + QDir::separator() + "ndk-stack"_L1;
+ if (QFile::exists(ndkStackPath))
+ g_options.ndkStackPath = ndkStackPath;
+ }
+
return true;
}
static void printHelp()
-{// "012345678901234567890123456789012345678901234567890123456789012345678901"
- fprintf(stderr, "Syntax: %s <options> -- [TESTARGS] \n"
+{
+ qWarning( "Syntax: %s <options> -- [TESTARGS] \n"
"\n"
- " Creates an Android package in a temp directory <destination> and\n"
- " runs it on the default emulator/device or on the one specified by\n"
- " \"ANDROID_DEVICE_SERIAL\" environment variable.\n"
+ " Runs an Android test on the default emulator/device or on the one\n"
+ " specified by \"ANDROID_DEVICE_SERIAL\" environment variable.\n"
"\n"
" Mandatory arguments:\n"
" --path <path>: The path where androiddeployqt builds the android package.\n"
@@ -277,7 +288,7 @@ static void printHelp()
"\n"
" Optional arguments:\n"
" --make <make cmd>: make command, needed to install the qt library.\n"
- " For Qt 5.14+ this can be \"make apk\".\n"
+ " For Qt 6, this can be \"cmake --build . --target <target>_make_apk\".\n"
"\n"
" --adb <adb cmd>: The Android ADB command. If missing the one from\n"
" $PATH will be used.\n"
@@ -285,15 +296,20 @@ static void printHelp()
" --activity <acitvity>: The Activity to run. If missing the first\n"
" activity from AndroidManifest.qml file will be used.\n"
"\n"
- " --timeout <seconds>: Timeout to run the test. Default is 5 minutes.\n"
+ " --timeout <seconds>: Timeout to run the test. Default is 10 minutes.\n"
"\n"
" --skip-install-root: Do not append INSTALL_ROOT=... to the make command.\n"
"\n"
+ " --show-logcat: Print Logcat output to stdout.\n"
+ "\n"
+ " --ndk-stack: Path to ndk-stack tool that symbolizes crash stacktraces.\n"
+ " By default, ANDROID_NDK_ROOT env var is used to deduce the tool path.\n"
+ "\n"
" -- Arguments that will be passed to the test application.\n"
"\n"
" --verbose: Prints out information during processing.\n"
"\n"
- " --help: Displays this information.\n\n",
+ " --help: Displays this information.\n",
qPrintable(QCoreApplication::arguments().at(0))
);
}
@@ -305,8 +321,8 @@ static QString packageNameFromAndroidManifest(const QString &androidManifestPath
QXmlStreamReader reader(&androidManifestXml);
while (!reader.atEnd()) {
reader.readNext();
- if (reader.isStartElement() && reader.name() == QStringLiteral("manifest"))
- return reader.attributes().value(QStringLiteral("package")).toString();
+ if (reader.isStartElement() && reader.name() == "manifest"_L1)
+ return reader.attributes().value("package"_L1).toString();
}
}
return {};
@@ -319,8 +335,8 @@ static QString activityFromAndroidManifest(const QString &androidManifestPath)
QXmlStreamReader reader(&androidManifestXml);
while (!reader.atEnd()) {
reader.readNext();
- if (reader.isStartElement() && reader.name() == QStringLiteral("activity"))
- return reader.attributes().value(QStringLiteral("android:name")).toString();
+ if (reader.isStartElement() && reader.name() == "activity"_L1)
+ return reader.attributes().value("android:name"_L1).toString();
}
}
return {};
@@ -329,26 +345,26 @@ static QString activityFromAndroidManifest(const QString &androidManifestPath)
static void setOutputFile(QString file, QString format)
{
if (file.isEmpty())
- file = QStringLiteral("-");
+ file = "-"_L1;
if (format.isEmpty())
- format = QStringLiteral("txt");
+ format = "txt"_L1;
g_options.outFiles[format] = file;
}
static bool parseTestArgs()
{
- QRegularExpression oldFormats{QStringLiteral("^-(txt|csv|xunitxml|junitxml|xml|lightxml|teamcity|tap)$")};
- QRegularExpression newLoggingFormat{QStringLiteral("^(.*),(txt|csv|xunitxml|junitxml|xml|lightxml|teamcity|tap)$")};
+ QRegularExpression oldFormats{"^-(txt|csv|xunitxml|junitxml|xml|lightxml|teamcity|tap)$"_L1};
+ QRegularExpression newLoggingFormat{"^(.*),(txt|csv|xunitxml|junitxml|xml|lightxml|teamcity|tap)$"_L1};
QString file;
QString logType;
- QString unhandledArgs;
+ QStringList unhandledArgs;
for (int i = 0; i < g_options.testArgsList.size(); ++i) {
const QString &arg = g_options.testArgsList[i].trimmed();
- if (arg == QStringLiteral("--"))
+ if (arg == "--"_L1)
continue;
- if (arg == QStringLiteral("-o")) {
+ if (arg == "-o"_L1) {
if (i >= g_options.testArgsList.size() - 1)
return false; // missing file argument
@@ -365,68 +381,157 @@ static bool parseTestArgs()
if (match.hasMatch()) {
logType = match.capturedTexts().at(1);
} else {
- unhandledArgs += QStringLiteral(" %1").arg(arg);
+ // Use triple literal quotes so that QProcess::splitCommand() in androidjnimain.cpp
+ // keeps quotes characters inside the string.
+ QString quotedArg = QString(arg).replace("\""_L1, "\\\"\\\"\\\""_L1);
+ // Escape single quotes so they don't interfere with the shell command,
+ // and so they get passed to the app as single quote inside the string.
+ quotedArg.replace("'"_L1, "\'"_L1);
+ // Add escaped double quote character so that args with spaces are treated as one.
+ unhandledArgs << " \\\"%1\\\""_L1.arg(quotedArg);
}
}
}
if (g_options.outFiles.isEmpty() || !file.isEmpty() || !logType.isEmpty())
setOutputFile(file, logType);
+ QString testAppArgs;
for (const auto &format : g_options.outFiles.keys())
- g_options.testArgs += QStringLiteral(" -o output.%1,%1").arg(format);
+ testAppArgs += "-o output.%1,%1 "_L1.arg(format);
+
+ testAppArgs += unhandledArgs.join(u' ').trimmed();
+ testAppArgs = "\"%1\""_L1.arg(testAppArgs.trimmed());
+ const QString activityName = "%1/%2"_L1.arg(g_options.package).arg(g_options.activity);
+
+ // Pass over any testlib env vars if set
+ QString testEnvVars;
+ const QStringList envVarsList = QProcessEnvironment::systemEnvironment().toStringList();
+ for (const QString &var : envVarsList) {
+ if (var.startsWith("QTEST_"_L1))
+ testEnvVars += "%1 "_L1.arg(var);
+ }
+
+ if (!testEnvVars.isEmpty()) {
+ testEnvVars = QString::fromUtf8(testEnvVars.trimmed().toUtf8().toBase64());
+ testEnvVars = "-e extraenvvars \"%4\""_L1.arg(testEnvVars);
+ }
+
+ g_options.amStarttestArgs = { "shell"_L1, "am"_L1, "start"_L1,
+ "-n"_L1, activityName,
+ "-e"_L1, "applicationArguments"_L1, testAppArgs,
+ testEnvVars
+ };
- g_options.testArgs += unhandledArgs;
- g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \\\"%1\\\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()),
- g_options.package,
- g_options.activity);
return true;
}
-static bool isRunning() {
+static bool obtainPid() {
QByteArray output;
- if (!execCommand(QStringLiteral("%1 shell \"ps | grep ' %2'\"").arg(g_options.adbCommand,
- shellQuote(g_options.package)), &output)) {
+ const QStringList psArgs = { "shell"_L1, "ps | grep ' %1'"_L1.arg(g_options.package) };
+ if (!execAdbCommand(psArgs, &output, false))
+ return false;
+ const QList<QByteArray> lines = output.split(u'\n');
+ if (lines.size() < 1)
return false;
+
+ QList<QByteArray> columns = lines.first().simplified().replace(u'\t', u' ').split(u' ');
+ if (columns.size() < 3)
+ return false;
+
+ if (g_testInfo.pid == -1) {
+ bool ok = false;
+ int pid = columns.at(1).toInt(&ok);
+ if (ok)
+ g_testInfo.pid = pid;
}
- return output.indexOf(QLatin1String(" " + g_options.package.toUtf8())) > -1;
+
+ return true;
}
-static bool waitToFinish()
+static bool isRunning() {
+ QByteArray output;
+ const QStringList psArgs = { "shell"_L1, "ps | grep ' %1'"_L1.arg(g_options.package) };
+ if (!execAdbCommand(psArgs, &output, false))
+ return false;
+
+ return output.indexOf(QLatin1StringView(" " + g_options.package.toUtf8())) > -1;
+}
+
+static void waitForStartedAndFinished()
{
- using clock = std::chrono::system_clock;
- auto start = clock::now();
- // wait to start
- while (!isRunning()) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if ((clock::now() - start) > std::chrono::seconds{10})
- return false;
- }
+ // wait to start and set PID
+ QDeadlineTimer startDeadline(10000);
+ do {
+ if (obtainPid())
+ break;
+ QThread::msleep(100);
+ } while (!startDeadline.hasExpired() && !g_testInfo.isTestRunnerInterrupted.load());
// Wait to finish
- while (isRunning()) {
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- if ((clock::now() - start) > g_options.timeout)
- return false;
- }
- return true;
+ QDeadlineTimer finishedDeadline(g_options.timeoutSecs * 1000);
+ do {
+ if (!isRunning())
+ break;
+ QThread::msleep(250);
+ } while (!finishedDeadline.hasExpired() && !g_testInfo.isTestRunnerInterrupted.load());
+
+ if (finishedDeadline.hasExpired())
+ qWarning() << "Timed out while waiting for the test to finish";
}
+static void obtainSdkVersion()
+{
+ // SDK version is necessary, as in SDK 23 pidof is broken, so we cannot obtain the pid.
+ // Also, Logcat cannot filter by pid in SDK 23, so we don't offer the --show-logcat option.
+ QByteArray output;
+ const QStringList versionArgs = { "shell"_L1, "getprop"_L1, "ro.build.version.sdk"_L1 };
+ execAdbCommand(versionArgs, &output, false);
+ bool ok = false;
+ int sdkVersion = output.toInt(&ok);
+ if (ok)
+ g_testInfo.sdkVersion = sdkVersion;
+ else
+ qCritical() << "Unable to obtain the SDK version of the target.";
+}
static bool pullFiles()
{
bool ret = true;
+ QByteArray userId;
+ // adb get-current-user command is available starting from API level 26.
+ if (g_testInfo.sdkVersion >= 26) {
+ const QStringList userIdArgs = {"shell"_L1, "cmd"_L1, "activity"_L1, "get-current-user"_L1};
+ if (!execAdbCommand(userIdArgs, &userId, false)) {
+ qCritical() << "Error: failed to retrieve the user ID";
+ return false;
+ }
+ } else {
+ userId = "0";
+ }
+
for (auto it = g_options.outFiles.constBegin(); it != g_options.outFiles.end(); ++it) {
+ // Get only stdout from cat and get rid of stderr and fail later if the output is empty
+ const QString outSuffix = it.key();
+ const QString catCmd = "cat files/output.%1 2> /dev/null"_L1.arg(outSuffix);
+ const QStringList fullCatArgs = { "shell"_L1, "run-as %1 --user %2 %3"_L1.arg(
+ g_options.package, QString::fromUtf8(userId.simplified()), catCmd) };
+
QByteArray output;
- if (!execCommand(QStringLiteral("%1 shell run-as %2 cat files/output.%3")
- .arg(g_options.adbCommand, g_options.package, it.key()), &output)) {
+ if (!execAdbCommand(fullCatArgs, &output, false)) {
+ qCritical() << "Error: failed to retrieve the test's output.%1 file."_L1.arg(outSuffix);
return false;
}
- auto checkerIt = g_options.checkFiles.find(it.key());
- ret = ret && checkerIt != g_options.checkFiles.end() && checkerIt.value()(output);
- if (it.value() == QStringLiteral("-")){
- fprintf(stdout, "%s", output.constData());
- fflush(stdout);
+
+ if (output.isEmpty()) {
+ qCritical() << "Error: the test's output.%1 is empty."_L1.arg(outSuffix);
+ return false;
+ }
+
+ auto checkerIt = g_options.checkFiles.find(outSuffix);
+ ret &= (checkerIt != g_options.checkFiles.end() && checkerIt.value()(output));
+ if (it.value() == "-"_L1) {
+ qDebug() << output.constData();
} else {
QFile out{it.value()};
if (!out.open(QIODevice::WriteOnly))
@@ -437,21 +542,171 @@ static bool pullFiles()
return ret;
}
-struct RunnerLocker
+void printLogcat(const QString &formattedTime)
{
- RunnerLocker()
- {
- runner.acquire();
+ QStringList logcatArgs = { "logcat"_L1 };
+ if (g_testInfo.sdkVersion <= 23 || g_testInfo.pid == -1)
+ logcatArgs << "-t"_L1 << formattedTime;
+ else
+ logcatArgs << "-d"_L1 << "--pid=%1"_L1.arg(QString::number(g_testInfo.pid));
+
+ QByteArray logcat;
+ if (!execAdbCommand(logcatArgs, &logcat, false)) {
+ qCritical() << "Error: failed to fetch logcat of the test";
+ return;
+ }
+
+ if (logcat.isEmpty()) {
+ qWarning() << "The retrieved logcat is empty";
+ return;
+ }
+
+ qDebug() << "****** Begin logcat output ******";
+ qDebug().noquote() << logcat;
+ qDebug() << "****** End logcat output ******";
+}
+
+static QString getDeviceABI()
+{
+ const QStringList abiArgs = { "shell"_L1, "getprop"_L1, "ro.product.cpu.abi"_L1 };
+ QByteArray abi;
+ if (!execAdbCommand(abiArgs, &abi, false)) {
+ qWarning() << "Warning: failed to get the device abi, fallback to first libs dir";
+ return {};
+ }
+
+ return QString::fromUtf8(abi.simplified());
+}
+
+void printLogcatCrashBuffer(const QString &formattedTime)
+{
+ bool useNdkStack = false;
+ auto libsPath = "%1/libs/"_L1.arg(g_options.buildPath);
+
+ if (!g_options.ndkStackPath.isEmpty()) {
+ QString abi = getDeviceABI();
+ if (abi.isEmpty()) {
+ QStringList subDirs = QDir(libsPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ if (!subDirs.isEmpty())
+ abi = subDirs.first();
+ }
+
+ if (!abi.isEmpty()) {
+ libsPath += abi;
+ useNdkStack = true;
+ } else {
+ qWarning() << "Warning: failed to get the libs abi, ndk-stack cannot be used.";
+ }
+ } else {
+ qWarning() << "Warning: ndk-stack path not provided and couldn't be deduced "
+ "using the ANDROID_NDK_ROOT environment variable.";
+ }
+
+ QProcess adbCrashProcess;
+ QProcess ndkStackProcess;
+
+ if (useNdkStack) {
+ adbCrashProcess.setStandardOutputProcess(&ndkStackProcess);
+ ndkStackProcess.start(g_options.ndkStackPath, { "-sym"_L1, libsPath });
+ }
+
+ const QStringList adbCrashArgs = { "logcat"_L1, "-b"_L1, "crash"_L1, "-t"_L1, formattedTime };
+ adbCrashProcess.start(g_options.adbCommand, adbCrashArgs);
+
+ if (!adbCrashProcess.waitForStarted()) {
+ qCritical() << "Error: failed to run adb logcat crash command.";
+ return;
+ }
+
+ if (useNdkStack && !ndkStackProcess.waitForStarted()) {
+ qCritical() << "Error: failed to run ndk-stack command.";
+ return;
+ }
+
+ if (!adbCrashProcess.waitForFinished()) {
+ qCritical() << "Error: adb command timed out.";
+ return;
}
- ~RunnerLocker()
+
+ if (useNdkStack && !ndkStackProcess.waitForFinished()) {
+ qCritical() << "Error: ndk-stack command timed out.";
+ return;
+ }
+
+ const QByteArray crash = useNdkStack ? ndkStackProcess.readAllStandardOutput()
+ : adbCrashProcess.readAllStandardOutput();
+ if (crash.isEmpty()) {
+ qWarning() << "The retrieved crash logcat is empty";
+ return;
+ }
+
+ qDebug() << "****** Begin logcat crash buffer output ******";
+ qDebug().noquote() << crash;
+ qDebug() << "****** End logcat crash buffer output ******";
+}
+
+static QString getCurrentTimeString()
+{
+ const QString timeFormat = (g_testInfo.sdkVersion <= 23) ?
+ "%m-%d %H:%M:%S.000"_L1 : "%Y-%m-%d %H:%M:%S.%3N"_L1;
+
+ QStringList dateArgs = { "shell"_L1, "date"_L1, "+'%1'"_L1.arg(timeFormat) };
+ QByteArray output;
+ if (!execAdbCommand(dateArgs, &output, false)) {
+ qWarning() << "Date/time adb command failed";
+ return {};
+ }
+
+ return QString::fromUtf8(output.simplified());
+}
+
+static bool uninstallTestPackage()
+{
+ return execAdbCommand({ "uninstall"_L1, g_options.package }, nullptr);
+}
+
+struct TestRunnerSystemSemaphore
+{
+ TestRunnerSystemSemaphore() { }
+ ~TestRunnerSystemSemaphore() { release(); }
+
+ void acquire() { isAcquired.store(semaphore.acquire()); }
+
+ void release()
{
- runner.release();
+ bool expected = true;
+ // NOTE: There's still could be tiny time gap between the compare_exchange_strong() call
+ // and release() call where the thread could be interrupted, if that's ever an issue,
+ // this code could be checked and improved further.
+ if (isAcquired.compare_exchange_strong(expected, false))
+ isAcquired.store(!semaphore.release());
}
- QSystemSemaphore runner{QStringLiteral("androidtestrunner"), 1, QSystemSemaphore::Open};
+
+ std::atomic<bool> isAcquired { false };
+ QSystemSemaphore semaphore { QSystemSemaphore::platformSafeKey(u"androidtestrunner"_s),
+ 1, QSystemSemaphore::Open };
};
+TestRunnerSystemSemaphore testRunnerLock;
+
+void sigHandler(int signal)
+{
+ std::signal(signal, SIG_DFL);
+ testRunnerLock.release();
+ // Ideally we shouldn't be doing such calls from a signal handler,
+ // and we can't use QSocketNotifier because this tool doesn't spin
+ // a main event loop. Since, there's no other alternative to do this,
+ // let's do the cleanup anyway.
+ if (!g_testInfo.isPackageInstalled.load())
+ _exit(-1);
+ g_testInfo.isTestRunnerInterrupted.store(true);
+}
+
int main(int argc, char *argv[])
{
+ std::signal(SIGINT, sigHandler);
+ std::signal(SIGTERM, sigHandler);
+
QCoreApplication a(argc, argv);
if (!parseOptions()) {
printHelp();
@@ -459,41 +714,33 @@ int main(int argc, char *argv[])
}
if (g_options.makeCommand.isEmpty()) {
- fprintf(stderr,
- "It is required to provide a make command with the \"--make\" parameter "
- "to generate the apk.\n");
+ qCritical() << "It is required to provide a make command with the \"--make\" parameter "
+ "to generate the apk.";
return 1;
}
if (!execCommand(g_options.makeCommand, nullptr, true)) {
if (!g_options.skipAddInstallRoot) {
// we need to run make INSTALL_ROOT=path install to install the application file(s) first
- if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install")
- .arg(g_options.makeCommand, QDir::toNativeSeparators(g_options.buildPath)), nullptr, g_options.verbose)) {
+ if (!execCommand("%1 INSTALL_ROOT=%2 install"_L1.arg(g_options.makeCommand,
+ QDir::toNativeSeparators(g_options.buildPath)), nullptr)) {
return 1;
}
} else {
- if (!execCommand(QStringLiteral("%1")
- .arg(g_options.makeCommand), nullptr, g_options.verbose)) {
+ if (!execCommand(g_options.makeCommand, nullptr))
return 1;
- }
}
}
if (!QFile::exists(g_options.apkPath)) {
- fprintf(stderr,
- "No apk \"%s\" found after running the make command. Check the provided path and "
- "the make command.\n",
- qPrintable(g_options.apkPath));
+ qCritical("No apk \"%s\" found after running the make command. "
+ "Check the provided path and the make command.",
+ qPrintable(g_options.apkPath));
return 1;
}
- RunnerLocker lock; // do not install or run packages while another test is running
- if (!execCommand(QStringLiteral("%1 install -r -g %2")
- .arg(g_options.adbCommand, g_options.apkPath), nullptr, g_options.verbose)) {
- return 1;
- }
+ obtainSdkVersion();
- QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml");
+ QString manifest = g_options.buildPath + "/AndroidManifest.xml"_L1;
g_options.package = packageNameFromAndroidManifest(manifest);
if (g_options.activity.isEmpty())
g_options.activity = activityFromAndroidManifest(manifest);
@@ -502,13 +749,42 @@ int main(int argc, char *argv[])
if (!parseTestArgs())
return 1;
+ // do not install or run packages while another test is running
+ testRunnerLock.acquire();
+
+ const QStringList installArgs = { "install"_L1, "-r"_L1, "-g"_L1, g_options.apkPath };
+ g_testInfo.isPackageInstalled.store(execAdbCommand(installArgs, nullptr));
+ if (!g_testInfo.isPackageInstalled)
+ return 1;
+
+ const QString formattedTime = getCurrentTimeString();
+
// start the tests
- bool res = execCommand(QStringLiteral("%1 %2").arg(g_options.adbCommand, g_options.testArgs),
- nullptr, g_options.verbose) && waitToFinish();
- if (res)
- res &= pullFiles();
- res &= execCommand(QStringLiteral("%1 uninstall %2").arg(g_options.adbCommand, g_options.package),
- nullptr, g_options.verbose);
- fflush(stdout);
- return res ? 0 : 1;
+ bool success = execAdbCommand(g_options.amStarttestArgs, nullptr);
+
+ waitForStartedAndFinished();
+
+ if (success) {
+ success &= pullFiles();
+ if (g_options.showLogcatOutput)
+ printLogcat(formattedTime);
+ }
+
+ // If we have a failure, attempt to print both logcat and the crash buffer which
+ // includes the crash stacktrace that is not included in the default logcat.
+ if (!success) {
+ printLogcat(formattedTime);
+ printLogcatCrashBuffer(formattedTime);
+ }
+
+ success &= uninstallTestPackage();
+
+ testRunnerLock.release();
+
+ if (g_testInfo.isTestRunnerInterrupted.load()) {
+ qCritical() << "The androidtestrunner was interrupted and the was test cleaned up.";
+ return 1;
+ }
+
+ return success ? 0 : 1;
}
diff --git a/src/tools/bootstrap/CMakeLists.txt b/src/tools/bootstrap/CMakeLists.txt
index 393ef7e1c2..93e826fa22 100644
--- a/src/tools/bootstrap/CMakeLists.txt
+++ b/src/tools/bootstrap/CMakeLists.txt
@@ -1,29 +1,27 @@
-# Generated from bootstrap.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## Bootstrap Module:
#####################################################################
-# special case begin
# The bootstrap library has a few manual tweaks compared to other
# libraries.
qt_add_library(Bootstrap STATIC)
-# special case end
+
+qt_internal_add_sync_header_dependencies(Bootstrap Core)
+
qt_internal_extend_target(Bootstrap
SOURCES
- ../../corelib/global/qendian.cpp
+ ../../corelib/global/qassert.cpp
../../corelib/global/qfloat16.cpp
- ../../corelib/global/qglobal.cpp
../../corelib/global/qlogging.cpp
../../corelib/global/qmalloc.cpp
- ../../corelib/global/qnumeric.cpp
- ../../corelib/global/qoperatingsystemversion.cpp
- ../../corelib/global/qrandom.cpp
+ ../../corelib/global/qtenvironmentvariables.cpp
../../corelib/io/qabstractfileengine.cpp
../../corelib/io/qbuffer.cpp
../../corelib/io/qdebug.cpp
../../corelib/io/qdir.cpp
- ../../corelib/io/qdiriterator.cpp
../../corelib/io/qfile.cpp
../../corelib/io/qfiledevice.cpp
../../corelib/io/qfileinfo.cpp
@@ -32,32 +30,14 @@ qt_internal_extend_target(Bootstrap
../../corelib/io/qfsfileengine.cpp
../../corelib/io/qfsfileengine_iterator.cpp
../../corelib/io/qiodevice.cpp
- ../../corelib/io/qipaddress.cpp
- ../../corelib/io/qloggingcategory.cpp
- ../../corelib/io/qloggingregistry.cpp
- ../../corelib/io/qresource.cpp
- ../../corelib/io/qsavefile.cpp
../../corelib/io/qstandardpaths.cpp
- ../../corelib/io/qtemporarydir.cpp
- ../../corelib/io/qtemporaryfile.cpp
- ../../corelib/io/qurl.cpp
- ../../corelib/io/qurlidna.cpp
- ../../corelib/io/qurlquery.cpp
- ../../corelib/io/qurlrecode.cpp
../../corelib/kernel/qcoreapplication.cpp
- ../../corelib/kernel/qcoreglobaldata.cpp
- ../../corelib/kernel/qiterable.cpp
- ../../corelib/kernel/qmetacontainer.cpp
../../corelib/kernel/qmetatype.cpp
- ../../corelib/kernel/qsharedmemory.cpp
../../corelib/kernel/qsystemerror.cpp
- ../../corelib/kernel/qsystemsemaphore.cpp
- ../../corelib/kernel/qvariant.cpp
../../corelib/plugin/quuid.cpp
../../corelib/serialization/qcborcommon.cpp
../../corelib/serialization/qcborstreamwriter.cpp
../../corelib/serialization/qcborvalue.cpp
- ../../corelib/serialization/qdatastream.cpp
../../corelib/serialization/qjsonarray.cpp
../../corelib/serialization/qjsoncbor.cpp
../../corelib/serialization/qjsondocument.cpp
@@ -66,12 +46,10 @@ qt_internal_extend_target(Bootstrap
../../corelib/serialization/qjsonvalue.cpp
../../corelib/serialization/qjsonwriter.cpp
../../corelib/serialization/qtextstream.cpp
- ../../corelib/serialization/qxmlstream.cpp
- ../../corelib/serialization/qxmlstreamgrammar.cpp
- ../../corelib/serialization/qxmlutils.cpp
../../corelib/text/qbytearray.cpp
../../corelib/text/qbytearraylist.cpp
../../corelib/text/qbytearraymatcher.cpp
+ ../../corelib/text/qlatin1stringmatcher.cpp
../../corelib/text/qlocale.cpp
../../corelib/text/qlocale_tools.cpp
../../corelib/text/qregularexpression.cpp
@@ -83,29 +61,24 @@ qt_internal_extend_target(Bootstrap
../../corelib/time/qcalendar.cpp
../../corelib/time/qdatetime.cpp
../../corelib/time/qgregoriancalendar.cpp
+ ../../corelib/time/qlocaltime.cpp
../../corelib/time/qromancalendar.cpp
+ ../../corelib/time/qtimezone.cpp
../../corelib/tools/qarraydata.cpp
- ../../corelib/tools/qbitarray.cpp
../../corelib/tools/qcommandlineoption.cpp
../../corelib/tools/qcommandlineparser.cpp
../../corelib/tools/qcryptographichash.cpp
../../corelib/tools/qhash.cpp
- ../../corelib/tools/qline.cpp
- ../../corelib/tools/qpoint.cpp
- ../../corelib/tools/qrect.cpp
../../corelib/tools/qringbuffer.cpp
- ../../corelib/tools/qsize.cpp
- ../../corelib/tools/qversionnumber.cpp
- ../../xml/dom/qdom.cpp
DEFINES
HAVE_CONFIG_H
QT_TYPESAFE_FLAGS
- PUBLIC_DEFINES # special case
- QT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} # special case
- QT_VERSION_MINOR=${PROJECT_VERSION_MINOR} # special case
- QT_VERSION_PATCH=${PROJECT_VERSION_PATCH} # special case
- QT_VERSION_STR="${PROJECT_VERSION}" # special case
- QT_USE_QSTRINGBUILDER # special case
+ PUBLIC_DEFINES
+ QT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
+ QT_VERSION_MINOR=${PROJECT_VERSION_MINOR}
+ QT_VERSION_PATCH=${PROJECT_VERSION_PATCH}
+ QT_VERSION_STR="${PROJECT_VERSION}"
+ QT_USE_QSTRINGBUILDER
QT_BOOTSTRAPPED
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
@@ -113,20 +86,14 @@ qt_internal_extend_target(Bootstrap
INCLUDE_DIRECTORIES
..
../../3rdparty/tinycbor/src
- PUBLIC_INCLUDE_DIRECTORIES # special case
- $<TARGET_PROPERTY:Core,INCLUDE_DIRECTORIES> # special case
- $<TARGET_PROPERTY:Xml,INCLUDE_DIRECTORIES> # special case
- PUBLIC_LIBRARIES # special case
- Qt::Platform # special case
+ PUBLIC_INCLUDE_DIRECTORIES
+ $<TARGET_PROPERTY:Core,INCLUDE_DIRECTORIES>
+ ../../corelib/global
+ PUBLIC_LIBRARIES
+ Qt::Platform
+ NO_UNITY_BUILD
)
-#### Keys ignored in scope 1:.:.:bootstrap.pro:<TRUE>:
-# INSTALLS = "lib"
-# MODULE_CONFIG = "gc_binaries"
-# MODULE_INCNAME = "QtCore" "QtXml"
-# _OPTION = "host_build"
-# lib.CONFIG = "dummy_install"
-
## Scopes:
#####################################################################
@@ -136,26 +103,24 @@ qt_internal_extend_target(Bootstrap CONDITION UNIX
../../corelib/io/qfilesystemiterator_unix.cpp
../../corelib/io/qfsfileengine_unix.cpp
../../corelib/kernel/qcore_unix.cpp
- ../../corelib/kernel/qsharedmemory_posix.cpp
- ../../corelib/kernel/qsharedmemory_systemv.cpp
- ../../corelib/kernel/qsharedmemory_unix.cpp
- ../../corelib/kernel/qsystemsemaphore_posix.cpp
- ../../corelib/kernel/qsystemsemaphore_systemv.cpp
- ../../corelib/kernel/qsystemsemaphore_unix.cpp
)
+if(APPLE)
+ set_source_files_properties(../../corelib/io/qfilesystemengine_unix.cpp PROPERTIES LANGUAGE OBJCXX)
+ qt_internal_extend_target(Bootstrap CONDITION
+ PUBLIC_LIBRARIES ${FWUniformTypeIdentifiers}
+ )
+endif()
qt_internal_extend_target(Bootstrap CONDITION WIN32
SOURCES
- ../../corelib/global/qoperatingsystemversion_win.cpp
../../corelib/io/qfilesystemengine_win.cpp
../../corelib/io/qfilesystemiterator_win.cpp
../../corelib/io/qfsfileengine_win.cpp
../../corelib/io/qstandardpaths_win.cpp
../../corelib/kernel/qcoreapplication_win.cpp
- ../../corelib/kernel/qsharedmemory_win.cpp
- ../../corelib/kernel/qsystemsemaphore_win.cpp
../../corelib/kernel/qwinregistry.cpp
../../corelib/plugin/qsystemlibrary.cpp
+ ../../corelib/kernel/qfunctions_win.cpp
PUBLIC_LIBRARIES
advapi32
netapi32
@@ -166,10 +131,12 @@ qt_internal_extend_target(Bootstrap CONDITION WIN32
qt_internal_extend_target(Bootstrap CONDITION APPLE
SOURCES
+ ../../corelib/global/qoperatingsystemversion.cpp
../../corelib/global/qoperatingsystemversion_darwin.mm
../../corelib/kernel/qcore_foundation.mm
../../corelib/kernel/qcore_mac.mm
../../corelib/kernel/qcoreapplication_mac.cpp
+ ../../corelib/tools/qversionnumber.cpp
PUBLIC_LIBRARIES
${FWFoundation}
)
@@ -197,6 +164,7 @@ qt_internal_extend_target(Bootstrap CONDITION CMAKE_CROSSCOMPILING OR NOT QT_FEA
../../3rdparty/pcre2/src/pcre2.h
../../3rdparty/pcre2/src/pcre2_auto_possess.c
../../3rdparty/pcre2/src/pcre2_chartables.c
+ ../../3rdparty/pcre2/src/pcre2_chkdint.c
../../3rdparty/pcre2/src/pcre2_compile.c
../../3rdparty/pcre2/src/pcre2_config.c
../../3rdparty/pcre2/src/pcre2_context.c
@@ -229,7 +197,7 @@ qt_internal_extend_target(Bootstrap CONDITION CMAKE_CROSSCOMPILING OR NOT QT_FEA
DEFINES
PCRE2_CODE_UNIT_WIDTH=16
PCRE2_DISABLE_JIT
- PUBLIC_INCLUDE_DIRECTORIES # special case
+ PUBLIC_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/pcre2/src>
)
@@ -243,41 +211,11 @@ qt_internal_extend_target(Bootstrap CONDITION QT_FEATURE_system_pcre2 AND NOT CM
WrapPCRE2::WrapPCRE2
)
-qt_internal_extend_target(Bootstrap CONDITION CMAKE_CROSSCOMPILING OR NOT QT_FEATURE_system_zlib
- SOURCES
- ../../3rdparty/zlib/src/adler32.c
- ../../3rdparty/zlib/src/compress.c
- ../../3rdparty/zlib/src/crc32.c
- ../../3rdparty/zlib/src/deflate.c
- ../../3rdparty/zlib/src/gzclose.c
- ../../3rdparty/zlib/src/gzlib.c
- ../../3rdparty/zlib/src/gzread.c
- ../../3rdparty/zlib/src/gzwrite.c
- ../../3rdparty/zlib/src/infback.c
- ../../3rdparty/zlib/src/inffast.c
- ../../3rdparty/zlib/src/inflate.c
- ../../3rdparty/zlib/src/inftrees.c
- ../../3rdparty/zlib/src/trees.c
- ../../3rdparty/zlib/src/uncompr.c
- ../../3rdparty/zlib/src/zutil.c
- INCLUDE_DIRECTORIES
- ../../3rdparty/zlib/src
-)
-
-qt_internal_extend_target(Bootstrap CONDITION QT_FEATURE_system_zlib AND NOT CMAKE_CROSSCOMPILING
- LIBRARIES
- WrapZLIB::WrapZLIB
-)
-
qt_internal_extend_target(Bootstrap CONDITION MINGW AND WIN32
PUBLIC_LIBRARIES
uuid
)
-#### Keys ignored in scope 22:.:../../3rdparty/pcre2:../../3rdparty/pcre2/pcre2.pri:QT_FEATURE_intelcet:
-# QMAKE_CFLAGS = "$$QMAKE_CFLAGS_SHSTK"
-
-# special case begin
target_link_libraries(Bootstrap PRIVATE PlatformCommonInternal)
qt_internal_apply_gc_binaries(Bootstrap PUBLIC)
set_target_properties(Bootstrap PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF)
@@ -286,6 +224,9 @@ qt_set_msvc_cplusplus_options(Bootstrap PUBLIC)
qt_set_common_target_properties(Bootstrap)
qt_internal_apply_intel_cet(Bootstrap PUBLIC)
+if(WARNINGS_ARE_ERRORS)
+ qt_internal_set_warnings_are_errors_flags(Bootstrap PRIVATE)
+endif()
qt_internal_extend_target(Bootstrap CONDITION MSVC
DEFINES
_CRT_SECURE_NO_WARNINGS
@@ -295,4 +236,3 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.20.0" AND QT_FEATURE_debug_and_release
set_property(TARGET Bootstrap
PROPERTY EXCLUDE_FROM_ALL "$<NOT:$<CONFIG:${QT_MULTI_CONFIG_FIRST_CONFIG}>>")
endif()
-# special case end
diff --git a/src/tools/cmake_automoc_parser/CMakeLists.txt b/src/tools/cmake_automoc_parser/CMakeLists.txt
index b5c4aba89c..a58c9c9ff1 100644
--- a/src/tools/cmake_automoc_parser/CMakeLists.txt
+++ b/src/tools/cmake_automoc_parser/CMakeLists.txt
@@ -1,20 +1,24 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#####################################################################
## moc Tool:
#####################################################################
qt_get_tool_target_name(target_name cmake_automoc_parser)
qt_internal_add_tool(${target_name}
- BOOTSTRAP
+ CORE_LIBRARY Bootstrap
+ TARGET_DESCRIPTION "Qt CMake AUTOMOC Parser"
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Core # special case
+ TOOLS_TARGET Core
SOURCES
main.cpp
DEFINES
QT_MOC
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_FROM_BYTEARRAY
- QT_NO_COMPRESS
QT_NO_FOREACH
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
)
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/cmake_automoc_parser/main.cpp b/src/tools/cmake_automoc_parser/main.cpp
index 6d0214638e..de484b184b 100644
--- a/src/tools/cmake_automoc_parser/main.cpp
+++ b/src/tools/cmake_automoc_parser/main.cpp
@@ -1,35 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qglobal.h>
#include <cstdio>
#include <cstdlib>
+#include <limits>
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
@@ -46,9 +22,12 @@
#include <qset.h>
#include <qstring.h>
#include <qstack.h>
+#include <qdatastream.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
using AutoGenHeaderMap = QMap<QString, QString>;
using AutoGenSourcesList = QList<QString>;
@@ -73,9 +52,9 @@ static bool readAutogenInfoJson(AutoGenHeaderMap &headers, AutoGenSourcesList &s
}
QJsonObject rootObject = doc.object();
- QJsonValue headersValue = rootObject.value(QLatin1String("HEADERS"));
- QJsonValue sourcesValue = rootObject.value(QLatin1String("SOURCES"));
- QJsonValue headerExtValue = rootObject.value(QLatin1String("HEADER_EXTENSIONS"));
+ QJsonValue headersValue = rootObject.value("HEADERS"_L1);
+ QJsonValue sourcesValue = rootObject.value("SOURCES"_L1);
+ QJsonValue headerExtValue = rootObject.value("HEADER_EXTENSIONS"_L1);
if (!headersValue.isArray() || !sourcesValue.isArray() || !headerExtValue.isArray()) {
fprintf(stderr,
@@ -152,20 +131,22 @@ static bool readParseCache(ParseCacheMap &entries, const QString &parseCacheFile
// ....
QTextStream textStream(&file);
- const QString mmcKey = QString(QLatin1String(" mmc:"));
- const QString miuKey = QString(QLatin1String(" miu:"));
- const QString uicKey = QString(QLatin1String(" uic:"));
- const QString midKey = QString(QLatin1String(" mid:"));
- const QString mdpKey = QString(QLatin1String(" mdp:"));
- const QString udpKey = QString(QLatin1String(" udp:"));
+ const QString mmcKey = QString(" mmc:"_L1);
+ const QString miuKey = QString(" miu:"_L1);
+ const QString uicKey = QString(" uic:"_L1);
+ const QString midKey = QString(" mid:"_L1);
+ const QString mdpKey = QString(" mdp:"_L1);
+ const QString udpKey = QString(" udp:"_L1);
QString line;
bool mmc_key_found = false;
while (textStream.readLineInto(&line)) {
- if (!line.startsWith(QLatin1Char(' '))) {
+ if (!line.startsWith(u' ')) {
if (!mocEntries.isEmpty() || mmc_key_found || !mocIncludes.isEmpty()) {
entries.insert(source,
ParseCacheEntry { std::move(mocEntries), std::move(mocIncludes) });
source.clear();
+ mocEntries = {};
+ mocIncludes = {};
mmc_key_found = false;
}
source = line;
@@ -194,38 +175,46 @@ static bool readParseCache(ParseCacheMap &entries, const QString &parseCacheFile
return true;
}
-static bool readJsonFiles(QList<QString> &entries, const QString &filePath)
+static bool writeJsonFiles(const QList<QString> &fileList, const QString &fileListFilePath,
+ const QString &timestampFilePath)
{
-
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- fprintf(stderr, "Could not open: %s\n", qPrintable(filePath));
+ QFile timestampFile(timestampFilePath);
+ if (!timestampFile.open(QIODevice::ReadWrite)) {
+ fprintf(stderr, "Could not open: %s\n", qPrintable(timestampFilePath));
return false;
}
- QTextStream textStream(&file);
- QString line;
- while (textStream.readLineInto(&line)) {
- entries.push_back(line);
+ qint64 timestamp = std::numeric_limits<qint64>::min();
+ if (timestampFile.size() == sizeof(timestamp))
+ timestampFile.read(reinterpret_cast<char *>(&timestamp), sizeof(timestamp));
+
+ // Check if any of the metatype json files produced by automoc is newer than the last file
+ // processed by cmake_automoc parser
+ for (const auto &jsonFile : fileList) {
+ const qint64 jsonFileLastModified =
+ QFileInfo(jsonFile).lastModified(QTimeZone::UTC).toMSecsSinceEpoch();
+ if (jsonFileLastModified > timestamp) {
+ timestamp = jsonFileLastModified;
+ }
}
- file.close();
- return true;
-}
-static bool writeJsonFiles(const QList<QString> &fileList, const QString &fileListFilePath)
-{
- QFile file(fileListFilePath);
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- fprintf(stderr, "Could not open: %s\n", qPrintable(fileListFilePath));
- return false;
- }
+ if (timestamp != std::numeric_limits<qint64>::min() || !QFile::exists(fileListFilePath)) {
+ QFile file(fileListFilePath);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ fprintf(stderr, "Could not open: %s\n", qPrintable(fileListFilePath));
+ return false;
+ }
- QTextStream textStream(&file);
- for (const auto &file : fileList) {
- textStream << file << Qt::endl;
- }
+ QTextStream textStream(&file);
+ for (const auto &jsonFile : fileList) {
+ textStream << jsonFile << Qt::endl;
+ }
+ textStream.flush();
- file.close();
+ // Update the timestamp according the newest json file timestamp.
+ timestampFile.resize(0);
+ timestampFile.write(reinterpret_cast<char *>(&timestamp), sizeof(timestamp));
+ }
return true;
}
@@ -270,6 +259,13 @@ int main(int argc, char **argv)
QStringLiteral("Set this option when using CMake with a multi-config generator"));
parser.addOption(isMultiConfigOption);
+ QCommandLineOption timestampFilePathOption(QStringLiteral("timestamp-file-path"));
+ timestampFilePathOption.setDescription(
+ QStringLiteral("The path to a timestamp file that determines whether the output"
+ " file needs to be updated."));
+ timestampFilePathOption.setValueName(QStringLiteral("timestamp file"));
+ parser.addOption(timestampFilePathOption);
+
QStringList arguments = QCoreApplication::arguments();
parser.process(arguments);
@@ -323,7 +319,7 @@ int main(int argc, char **argv)
const QString base = fileInfo.path() + fileInfo.completeBaseName();
// 1a) erase header
for (const auto &ext : headerExtList) {
- const QString headerPath = base + QLatin1Char('.') + ext;
+ const QString headerPath = base + u'.' + ext;
auto it = autoGenHeaders.find(headerPath);
if (it != autoGenHeaders.end()) {
autoGenHeaders.erase(it);
@@ -331,19 +327,18 @@ int main(int argc, char **argv)
}
}
// Add extra moc files
- for (const auto &mocFile : it.value().mocFiles) {
- jsonFileList.push_back(dir.filePath(mocFile) + QLatin1String(".json"));
- }
+ for (const auto &mocFile : it.value().mocFiles)
+ jsonFileList.push_back(dir.filePath(mocFile) + ".json"_L1);
// Add main moc files
for (const auto &mocFile : it.value().mocIncludes) {
- jsonFileList.push_back(dir.filePath(mocFile) + QLatin1String(".json"));
+ jsonFileList.push_back(dir.filePath(mocFile) + ".json"_L1);
// 1b) Locate this header and delete it
constexpr int mocKeyLen = 4; // length of "moc_"
const QString headerBaseName =
QFileInfo(mocFile.right(mocFile.size() - mocKeyLen)).completeBaseName();
bool breakFree = false;
for (auto &ext : headerExtList) {
- const QString headerSuffix = headerBaseName + QLatin1Char('.') + ext;
+ const QString headerSuffix = headerBaseName + u'.' + ext;
for (auto it = autoGenHeaders.begin(); it != autoGenHeaders.end(); ++it) {
if (it.key().endsWith(headerSuffix)
&& QFileInfo(it.key()).completeBaseName() == headerBaseName) {
@@ -369,8 +364,7 @@ int main(int argc, char **argv)
const QString pathPrefix = !isMultiConfig
? QStringLiteral("../")
: QString();
- const QString jsonPath =
- dir.filePath(pathPrefix + mapIt.value() + QLatin1String(".json"));
+ const QString jsonPath = dir.filePath(pathPrefix + mapIt.value() + ".json"_L1);
jsonFileList.push_back(jsonPath);
}
@@ -378,19 +372,9 @@ int main(int argc, char **argv)
jsonFileList.sort();
// Read Previous file list (if any)
- const QString fileListFilePath = parser.value(outputFileOption);
- QList<QString> previousList;
- QFile prev_file(fileListFilePath);
-
- // Only try to open file if it exists to avoid error messages
- if (prev_file.exists()) {
- (void)readJsonFiles(previousList, fileListFilePath);
- }
-
- if (previousList != jsonFileList || !QFile(fileListFilePath).exists()) {
- if (!writeJsonFiles(jsonFileList, fileListFilePath)) {
- return EXIT_FAILURE;
- }
+ if (!writeJsonFiles(jsonFileList, parser.value(outputFileOption),
+ parser.value(timestampFilePathOption))) {
+ return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/src/tools/configure.cmake b/src/tools/configure.cmake
new file mode 100644
index 0000000000..f813b727ba
--- /dev/null
+++ b/src/tools/configure.cmake
@@ -0,0 +1,35 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_feature("androiddeployqt" PRIVATE
+ SECTION "Deployment"
+ LABEL "Android deployment tool"
+ PURPOSE "The Android deployment tool automates the process of creating Android packages."
+ CONDITION NOT CMAKE_CROSSCOMPILING AND QT_FEATURE_regularexpression AND QT_FEATURE_settings)
+
+qt_feature("macdeployqt" PRIVATE
+ SECTION "Deployment"
+ LABEL "macOS deployment tool"
+ PURPOSE "The Mac deployment tool automates the process of creating a deployable application bundle that contains the Qt libraries as private frameworks."
+ AUTODETECT CMAKE_HOST_APPLE
+ CONDITION MACOS AND QT_FEATURE_thread)
+
+qt_feature("windeployqt" PRIVATE
+ SECTION "Deployment"
+ LABEL "Windows deployment tool"
+ PURPOSE "The Windows deployment tool is designed to automate the process of creating a deployable folder containing the Qt-related dependencies (libraries, QML imports, plugins, and translations) required to run the application from that folder. The folder can be easily bundled into an installation package."
+ AUTODETECT CMAKE_HOST_WIN32
+ CONDITION WIN32)
+
+qt_feature("qmake" PRIVATE
+ PURPOSE "The qmake tool helps simplify the build process for development projects across different platforms."
+ CONDITION QT_FEATURE_settings AND QT_FEATURE_alloca AND
+ (QT_FEATURE_alloca_malloc_h OR NOT WIN32) AND QT_FEATURE_cborstreamwriter AND
+ QT_FEATURE_datestring AND QT_FEATURE_regularexpression AND QT_FEATURE_temporaryfile)
+
+qt_configure_add_summary_section(NAME "Core tools")
+qt_configure_add_summary_entry(ARGS "androiddeployqt")
+qt_configure_add_summary_entry(ARGS "macdeployqt")
+qt_configure_add_summary_entry(ARGS "windeployqt")
+qt_configure_add_summary_entry(ARGS "qmake")
+qt_configure_end_summary_section()
diff --git a/src/tools/macdeployqt/CMakeLists.txt b/src/tools/macdeployqt/CMakeLists.txt
new file mode 100644
index 0000000000..2e01ec90b4
--- /dev/null
+++ b/src/tools/macdeployqt/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_FEATURE_macdeployqt)
+ return()
+endif()
+add_subdirectory(macdeployqt)
diff --git a/src/tools/macdeployqt/macdeployqt/CMakeLists.txt b/src/tools/macdeployqt/macdeployqt/CMakeLists.txt
new file mode 100644
index 0000000000..6cd66adaa7
--- /dev/null
+++ b/src/tools/macdeployqt/macdeployqt/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## macdeployqt Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name macdeployqt)
+qt_internal_add_tool(${target_name}
+ TOOLS_TARGET Core
+ USER_FACING
+ INSTALL_VERSIONED_LINK
+ TARGET_DESCRIPTION "Qt macOS Deployment Tool"
+ SOURCES
+ ../shared/shared.cpp
+ main.cpp
+ DEFINES
+ QT_NO_FOREACH
+ LIBRARIES
+ ${FWCoreFoundation}
+)
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/macdeployqt/macdeployqt/main.cpp b/src/tools/macdeployqt/macdeployqt/main.cpp
new file mode 100644
index 0000000000..8b6c476fa5
--- /dev/null
+++ b/src/tools/macdeployqt/macdeployqt/main.cpp
@@ -0,0 +1,253 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include <QCoreApplication>
+#include <QDir>
+#include <QLibraryInfo>
+
+#include "../shared/shared.h"
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QString appBundlePath;
+ if (argc > 1)
+ appBundlePath = QString::fromLocal8Bit(argv[1]);
+
+ if (argc < 2 || appBundlePath.startsWith("-")) {
+ qDebug() << "Usage: macdeployqt app-bundle [options]";
+ qDebug() << "";
+ qDebug() << "Options:";
+ qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
+ qDebug() << " -no-plugins : Skip plugin deployment";
+ qDebug() << " -dmg : Create a .dmg disk image";
+ qDebug() << " -no-strip : Don't run 'strip' on the binaries";
+ qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
+ qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
+ qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
+ qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
+ qDebug() << " -always-overwrite : Copy files even if the target file exists";
+ qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables";
+ qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
+ qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
+ qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
+ qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
+ qDebug() << " -libpath=<path> : Add the given path to the library search path";
+ qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
+ qDebug() << "";
+ qDebug() << "macdeployqt takes an application bundle as input and makes it";
+ qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
+ qDebug() << "the application uses.";
+ qDebug() << "";
+ qDebug() << "Plugins related to a framework are copied in with the";
+ qDebug() << "framework. The accessibility, image formats, and text codec";
+ qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
+ qDebug() << "";
+ qDebug() << "Qt plugins may use private API and will cause the app to be";
+ qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning";
+ qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
+ qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
+ qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
+ qDebug() << "";
+ qDebug() << "See the \"Deploying Applications on OS X\" topic in the";
+ qDebug() << "documentation for more information about deployment on OS X.";
+
+ return 1;
+ }
+
+ appBundlePath = QDir::cleanPath(appBundlePath);
+
+ if (!QDir(appBundlePath).exists()) {
+ qDebug() << "Error: Could not find app bundle" << appBundlePath;
+ return 1;
+ }
+
+ bool plugins = true;
+ bool dmg = false;
+ QByteArray filesystem("HFS+");
+ bool useDebugLibs = false;
+ extern bool runStripEnabled;
+ extern bool alwaysOwerwriteEnabled;
+ extern QStringList librarySearchPath;
+ QStringList additionalExecutables;
+ bool qmldirArgumentUsed = false;
+ QStringList qmlDirs;
+ QStringList qmlImportPaths;
+ extern bool runCodesign;
+ extern QString codesignIdentiy;
+ extern bool hardenedRuntime;
+ extern bool appstoreCompliant;
+ extern bool deployFramework;
+ extern bool secureTimestamp;
+
+ for (int i = 2; i < argc; ++i) {
+ QByteArray argument = QByteArray(argv[i]);
+ if (argument == QByteArray("-no-plugins")) {
+ LogDebug() << "Argument found:" << argument;
+ plugins = false;
+ } else if (argument == QByteArray("-dmg")) {
+ LogDebug() << "Argument found:" << argument;
+ dmg = true;
+ } else if (argument == QByteArray("-no-strip")) {
+ LogDebug() << "Argument found:" << argument;
+ runStripEnabled = false;
+ } else if (argument == QByteArray("-use-debug-libs")) {
+ LogDebug() << "Argument found:" << argument;
+ useDebugLibs = true;
+ runStripEnabled = false;
+ } else if (argument.startsWith(QByteArray("-verbose"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf("=");
+ bool ok = false;
+ int number = argument.mid(index+1).toInt(&ok);
+ if (!ok)
+ LogError() << "Could not parse verbose level";
+ else
+ logLevel = number;
+ } else if (argument.startsWith(QByteArray("-executable"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf('=');
+ if (index == -1)
+ LogError() << "Missing executable path";
+ else
+ additionalExecutables << argument.mid(index+1);
+ } else if (argument.startsWith(QByteArray("-qmldir"))) {
+ LogDebug() << "Argument found:" << argument;
+ qmldirArgumentUsed = true;
+ int index = argument.indexOf('=');
+ if (index == -1)
+ LogError() << "Missing qml directory path";
+ else
+ qmlDirs << argument.mid(index+1);
+ } else if (argument.startsWith(QByteArray("-qmlimport"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf('=');
+ if (index == -1)
+ LogError() << "Missing qml import path";
+ else
+ qmlImportPaths << argument.mid(index+1);
+ } else if (argument.startsWith(QByteArray("-libpath"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf('=');
+ if (index == -1)
+ LogError() << "Missing library search path";
+ else
+ librarySearchPath << argument.mid(index+1);
+ } else if (argument == QByteArray("-always-overwrite")) {
+ LogDebug() << "Argument found:" << argument;
+ alwaysOwerwriteEnabled = true;
+ } else if (argument.startsWith(QByteArray("-codesign"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf("=");
+ if (index < 0 || index >= argument.size()) {
+ LogError() << "Missing code signing identity";
+ } else {
+ runCodesign = true;
+ codesignIdentiy = argument.mid(index+1);
+ }
+ } else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf("=");
+ if (index < 0 || index >= argument.size()) {
+ LogError() << "Missing code signing identity";
+ } else {
+ runCodesign = true;
+ hardenedRuntime = true;
+ secureTimestamp = true;
+ codesignIdentiy = argument.mid(index+1);
+ }
+ } else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
+ LogDebug() << "Argument found:" << argument;
+ hardenedRuntime = true;
+ } else if (argument.startsWith(QByteArray("-timestamp"))) {
+ LogDebug() << "Argument found:" << argument;
+ secureTimestamp = true;
+ } else if (argument == QByteArray("-appstore-compliant")) {
+ LogDebug() << "Argument found:" << argument;
+ appstoreCompliant = true;
+
+ // Undocumented option, may not work as intended
+ } else if (argument == QByteArray("-deploy-framework")) {
+ LogDebug() << "Argument found:" << argument;
+ deployFramework = true;
+
+ } else if (argument.startsWith(QByteArray("-fs"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf('=');
+ if (index == -1)
+ LogError() << "Missing filesystem type";
+ else
+ filesystem = argument.mid(index+1);
+ } else if (argument.startsWith("-")) {
+ LogError() << "Unknown argument" << argument << "\n";
+ return 1;
+ }
+ }
+
+ DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
+
+ if (deploymentInfo.isDebug)
+ useDebugLibs = true;
+
+ if (deployFramework && deploymentInfo.isFramework)
+ fixupFramework(appBundlePath);
+
+ // Convenience: Look for .qml files in the current directory if no -qmldir specified.
+ if (qmlDirs.isEmpty()) {
+ QDir dir;
+ if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
+ qmlDirs += QStringLiteral(".");
+ }
+ }
+
+ if (!qmlDirs.isEmpty()) {
+ bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
+ if (!ok && qmldirArgumentUsed)
+ return 1; // exit if the user explicitly asked for qml import deployment
+
+ // Update deploymentInfo.deployedFrameworks - the QML imports
+ // may have brought in extra frameworks as dependencies.
+ deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
+ deploymentInfo.deployedFrameworks =
+ QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
+ deploymentInfo.deployedFrameworks.end()).values();
+ }
+
+ // Handle plugins
+ if (plugins) {
+ // Set the plugins search directory
+ deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
+
+ // Sanity checks
+ if (deploymentInfo.pluginPath.isEmpty()) {
+ LogError() << "Missing Qt plugins path\n";
+ return 1;
+ }
+
+ if (!QDir(deploymentInfo.pluginPath).exists()) {
+ LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
+ return 1;
+ }
+
+ // Deploy plugins
+ Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
+ if (!deploymentInfo.pluginPath.isEmpty()) {
+ LogNormal();
+ deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
+ createQtConf(appBundlePath);
+ }
+ }
+
+ if (runStripEnabled)
+ stripAppBinary(appBundlePath);
+
+ if (runCodesign)
+ codesign(codesignIdentiy, appBundlePath);
+
+ if (dmg) {
+ LogNormal();
+ createDiskImage(appBundlePath, filesystem);
+ }
+
+ return 0;
+}
diff --git a/src/tools/macdeployqt/shared/shared.cpp b/src/tools/macdeployqt/shared/shared.cpp
new file mode 100644
index 0000000000..6ff269b36d
--- /dev/null
+++ b/src/tools/macdeployqt/shared/shared.cpp
@@ -0,0 +1,1606 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include <QCoreApplication>
+#include <QString>
+#include <QStringList>
+#include <QDebug>
+#include <iostream>
+#include <utility>
+#include <QProcess>
+#include <QDir>
+#include <QSet>
+#include <QVariant>
+#include <QVariantMap>
+#include <QStack>
+#include <QDirIterator>
+#include <QLibraryInfo>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <QRegularExpression>
+#include "shared.h"
+
+#ifdef Q_OS_DARWIN
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+bool runStripEnabled = true;
+bool alwaysOwerwriteEnabled = false;
+bool runCodesign = false;
+QStringList librarySearchPath;
+QString codesignIdentiy;
+QString extraEntitlements;
+bool hardenedRuntime = false;
+bool secureTimestamp = false;
+bool appstoreCompliant = false;
+int logLevel = 1;
+bool deployFramework = false;
+
+using std::cout;
+using std::endl;
+using namespace Qt::StringLiterals;
+
+bool operator==(const FrameworkInfo &a, const FrameworkInfo &b)
+{
+ return ((a.frameworkPath == b.frameworkPath) && (a.binaryPath == b.binaryPath));
+}
+
+QDebug operator<<(QDebug debug, const FrameworkInfo &info)
+{
+ debug << "Framework name" << info.frameworkName << "\n";
+ debug << "Framework directory" << info.frameworkDirectory << "\n";
+ debug << "Framework path" << info.frameworkPath << "\n";
+ debug << "Binary directory" << info.binaryDirectory << "\n";
+ debug << "Binary name" << info.binaryName << "\n";
+ debug << "Binary path" << info.binaryPath << "\n";
+ debug << "Version" << info.version << "\n";
+ debug << "Install name" << info.installName << "\n";
+ debug << "Deployed install name" << info.deployedInstallName << "\n";
+ debug << "Source file Path" << info.sourceFilePath << "\n";
+ debug << "Framework Destination Directory (relative to bundle)" << info.frameworkDestinationDirectory << "\n";
+ debug << "Binary Destination Directory (relative to bundle)" << info.binaryDestinationDirectory << "\n";
+
+ return debug;
+}
+
+const QString bundleFrameworkDirectory = "Contents/Frameworks";
+
+inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info)
+{
+ debug << "Application bundle path" << info.path << "\n";
+ debug << "Binary path" << info.binaryPath << "\n";
+ debug << "Additional libraries" << info.libraryPaths << "\n";
+ return debug;
+}
+
+bool copyFilePrintStatus(const QString &from, const QString &to)
+{
+ if (QFile::exists(to)) {
+ if (alwaysOwerwriteEnabled) {
+ QFile(to).remove();
+ } else {
+ qDebug() << "File exists, skip copy:" << to;
+ return false;
+ }
+ }
+
+ if (QFile::copy(from, to)) {
+ QFile dest(to);
+ dest.setPermissions(dest.permissions() | QFile::WriteOwner | QFile::WriteUser);
+ LogNormal() << " copied:" << from;
+ LogNormal() << " to" << to;
+
+ // The source file might not have write permissions set. Set the
+ // write permission on the target file to make sure we can use
+ // install_name_tool on it later.
+ QFile toFile(to);
+ if (toFile.permissions() & QFile::WriteOwner)
+ return true;
+
+ if (!toFile.setPermissions(toFile.permissions() | QFile::WriteOwner)) {
+ LogError() << "Failed to set u+w permissions on target file: " << to;
+ return false;
+ }
+
+ return true;
+ } else {
+ LogError() << "file copy failed from" << from;
+ LogError() << " to" << to;
+ return false;
+ }
+}
+
+bool linkFilePrintStatus(const QString &file, const QString &link)
+{
+ if (QFile::exists(link)) {
+ if (QFile(link).symLinkTarget().isEmpty())
+ LogError() << link << "exists but it's a file.";
+ else
+ LogNormal() << "Symlink exists, skipping:" << link;
+ return false;
+ } else if (QFile::link(file, link)) {
+ LogNormal() << " symlink" << link;
+ LogNormal() << " points to" << file;
+ return true;
+ } else {
+ LogError() << "failed to symlink" << link;
+ LogError() << " to" << file;
+ return false;
+ }
+}
+
+void patch_debugInInfoPlist(const QString &infoPlistPath)
+{
+ // Older versions of qmake may have the "_debug" binary as
+ // the value for CFBundleExecutable. Remove it.
+ QFile infoPlist(infoPlistPath);
+ infoPlist.open(QIODevice::ReadOnly);
+ QByteArray contents = infoPlist.readAll();
+ infoPlist.close();
+ infoPlist.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ contents.replace("_debug", ""); // surely there are no legit uses of "_debug" in an Info.plist
+ infoPlist.write(contents);
+}
+
+OtoolInfo findDependencyInfo(const QString &binaryPath)
+{
+ OtoolInfo info;
+ info.binaryPath = binaryPath;
+
+ LogDebug() << "Using otool:";
+ LogDebug() << " inspecting" << binaryPath;
+ QProcess otool;
+ otool.start("otool", QStringList() << "-L" << binaryPath);
+ otool.waitForFinished(-1);
+
+ if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
+ LogError() << otool.readAllStandardError();
+ return info;
+ }
+
+ static const QRegularExpression regexp(QStringLiteral(
+ "^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), "
+ "current version (\\d+\\.\\d+\\.\\d+)(, weak|, reexport)?\\)$"));
+
+ QString output = otool.readAllStandardOutput();
+ QStringList outputLines = output.split("\n", Qt::SkipEmptyParts);
+ if (outputLines.size() < 2) {
+ LogError() << "Could not parse otool output:" << output;
+ return info;
+ }
+
+ outputLines.removeFirst(); // remove line containing the binary path
+ if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) {
+ const auto match = regexp.match(outputLines.constFirst());
+ if (match.hasMatch()) {
+ QString installname = match.captured(1);
+ if (QFileInfo(binaryPath).fileName() == QFileInfo(installname).fileName()) {
+ info.installName = installname;
+ info.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
+ info.currentVersion = QVersionNumber::fromString(match.captured(3));
+ outputLines.removeFirst();
+ } else {
+ info.installName = binaryPath;
+ }
+ } else {
+ LogDebug() << "Could not parse otool output line:" << outputLines.constFirst();
+ outputLines.removeFirst();
+ }
+ }
+
+ for (const QString &outputLine : outputLines) {
+ const auto match = regexp.match(outputLine);
+ if (match.hasMatch()) {
+ if (match.captured(1) == info.installName)
+ continue; // Another arch reference to the same binary
+ DylibInfo dylib;
+ dylib.binaryPath = match.captured(1);
+ dylib.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
+ dylib.currentVersion = QVersionNumber::fromString(match.captured(3));
+ info.dependencies << dylib;
+ } else {
+ LogDebug() << "Could not parse otool output line:" << outputLine;
+ }
+ }
+
+ return info;
+}
+
+FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
+{
+ FrameworkInfo info;
+ QString trimmed = line.trimmed();
+
+ if (trimmed.isEmpty())
+ return info;
+
+ // Don't deploy system libraries.
+ if (trimmed.startsWith("/System/Library/") ||
+ (trimmed.startsWith("/usr/lib/") && trimmed.contains("libQt") == false) // exception for libQtuitools and libQtlucene
+ || trimmed.startsWith("@executable_path") || trimmed.startsWith("@loader_path"))
+ return info;
+
+ // Resolve rpath relative libraries.
+ if (trimmed.startsWith("@rpath/")) {
+ QString rpathRelativePath = trimmed.mid(QStringLiteral("@rpath/").length());
+ bool foundInsideBundle = false;
+ for (const QString &rpath : std::as_const(rpaths)) {
+ QString path = QDir::cleanPath(rpath + "/" + rpathRelativePath);
+ // Skip paths already inside the bundle.
+ if (!appBundlePath.isEmpty()) {
+ if (QDir::isAbsolutePath(appBundlePath)) {
+ if (path.startsWith(QDir::cleanPath(appBundlePath) + "/")) {
+ foundInsideBundle = true;
+ continue;
+ }
+ } else {
+ if (path.startsWith(QDir::cleanPath(QDir::currentPath() + "/" + appBundlePath) + "/")) {
+ foundInsideBundle = true;
+ continue;
+ }
+ }
+ }
+ // Try again with substituted rpath.
+ FrameworkInfo resolvedInfo = parseOtoolLibraryLine(path, appBundlePath, rpaths, useDebugLibs);
+ if (!resolvedInfo.frameworkName.isEmpty() && QFile::exists(resolvedInfo.frameworkPath)) {
+ resolvedInfo.rpathUsed = rpath;
+ resolvedInfo.installName = trimmed;
+ return resolvedInfo;
+ }
+ }
+ if (!rpaths.isEmpty() && !foundInsideBundle) {
+ LogError() << "Cannot resolve rpath" << trimmed;
+ LogError() << " using" << rpaths;
+ }
+ return info;
+ }
+
+ enum State {QtPath, FrameworkName, DylibName, Version, FrameworkBinary, End};
+ State state = QtPath;
+ int part = 0;
+ QString name;
+ QString qtPath;
+ QString suffix = useDebugLibs ? "_debug" : "";
+
+ // Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/
+ QStringList parts = trimmed.split("/");
+ while (part < parts.count()) {
+ const QString currentPart = parts.at(part).simplified();
+ ++part;
+ if (currentPart == "")
+ continue;
+
+ if (state == QtPath) {
+ // Check for library name part
+ if (part < parts.count() && parts.at(part).contains(".dylib")) {
+ info.frameworkDirectory += "/" + QString(qtPath + currentPart + "/").simplified();
+ state = DylibName;
+ continue;
+ } else if (part < parts.count() && parts.at(part).endsWith(".framework")) {
+ info.frameworkDirectory += "/" + QString(qtPath + "lib/").simplified();
+ state = FrameworkName;
+ continue;
+ } else if (trimmed.startsWith("/") == false) { // If the line does not contain a full path, the app is using a binary Qt package.
+ QStringList partsCopy = parts;
+ partsCopy.removeLast();
+ for (QString &path : librarySearchPath) {
+ if (!path.endsWith("/"))
+ path += '/';
+ QString nameInPath = path + parts.join(u'/');
+ if (QFile::exists(nameInPath)) {
+ info.frameworkDirectory = path + partsCopy.join(u'/');
+ break;
+ }
+ }
+ if (currentPart.contains(".framework")) {
+ if (info.frameworkDirectory.isEmpty())
+ info.frameworkDirectory = "/Library/Frameworks/" + partsCopy.join(u'/');
+ if (!info.frameworkDirectory.endsWith("/"))
+ info.frameworkDirectory += "/";
+ state = FrameworkName;
+ --part;
+ continue;
+ } else if (currentPart.contains(".dylib")) {
+ if (info.frameworkDirectory.isEmpty())
+ info.frameworkDirectory = "/usr/lib/" + partsCopy.join(u'/');
+ if (!info.frameworkDirectory.endsWith("/"))
+ info.frameworkDirectory += "/";
+ state = DylibName;
+ --part;
+ continue;
+ }
+ }
+ qtPath += (currentPart + "/");
+
+ } if (state == FrameworkName) {
+ // remove ".framework"
+ name = currentPart;
+ name.chop(QString(".framework").length());
+ info.isDylib = false;
+ info.frameworkName = currentPart;
+ state = Version;
+ ++part;
+ continue;
+ } if (state == DylibName) {
+ name = currentPart;
+ info.isDylib = true;
+ info.frameworkName = name;
+ info.binaryName = name.contains(suffix) ? name : name.left(name.indexOf('.')) + suffix + name.mid(name.indexOf('.'));
+ info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName;
+ info.frameworkPath = info.frameworkDirectory + info.binaryName;
+ info.sourceFilePath = info.frameworkPath;
+ info.frameworkDestinationDirectory = bundleFrameworkDirectory + "/";
+ info.binaryDestinationDirectory = info.frameworkDestinationDirectory;
+ info.binaryDirectory = info.frameworkDirectory;
+ info.binaryPath = info.frameworkPath;
+ state = End;
+ ++part;
+ continue;
+ } else if (state == Version) {
+ info.version = currentPart;
+ info.binaryDirectory = "Versions/" + info.version;
+ info.frameworkPath = info.frameworkDirectory + info.frameworkName;
+ info.frameworkDestinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName;
+ info.binaryDestinationDirectory = info.frameworkDestinationDirectory + "/" + info.binaryDirectory;
+ state = FrameworkBinary;
+ } else if (state == FrameworkBinary) {
+ info.binaryName = currentPart.contains(suffix) ? currentPart : currentPart + suffix;
+ info.binaryPath = "/" + info.binaryDirectory + "/" + info.binaryName;
+ info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName + info.binaryPath;
+ info.sourceFilePath = info.frameworkPath + info.binaryPath;
+ state = End;
+ } else if (state == End) {
+ break;
+ }
+ }
+
+ if (!info.sourceFilePath.isEmpty() && QFile::exists(info.sourceFilePath)) {
+ info.installName = findDependencyInfo(info.sourceFilePath).installName;
+ if (info.installName.startsWith("@rpath/"))
+ info.deployedInstallName = info.installName;
+ }
+
+ return info;
+}
+
+QString findAppBinary(const QString &appBundlePath)
+{
+ QString binaryPath;
+
+#ifdef Q_OS_DARWIN
+ CFStringRef bundlePath = appBundlePath.toCFString();
+ CFURLRef bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath,
+ kCFURLPOSIXPathStyle, true);
+ CFRelease(bundlePath);
+ CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, bundleURL);
+ if (bundle) {
+ CFURLRef executableURL = CFBundleCopyExecutableURL(bundle);
+ if (executableURL) {
+ CFURLRef absoluteExecutableURL = CFURLCopyAbsoluteURL(executableURL);
+ if (absoluteExecutableURL) {
+ CFStringRef executablePath = CFURLCopyFileSystemPath(absoluteExecutableURL,
+ kCFURLPOSIXPathStyle);
+ if (executablePath) {
+ binaryPath = QString::fromCFString(executablePath);
+ CFRelease(executablePath);
+ }
+ CFRelease(absoluteExecutableURL);
+ }
+ CFRelease(executableURL);
+ }
+ CFRelease(bundle);
+ }
+ CFRelease(bundleURL);
+#endif
+
+ if (QFile::exists(binaryPath))
+ return binaryPath;
+ LogError() << "Could not find bundle binary for" << appBundlePath;
+ return QString();
+}
+
+QStringList findAppFrameworkNames(const QString &appBundlePath)
+{
+ QStringList frameworks;
+
+ // populate the frameworks list with QtFoo.framework etc,
+ // as found in /Contents/Frameworks/
+ QString searchPath = appBundlePath + "/Contents/Frameworks/";
+ QDirIterator iter(searchPath, QStringList() << QString::fromLatin1("*.framework"),
+ QDir::Dirs | QDir::NoSymLinks);
+ while (iter.hasNext()) {
+ iter.next();
+ frameworks << iter.fileInfo().fileName();
+ }
+
+ return frameworks;
+}
+
+QStringList findAppFrameworkPaths(const QString &appBundlePath)
+{
+ QStringList frameworks;
+ QString searchPath = appBundlePath + "/Contents/Frameworks/";
+ QDirIterator iter(searchPath, QStringList() << QString::fromLatin1("*.framework"),
+ QDir::Dirs | QDir::NoSymLinks);
+ while (iter.hasNext()) {
+ iter.next();
+ frameworks << iter.fileInfo().filePath();
+ }
+
+ return frameworks;
+}
+
+QStringList findAppLibraries(const QString &appBundlePath)
+{
+ QStringList result;
+ // dylibs
+ QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*.dylib") << QString::fromLatin1("*.so"),
+ QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
+ while (iter.hasNext()) {
+ iter.next();
+ result << iter.fileInfo().filePath();
+ }
+ return result;
+}
+
+QStringList findAppBundleFiles(const QString &appBundlePath, bool absolutePath = false)
+{
+ QStringList result;
+
+ QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*"),
+ QDir::Files, QDirIterator::Subdirectories);
+
+ while (iter.hasNext()) {
+ iter.next();
+ if (iter.fileInfo().isSymLink())
+ continue;
+ result << (absolutePath ? iter.fileInfo().absoluteFilePath() : iter.fileInfo().filePath());
+ }
+
+ return result;
+}
+
+QString findEntitlementsFile(const QString& path)
+{
+ QDirIterator iter(path, QStringList() << QString::fromLatin1("*.entitlements"),
+ QDir::Files, QDirIterator::Subdirectories);
+
+ while (iter.hasNext()) {
+ iter.next();
+ if (iter.fileInfo().isSymLink())
+ continue;
+
+ //return the first entitlements file - only one is used for signing anyway
+ return iter.fileInfo().absoluteFilePath();
+ }
+
+ return QString();
+}
+
+QList<FrameworkInfo> getQtFrameworks(const QList<DylibInfo> &dependencies, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
+{
+ QList<FrameworkInfo> libraries;
+ for (const DylibInfo &dylibInfo : dependencies) {
+ FrameworkInfo info = parseOtoolLibraryLine(dylibInfo.binaryPath, appBundlePath, rpaths, useDebugLibs);
+ if (info.frameworkName.isEmpty() == false) {
+ LogDebug() << "Adding framework:";
+ LogDebug() << info;
+ libraries.append(info);
+ }
+ }
+ return libraries;
+}
+
+QString resolveDyldPrefix(const QString &path, const QString &loaderPath, const QString &executablePath)
+{
+ if (path.startsWith("@")) {
+ if (path.startsWith(QStringLiteral("@executable_path/"))) {
+ // path relative to bundle executable dir
+ if (QDir::isAbsolutePath(executablePath)) {
+ return QDir::cleanPath(QFileInfo(executablePath).path() + path.mid(QStringLiteral("@executable_path").length()));
+ } else {
+ return QDir::cleanPath(QDir::currentPath() + "/" +
+ QFileInfo(executablePath).path() + path.mid(QStringLiteral("@executable_path").length()));
+ }
+ } else if (path.startsWith(QStringLiteral("@loader_path"))) {
+ // path relative to loader dir
+ if (QDir::isAbsolutePath(loaderPath)) {
+ return QDir::cleanPath(QFileInfo(loaderPath).path() + path.mid(QStringLiteral("@loader_path").length()));
+ } else {
+ return QDir::cleanPath(QDir::currentPath() + "/" +
+ QFileInfo(loaderPath).path() + path.mid(QStringLiteral("@loader_path").length()));
+ }
+ } else {
+ LogError() << "Unexpected prefix" << path;
+ }
+ }
+ return path;
+}
+
+QList<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString executablePath = QString())
+{
+ QList<QString> rpaths;
+
+ QProcess otool;
+ otool.start("otool", QStringList() << "-l" << path);
+ otool.waitForFinished();
+
+ if (otool.exitCode() != 0) {
+ LogError() << otool.readAllStandardError();
+ }
+
+ if (resolve && executablePath.isEmpty()) {
+ executablePath = path;
+ }
+
+ QString output = otool.readAllStandardOutput();
+ QStringList outputLines = output.split("\n");
+
+ for (auto i = outputLines.cbegin(), end = outputLines.cend(); i != end; ++i) {
+ if (i->contains("cmd LC_RPATH") && ++i != end &&
+ i->contains("cmdsize") && ++i != end) {
+ const QString &rpathCmd = *i;
+ int pathStart = rpathCmd.indexOf("path ");
+ int pathEnd = rpathCmd.indexOf(" (");
+ if (pathStart >= 0 && pathEnd >= 0 && pathStart < pathEnd) {
+ QString rpath = rpathCmd.mid(pathStart + 5, pathEnd - pathStart - 5);
+ if (resolve) {
+ rpaths << resolveDyldPrefix(rpath, path, executablePath);
+ } else {
+ rpaths << rpath;
+ }
+ }
+ }
+ }
+
+ return rpaths;
+}
+
+QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
+{
+ const OtoolInfo info = findDependencyInfo(path);
+ QList<QString> allRPaths = rpaths + getBinaryRPaths(path);
+ allRPaths.removeDuplicates();
+ return getQtFrameworks(info.dependencies, appBundlePath, allRPaths, useDebugLibs);
+}
+
+QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
+{
+ QList<FrameworkInfo> result;
+ QSet<QString> existing;
+ for (const QString &path : paths) {
+ for (const FrameworkInfo &info : getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) {
+ if (!existing.contains(info.frameworkPath)) { // avoid duplicates
+ existing.insert(info.frameworkPath);
+ result << info;
+ }
+ }
+ }
+ return result;
+}
+
+QStringList getBinaryDependencies(const QString executablePath,
+ const QString &path,
+ const QList<QString> &additionalBinariesContainingRpaths)
+{
+ QStringList binaries;
+
+ const auto dependencies = findDependencyInfo(path).dependencies;
+
+ bool rpathsLoaded = false;
+ QList<QString> rpaths;
+
+ // return bundle-local dependencies. (those starting with @executable_path)
+ for (const DylibInfo &info : dependencies) {
+ QString trimmedLine = info.binaryPath;
+ if (trimmedLine.startsWith("@executable_path/")) {
+ QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length()));
+ if (binary != path)
+ binaries.append(binary);
+ } else if (trimmedLine.startsWith("@loader_path/")) {
+ QString binary = QDir::cleanPath(QFileInfo(path).path() + "/" + trimmedLine.mid(QStringLiteral("@loader_path/").length()));
+ if (binary != path)
+ binaries.append(binary);
+ } else if (trimmedLine.startsWith("@rpath/")) {
+ if (!rpathsLoaded) {
+ rpaths = getBinaryRPaths(path, true, executablePath);
+ for (const QString &binaryPath : additionalBinariesContainingRpaths)
+ rpaths += getBinaryRPaths(binaryPath, true);
+ rpaths.removeDuplicates();
+ rpathsLoaded = true;
+ }
+ bool resolved = false;
+ for (const QString &rpath : std::as_const(rpaths)) {
+ QString binary = QDir::cleanPath(rpath + "/" + trimmedLine.mid(QStringLiteral("@rpath/").length()));
+ LogDebug() << "Checking for" << binary;
+ if (QFile::exists(binary)) {
+ binaries.append(binary);
+ resolved = true;
+ break;
+ }
+ }
+ if (!resolved && !rpaths.isEmpty()) {
+ LogError() << "Cannot resolve rpath" << trimmedLine;
+ LogError() << " using" << rpaths;
+ }
+ }
+ }
+
+ return binaries;
+}
+
+// copies everything _inside_ sourcePath to destinationPath
+bool recursiveCopy(const QString &sourcePath, const QString &destinationPath,
+ const QRegularExpression &ignoreRegExp = QRegularExpression())
+{
+ const QDir sourceDir(sourcePath);
+ if (!sourceDir.exists())
+ return false;
+ QDir().mkpath(destinationPath);
+
+ LogNormal() << "copy:" << sourcePath << destinationPath;
+
+ const QStringList files = sourceDir.entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot);
+ const bool hasValidRegExp = ignoreRegExp.isValid() && ignoreRegExp.pattern().length() > 0;
+ for (const QString &file : files) {
+ if (hasValidRegExp && ignoreRegExp.match(file).hasMatch())
+ continue;
+ const QString fileSourcePath = sourcePath + "/" + file;
+ const QString fileDestinationPath = destinationPath + "/" + file;
+ copyFilePrintStatus(fileSourcePath, fileDestinationPath);
+ }
+
+ const QStringList subdirs = sourceDir.entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &dir : subdirs) {
+ recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir);
+ }
+ return true;
+}
+
+void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &rpaths, const QString &sourcePath, const QString &destinationPath)
+{
+ QDir().mkpath(destinationPath);
+
+ LogNormal() << "copy:" << sourcePath << destinationPath;
+ const bool isDwarfPath = sourcePath.endsWith("DWARF");
+
+ const QDir sourceDir(sourcePath);
+
+ const QStringList files = sourceDir.entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot);
+ for (const QString &file : files) {
+ const QString fileSourcePath = sourcePath + u'/' + file;
+
+ if (file.endsWith("_debug.dylib")) {
+ continue; // Skip debug versions
+ } else if (!isDwarfPath && file.endsWith(QStringLiteral(".dylib"))) {
+ // App store code signing rules forbids code binaries in Contents/Resources/,
+ // which poses a problem for deploying mixed .qml/.dylib Qt Quick imports.
+ // Solve this by placing the dylibs in Contents/PlugIns/quick, and then
+ // creting a symlink to there from the Qt Quick import in Contents/Resources/.
+ //
+ // Example:
+ // MyApp.app/Contents/Resources/qml/QtQuick/Controls/libqtquickcontrolsplugin.dylib ->
+ // ../../../../PlugIns/quick/libqtquickcontrolsplugin.dylib
+ //
+
+ // The .dylib destination path:
+ QString fileDestinationDir = appBundlePath + QStringLiteral("/Contents/PlugIns/quick/");
+ QDir().mkpath(fileDestinationDir);
+ QString fileDestinationPath = fileDestinationDir + file;
+
+ // The .dylib symlink destination path:
+ QString linkDestinationPath = destinationPath + u'/' + file;
+
+ // The (relative) link; with a correct number of "../"'s.
+ QString linkPath = QStringLiteral("PlugIns/quick/") + file;
+ int cdupCount = linkDestinationPath.count(QStringLiteral("/")) - appBundlePath.count(QStringLiteral("/"));
+ for (int i = 0; i < cdupCount - 2; ++i)
+ linkPath.prepend("../");
+
+ if (copyFilePrintStatus(fileSourcePath, fileDestinationPath)) {
+ linkFilePrintStatus(linkPath, linkDestinationPath);
+
+ runStrip(fileDestinationPath);
+ bool useDebugLibs = false;
+ bool useLoaderPath = false;
+ QList<FrameworkInfo> frameworks = getQtFrameworks(fileDestinationPath, appBundlePath, rpaths, useDebugLibs);
+ deployQtFrameworks(frameworks, appBundlePath, QStringList(fileDestinationPath), useDebugLibs, useLoaderPath);
+ }
+ } else {
+ QString fileDestinationPath = destinationPath + u'/' + file;
+ copyFilePrintStatus(fileSourcePath, fileDestinationPath);
+ }
+ }
+
+ const QStringList subdirs = sourceDir.entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &dir : subdirs) {
+ recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + u'/' + dir, destinationPath + u'/' + dir);
+ }
+}
+
+QString copyDylib(const FrameworkInfo &framework, const QString path)
+{
+ if (!QFile::exists(framework.sourceFilePath)) {
+ LogError() << "no file at" << framework.sourceFilePath;
+ return QString();
+ }
+
+ // Construct destination paths. The full path typically looks like
+ // MyApp.app/Contents/Frameworks/libfoo.dylib
+ QString dylibDestinationDirectory = path + u'/' + framework.frameworkDestinationDirectory;
+ QString dylibDestinationBinaryPath = dylibDestinationDirectory + u'/' + framework.binaryName;
+
+ // Create destination directory
+ if (!QDir().mkpath(dylibDestinationDirectory)) {
+ LogError() << "could not create destination directory" << dylibDestinationDirectory;
+ return QString();
+ }
+
+ // Return if the dylib has already been deployed
+ if (QFileInfo::exists(dylibDestinationBinaryPath) && !alwaysOwerwriteEnabled)
+ return dylibDestinationBinaryPath;
+
+ // Copy dylib binary
+ copyFilePrintStatus(framework.sourceFilePath, dylibDestinationBinaryPath);
+ return dylibDestinationBinaryPath;
+}
+
+QString copyFramework(const FrameworkInfo &framework, const QString path)
+{
+ if (!QFile::exists(framework.sourceFilePath)) {
+ LogError() << "no file at" << framework.sourceFilePath;
+ return QString();
+ }
+
+ // Construct destination paths. The full path typically looks like
+ // MyApp.app/Contents/Frameworks/Foo.framework/Versions/5/QtFoo
+ QString frameworkDestinationDirectory = path + u'/' + framework.frameworkDestinationDirectory;
+ QString frameworkBinaryDestinationDirectory = frameworkDestinationDirectory + u'/' + framework.binaryDirectory;
+ QString frameworkDestinationBinaryPath = frameworkBinaryDestinationDirectory + u'/' + framework.binaryName;
+
+ // Return if the framework has aleardy been deployed
+ if (QDir(frameworkDestinationDirectory).exists() && !alwaysOwerwriteEnabled)
+ return QString();
+
+ // Create destination directory
+ if (!QDir().mkpath(frameworkBinaryDestinationDirectory)) {
+ LogError() << "could not create destination directory" << frameworkBinaryDestinationDirectory;
+ return QString();
+ }
+
+ // Now copy the framework. Some parts should be left out (headers/, .prl files).
+ // Some parts should be included (Resources/, symlink structure). We want this
+ // function to make as few assumptions about the framework as possible while at
+ // the same time producing a codesign-compatible framework.
+
+ // Copy framework binary
+ copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath);
+
+ // Copy Resources/, Libraries/ and Helpers/
+ const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
+ const QString resourcesDestinationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
+ // Ignore *.prl files that are in the Resources directory
+ recursiveCopy(resourcesSourcePath, resourcesDestinationPath,
+ QRegularExpression("\\A(?:[^/]*\\.prl)\\z"));
+ const QString librariesSourcePath = framework.frameworkPath + "/Libraries";
+ const QString librariesDestinationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Libraries";
+ bool createdLibraries = recursiveCopy(librariesSourcePath, librariesDestinationPath);
+ const QString helpersSourcePath = framework.frameworkPath + "/Helpers";
+ const QString helpersDestinationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Helpers";
+ bool createdHelpers = recursiveCopy(helpersSourcePath, helpersDestinationPath);
+
+ // Create symlink structure. Links at the framework root point to Versions/Current/
+ // which again points to the actual version:
+ // QtFoo.framework/QtFoo -> Versions/Current/QtFoo
+ // QtFoo.framework/Resources -> Versions/Current/Resources
+ // QtFoo.framework/Versions/Current -> 5
+ linkFilePrintStatus("Versions/Current/" + framework.binaryName, frameworkDestinationDirectory + "/" + framework.binaryName);
+ linkFilePrintStatus("Versions/Current/Resources", frameworkDestinationDirectory + "/Resources");
+ if (createdLibraries)
+ linkFilePrintStatus("Versions/Current/Libraries", frameworkDestinationDirectory + "/Libraries");
+ if (createdHelpers)
+ linkFilePrintStatus("Versions/Current/Helpers", frameworkDestinationDirectory + "/Helpers");
+ linkFilePrintStatus(framework.version, frameworkDestinationDirectory + "/Versions/Current");
+
+ // Correct Info.plist location for frameworks produced by older versions of qmake
+ // Contents/Info.plist should be Versions/5/Resources/Info.plist
+ const QString legacyInfoPlistPath = framework.frameworkPath + "/Contents/Info.plist";
+ const QString correctInfoPlistPath = frameworkDestinationDirectory + "/Resources/Info.plist";
+ if (QFile::exists(legacyInfoPlistPath)) {
+ copyFilePrintStatus(legacyInfoPlistPath, correctInfoPlistPath);
+ patch_debugInInfoPlist(correctInfoPlistPath);
+ }
+ return frameworkDestinationBinaryPath;
+}
+
+void runInstallNameTool(QStringList options)
+{
+ QProcess installNametool;
+ installNametool.start("install_name_tool", options);
+ installNametool.waitForFinished();
+ if (installNametool.exitCode() != 0) {
+ LogError() << installNametool.readAllStandardError();
+ LogError() << installNametool.readAllStandardOutput();
+ }
+}
+
+void changeIdentification(const QString &id, const QString &binaryPath)
+{
+ LogDebug() << "Using install_name_tool:";
+ LogDebug() << " change identification in" << binaryPath;
+ LogDebug() << " to" << id;
+ runInstallNameTool(QStringList() << "-id" << id << binaryPath);
+}
+
+void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath)
+{
+ const QString absBundlePath = QFileInfo(bundlePath).absoluteFilePath();
+ for (const QString &binary : binaryPaths) {
+ QString deployedInstallName;
+ if (useLoaderPath) {
+ deployedInstallName = "@loader_path/"_L1
+ + QFileInfo(binary).absoluteDir().relativeFilePath(absBundlePath + u'/' + framework.binaryDestinationDirectory + u'/' + framework.binaryName);
+ } else {
+ deployedInstallName = framework.deployedInstallName;
+ }
+ changeInstallName(framework.installName, deployedInstallName, binary);
+ // Workaround for the case when the library ID name is a symlink, while the dependencies
+ // specified using the canonical path to the library (QTBUG-56814)
+ QFileInfo fileInfo= QFileInfo(framework.installName);
+ QString canonicalInstallName = fileInfo.canonicalFilePath();
+ if (!canonicalInstallName.isEmpty() && canonicalInstallName != framework.installName) {
+ changeInstallName(canonicalInstallName, deployedInstallName, binary);
+ // some libraries' inner dependencies (such as ffmpeg, nettle) use symbol link (QTBUG-100093)
+ QString innerDependency = fileInfo.canonicalPath() + "/" + fileInfo.fileName();
+ if (innerDependency != canonicalInstallName && innerDependency != framework.installName) {
+ changeInstallName(innerDependency, deployedInstallName, binary);
+ }
+ }
+ }
+}
+
+void addRPath(const QString &rpath, const QString &binaryPath)
+{
+ runInstallNameTool(QStringList() << "-add_rpath" << rpath << binaryPath);
+}
+
+void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QString &binaryPath, bool useLoaderPath)
+{
+ const QString absFrameworksPath = QFileInfo(bundlePath).absoluteFilePath()
+ + "/Contents/Frameworks"_L1;
+ const QString relativeFrameworkPath = QFileInfo(binaryPath).absoluteDir().relativeFilePath(absFrameworksPath);
+ const QString loaderPathToFrameworks = "@loader_path/"_L1 + relativeFrameworkPath;
+ bool rpathToFrameworksFound = false;
+ QStringList args;
+ QList<QString> binaryRPaths = getBinaryRPaths(binaryPath, false);
+ for (const QString &rpath : std::as_const(binaryRPaths)) {
+ if (rpath == "@executable_path/../Frameworks" ||
+ rpath == loaderPathToFrameworks) {
+ rpathToFrameworksFound = true;
+ continue;
+ }
+ if (rpaths.contains(resolveDyldPrefix(rpath, binaryPath, binaryPath))) {
+ if (!args.contains(rpath))
+ args << "-delete_rpath" << rpath;
+ }
+ }
+ if (!args.length()) {
+ return;
+ }
+ if (!rpathToFrameworksFound) {
+ if (!useLoaderPath) {
+ args << "-add_rpath" << "@executable_path/../Frameworks";
+ } else {
+ args << "-add_rpath" << loaderPathToFrameworks;
+ }
+ }
+ LogDebug() << "Using install_name_tool:";
+ LogDebug() << " change rpaths in" << binaryPath;
+ LogDebug() << " using" << args;
+ runInstallNameTool(QStringList() << args << binaryPath);
+}
+
+void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QStringList &binaryPaths, bool useLoaderPath)
+{
+ for (const QString &binary : binaryPaths) {
+ deployRPaths(bundlePath, rpaths, binary, useLoaderPath);
+ }
+}
+
+void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath)
+{
+ LogDebug() << "Using install_name_tool:";
+ LogDebug() << " in" << binaryPath;
+ LogDebug() << " change reference" << oldName;
+ LogDebug() << " to" << newName;
+ runInstallNameTool(QStringList() << "-change" << oldName << newName << binaryPath);
+}
+
+void runStrip(const QString &binaryPath)
+{
+ if (runStripEnabled == false)
+ return;
+
+ LogDebug() << "Using strip:";
+ LogDebug() << " stripped" << binaryPath;
+ QProcess strip;
+ strip.start("strip", QStringList() << "-x" << binaryPath);
+ strip.waitForFinished();
+ if (strip.exitCode() != 0) {
+ LogError() << strip.readAllStandardError();
+ LogError() << strip.readAllStandardOutput();
+ }
+}
+
+void stripAppBinary(const QString &bundlePath)
+{
+ runStrip(findAppBinary(bundlePath));
+}
+
+bool DeploymentInfo::containsModule(const QString &module, const QString &libInFix) const
+{
+ // Check for framework first
+ if (deployedFrameworks.contains("Qt"_L1 + module + libInFix + ".framework"_L1))
+ return true;
+ // Check for dylib
+ const QRegularExpression dylibRegExp("libQt[0-9]+"_L1
+ + module + libInFix
+ + (isDebug ? "_debug" : "")
+ + ".[0-9]+.dylib"_L1);
+ return deployedFrameworks.filter(dylibRegExp).size() > 0;
+}
+
+/*
+ Deploys the listed frameworks into an app bundle.
+ The frameworks are searched for dependencies, which are also deployed.
+ (deploying Qt3Support will also deploy QtNetwork and QtSql for example.)
+ Returns a DeploymentInfo structure containing the Qt path used and a
+ a list of actually deployed frameworks.
+*/
+DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
+ const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs,
+ bool useLoaderPath)
+{
+ LogNormal();
+ LogNormal() << "Deploying Qt frameworks found inside:" << binaryPaths;
+ QStringList copiedFrameworks;
+ DeploymentInfo deploymentInfo;
+ deploymentInfo.useLoaderPath = useLoaderPath;
+ deploymentInfo.isFramework = bundlePath.contains(".framework");
+ deploymentInfo.isDebug = false;
+ QList<QString> rpathsUsed;
+
+ while (frameworks.isEmpty() == false) {
+ const FrameworkInfo framework = frameworks.takeFirst();
+ copiedFrameworks.append(framework.frameworkName);
+
+ // If a single dependency has the _debug suffix, we treat that as
+ // the whole deployment being a debug deployment, including deploying
+ // the debug version of plugins.
+ if (framework.isDebugLibrary())
+ deploymentInfo.isDebug = true;
+
+ if (deploymentInfo.qtPath.isNull())
+ deploymentInfo.qtPath = QLibraryInfo::path(QLibraryInfo::PrefixPath);
+
+ if (framework.frameworkDirectory.startsWith(bundlePath)) {
+ LogError() << framework.frameworkName << "already deployed, skipping.";
+ continue;
+ }
+
+ if (!framework.rpathUsed.isEmpty() && !rpathsUsed.contains(framework.rpathUsed)) {
+ rpathsUsed.append(framework.rpathUsed);
+ }
+
+ // Copy the framework/dylib to the app bundle.
+ const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath)
+ : copyFramework(framework, bundlePath);
+
+ // Install_name_tool the new id into the binaries
+ changeInstallName(bundlePath, framework, binaryPaths, useLoaderPath);
+
+ // Skip the rest if already was deployed.
+ if (deployedBinaryPath.isNull())
+ continue;
+
+ runStrip(deployedBinaryPath);
+
+ // Install_name_tool it a new id.
+ if (!framework.rpathUsed.length()) {
+ changeIdentification(framework.deployedInstallName, deployedBinaryPath);
+ }
+
+ // Check for framework dependencies
+ QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs);
+
+ for (const FrameworkInfo &dependency : dependencies) {
+ if (dependency.rpathUsed.isEmpty()) {
+ changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath);
+ } else {
+ rpathsUsed.append(dependency.rpathUsed);
+ }
+
+ // Deploy framework if necessary.
+ if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) {
+ frameworks.append(dependency);
+ }
+ }
+ }
+ deploymentInfo.deployedFrameworks = copiedFrameworks;
+ deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath);
+ deploymentInfo.rpathsUsed += rpathsUsed;
+ deploymentInfo.rpathsUsed.removeDuplicates();
+ return deploymentInfo;
+}
+
+DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs)
+{
+ ApplicationBundleInfo applicationBundle;
+ applicationBundle.path = appBundlePath;
+ applicationBundle.binaryPath = findAppBinary(appBundlePath);
+ applicationBundle.libraryPaths = findAppLibraries(appBundlePath);
+ QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths
+ << additionalExecutables;
+
+ QList<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true);
+ allLibraryPaths.append(QLibraryInfo::path(QLibraryInfo::LibrariesPath));
+ allLibraryPaths.removeDuplicates();
+
+ QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs);
+ if (frameworks.isEmpty() && !alwaysOwerwriteEnabled) {
+ LogWarning();
+ LogWarning() << "Could not find any external Qt frameworks to deploy in" << appBundlePath;
+ LogWarning() << "Perhaps macdeployqt was already used on" << appBundlePath << "?";
+ LogWarning() << "If so, you will need to rebuild" << appBundlePath << "before trying again.";
+ return DeploymentInfo();
+ } else {
+ return deployQtFrameworks(frameworks, applicationBundle.path, allBinaryPaths, useDebugLibs, !additionalExecutables.isEmpty());
+ }
+}
+
+QString getLibInfix(const QStringList &deployedFrameworks)
+{
+ QString libInfix;
+ for (const QString &framework : deployedFrameworks) {
+ if (framework.startsWith(QStringLiteral("QtCore")) && framework.endsWith(QStringLiteral(".framework")) &&
+ !framework.contains(QStringLiteral("5Compat"))) {
+ Q_ASSERT(framework.length() >= 16);
+ // 16 == "QtCore" + ".framework"
+ const int lengthOfLibInfix = framework.length() - 16;
+ if (lengthOfLibInfix)
+ libInfix = framework.mid(6, lengthOfLibInfix);
+ break;
+ }
+ }
+ return libInfix;
+}
+
+void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath,
+ const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs)
+{
+ LogNormal() << "Deploying plugins from" << pluginSourcePath;
+
+ if (!pluginSourcePath.contains(deploymentInfo.pluginPath))
+ return;
+
+ // Plugin white list:
+ QStringList pluginList;
+
+ const auto addPlugins = [&pluginSourcePath,&pluginList,useDebugLibs](const QString &subDirectory,
+ const std::function<bool(QString)> &predicate = std::function<bool(QString)>()) {
+ const QStringList libs = QDir(pluginSourcePath + u'/' + subDirectory)
+ .entryList({QStringLiteral("*.dylib")});
+ for (const QString &lib : libs) {
+ if (lib.endsWith(QStringLiteral("_debug.dylib")) != useDebugLibs)
+ continue;
+ if (!predicate || predicate(lib))
+ pluginList.append(subDirectory + u'/' + lib);
+ }
+ };
+
+ // Platform plugin:
+ addPlugins(QStringLiteral("platforms"), [](const QString &lib) {
+ // Ignore minimal and offscreen platform plugins
+ if (!lib.contains(QStringLiteral("cocoa")))
+ return false;
+ return true;
+ });
+
+ // Cocoa print support
+ addPlugins(QStringLiteral("printsupport"));
+
+ // Styles
+ addPlugins(QStringLiteral("styles"));
+
+ // Check if Qt was configured with -libinfix
+ const QString libInfix = getLibInfix(deploymentInfo.deployedFrameworks);
+
+ // Network
+ if (deploymentInfo.containsModule("Network", libInfix)) {
+ addPlugins(QStringLiteral("tls"));
+ addPlugins(QStringLiteral("networkinformation"));
+ }
+
+ // All image formats (svg if QtSvg is used)
+ const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
+ addPlugins(QStringLiteral("imageformats"), [usesSvg](const QString &lib) {
+ if (lib.contains(QStringLiteral("qsvg")) && !usesSvg)
+ return false;
+ return true;
+ });
+
+ addPlugins(QStringLiteral("iconengines"));
+
+ // Platforminputcontext plugins if QtGui is in use
+ if (deploymentInfo.containsModule("Gui", libInfix)) {
+ addPlugins(QStringLiteral("platforminputcontexts"), [&addPlugins](const QString &lib) {
+ // Deploy the virtual keyboard plugins if we have deployed virtualkeyboard
+ if (lib.startsWith(QStringLiteral("libqtvirtualkeyboard")))
+ addPlugins(QStringLiteral("virtualkeyboard"));
+ return true;
+ });
+ }
+
+ // Sql plugins if QtSql is in use
+ if (deploymentInfo.containsModule("Sql", libInfix)) {
+ addPlugins(QStringLiteral("sqldrivers"), [](const QString &lib) {
+ if (lib.startsWith(QStringLiteral("libqsqlodbc")) || lib.startsWith(QStringLiteral("libqsqlpsql"))) {
+ LogWarning() << "Plugin" << lib << "uses private API and is not Mac App store compliant.";
+ if (appstoreCompliant) {
+ LogWarning() << "Skip plugin" << lib;
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+ // WebView plugins if QtWebView is in use
+ if (deploymentInfo.containsModule("WebView", libInfix)) {
+ addPlugins(QStringLiteral("webview"), [](const QString &lib) {
+ if (lib.startsWith(QStringLiteral("libqtwebview_webengine"))) {
+ LogWarning() << "Plugin" << lib << "uses QtWebEngine and is not Mac App store compliant.";
+ if (appstoreCompliant) {
+ LogWarning() << "Skip plugin" << lib;
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+ static const std::map<QString, std::vector<QString>> map {
+ {QStringLiteral("Multimedia"), {QStringLiteral("multimedia")}},
+ {QStringLiteral("3DRender"), {QStringLiteral("sceneparsers"), QStringLiteral("geometryloaders"), QStringLiteral("renderers")}},
+ {QStringLiteral("3DQuickRender"), {QStringLiteral("renderplugins")}},
+ {QStringLiteral("Positioning"), {QStringLiteral("position")}},
+ {QStringLiteral("Location"), {QStringLiteral("geoservices")}},
+ {QStringLiteral("TextToSpeech"), {QStringLiteral("texttospeech")}}
+ };
+
+ for (const auto &it : map) {
+ if (deploymentInfo.containsModule(it.first, libInfix)) {
+ for (const auto &pluginType : it.second) {
+ addPlugins(pluginType);
+ }
+ }
+ }
+
+ for (const QString &plugin : pluginList) {
+ QString sourcePath = pluginSourcePath + "/" + plugin;
+ const QString destinationPath = pluginDestinationPath + "/" + plugin;
+ QDir dir;
+ dir.mkpath(QFileInfo(destinationPath).path());
+
+ if (copyFilePrintStatus(sourcePath, destinationPath)) {
+ runStrip(destinationPath);
+ QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
+ deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
+ }
+ }
+}
+
+void createQtConf(const QString &appBundlePath)
+{
+ // Set Plugins and imports paths. These are relative to App.app/Contents.
+ QByteArray contents = "[Paths]\n"
+ "Plugins = PlugIns\n"
+ "Imports = Resources/qml\n"
+ "QmlImports = Resources/qml\n";
+
+ QString filePath = appBundlePath + "/Contents/Resources/";
+ QString fileName = filePath + "qt.conf";
+
+ QDir().mkpath(filePath);
+
+ QFile qtconf(fileName);
+ if (qtconf.exists() && !alwaysOwerwriteEnabled) {
+ LogWarning();
+ LogWarning() << fileName << "already exists, will not overwrite.";
+ LogWarning() << "To make sure the plugins are loaded from the correct location,";
+ LogWarning() << "please make sure qt.conf contains the following lines:";
+ LogWarning() << "[Paths]";
+ LogWarning() << " Plugins = PlugIns";
+ return;
+ }
+
+ qtconf.open(QIODevice::WriteOnly);
+ if (qtconf.write(contents) != -1) {
+ LogNormal() << "Created configuration file:" << fileName;
+ LogNormal() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns";
+ }
+}
+
+void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs)
+{
+ ApplicationBundleInfo applicationBundle;
+ applicationBundle.path = appBundlePath;
+ applicationBundle.binaryPath = findAppBinary(appBundlePath);
+
+ const QString pluginDestinationPath = appBundlePath + "/" + "Contents/PlugIns";
+ deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs);
+}
+
+void deployQmlImport(const QString &appBundlePath, const QList<QString> &rpaths, const QString &importSourcePath, const QString &importName)
+{
+ QString importDestinationPath = appBundlePath + "/Contents/Resources/qml/" + importName;
+
+ // Skip already deployed imports. This can happen in cases like "QtQuick.Controls.Styles",
+ // where deploying QtQuick.Controls will also deploy the "Styles" sub-import.
+ if (QDir().exists(importDestinationPath))
+ return;
+
+ recursiveCopyAndDeploy(appBundlePath, rpaths, importSourcePath, importDestinationPath);
+}
+
+static bool importLessThan(const QVariant &v1, const QVariant &v2)
+{
+ QVariantMap import1 = v1.toMap();
+ QVariantMap import2 = v2.toMap();
+ QString path1 = import1["path"].toString();
+ QString path2 = import2["path"].toString();
+ return path1 < path2;
+}
+
+// Scan qml files in qmldirs for import statements, deploy used imports from QmlImportsPath to Contents/Resources/qml.
+bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths)
+{
+ LogNormal() << "";
+ LogNormal() << "Deploying QML imports ";
+ LogNormal() << "Application QML file path(s) is" << qmlDirs;
+ LogNormal() << "QML module search path(s) is" << qmlImportPaths;
+
+ // Use qmlimportscanner from QLibraryInfo::LibraryExecutablesPath
+ QString qmlImportScannerPath =
+ QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath)
+ + "/qmlimportscanner");
+
+ // Fallback: Look relative to the macdeployqt binary
+ if (!QFile::exists(qmlImportScannerPath))
+ qmlImportScannerPath = QCoreApplication::applicationDirPath() + "/qmlimportscanner";
+
+ // Verify that we found a qmlimportscanner binary
+ if (!QFile::exists(qmlImportScannerPath)) {
+ LogError() << "qmlimportscanner not found at" << qmlImportScannerPath;
+ LogError() << "Rebuild qtdeclarative/tools/qmlimportscanner";
+ return false;
+ }
+
+ // build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml"
+ // ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed)
+ QStringList argumentList;
+ for (const QString &qmlDir : qmlDirs) {
+ argumentList.append("-rootPath");
+ argumentList.append(qmlDir);
+ }
+ for (const QString &importPath : qmlImportPaths)
+ argumentList << "-importPath" << importPath;
+ QString qmlImportsPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
+ argumentList.append( "-importPath");
+ argumentList.append(qmlImportsPath);
+
+ // run qmlimportscanner
+ QProcess qmlImportScanner;
+ qmlImportScanner.start(qmlImportScannerPath, argumentList);
+ if (!qmlImportScanner.waitForStarted()) {
+ LogError() << "Could not start qmlimpoortscanner. Process error is" << qmlImportScanner.errorString();
+ return false;
+ }
+ qmlImportScanner.waitForFinished(-1);
+
+ // log qmlimportscanner errors
+ qmlImportScanner.setReadChannel(QProcess::StandardError);
+ QByteArray errors = qmlImportScanner.readAll();
+ if (!errors.isEmpty()) {
+ LogWarning() << "QML file parse error (deployment will continue):";
+ LogWarning() << errors;
+ }
+
+ // parse qmlimportscanner json
+ qmlImportScanner.setReadChannel(QProcess::StandardOutput);
+ QByteArray json = qmlImportScanner.readAll();
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ if (!doc.isArray()) {
+ LogError() << "qmlimportscanner output error. Expected json array, got:";
+ LogError() << json;
+ return false;
+ }
+
+ // sort imports to deploy a module before its sub-modules (otherwise
+ // deployQmlImports can consider the module deployed if it has already
+ // deployed one of its sub-module)
+ QVariantList array = doc.array().toVariantList();
+ std::sort(array.begin(), array.end(), importLessThan);
+
+ // deploy each import
+ for (const QVariant &importValue : array) {
+ QVariantMap import = importValue.toMap();
+ QString name = import["name"].toString();
+ QString path = import["path"].toString();
+ QString type = import["type"].toString();
+
+ LogNormal() << "Deploying QML import" << name;
+
+ // Skip imports with missing info - path will be empty if the import is not found.
+ if (name.isEmpty() || path.isEmpty()) {
+ LogNormal() << " Skip import: name or path is empty";
+ LogNormal() << "";
+ continue;
+ }
+
+ // Deploy module imports only, skip directory (local/remote) and js imports. These
+ // should be deployed as a part of the application build.
+ if (type != QStringLiteral("module")) {
+ LogNormal() << " Skip non-module import";
+ LogNormal() << "";
+ continue;
+ }
+
+ // Create the destination path from the name
+ // and version (grabbed from the source path)
+ // ### let qmlimportscanner provide this.
+ name.replace(u'.', u'/');
+ int secondTolast = path.length() - 2;
+ QString version = path.mid(secondTolast);
+ if (version.startsWith(u'.'))
+ name.append(version);
+
+ deployQmlImport(appBundlePath, deploymentInfo.rpathsUsed, path, name);
+ LogNormal() << "";
+ }
+ return true;
+}
+
+void codesignFile(const QString &identity, const QString &filePath)
+{
+ if (!runCodesign)
+ return;
+
+ QString codeSignLogMessage = "codesign";
+ if (hardenedRuntime)
+ codeSignLogMessage += ", enable hardened runtime";
+ if (secureTimestamp)
+ codeSignLogMessage += ", include secure timestamp";
+ LogNormal() << codeSignLogMessage << filePath;
+
+ QStringList codeSignOptions = { "--preserve-metadata=identifier,entitlements", "--force", "-s",
+ identity, filePath };
+ if (hardenedRuntime)
+ codeSignOptions << "-o" << "runtime";
+
+ if (secureTimestamp)
+ codeSignOptions << "--timestamp";
+
+ if (!extraEntitlements.isEmpty())
+ codeSignOptions << "--entitlements" << extraEntitlements;
+
+ QProcess codesign;
+ codesign.start("codesign", codeSignOptions);
+ codesign.waitForFinished(-1);
+
+ QByteArray err = codesign.readAllStandardError();
+ if (codesign.exitCode() > 0) {
+ LogError() << "Codesign signing error:";
+ LogError() << err;
+ } else if (!err.isEmpty()) {
+ LogDebug() << err;
+ }
+}
+
+QSet<QString> codesignBundle(const QString &identity,
+ const QString &appBundlePath,
+ QList<QString> additionalBinariesContainingRpaths)
+{
+ // Code sign all binaries in the app bundle. This needs to
+ // be done inside-out, e.g sign framework dependencies
+ // before the main app binary. The codesign tool itself has
+ // a "--deep" option to do this, but usage when signing is
+ // not recommended: "Signing with --deep is for emergency
+ // repairs and temporary adjustments only."
+
+ LogNormal() << "";
+ LogNormal() << "Signing" << appBundlePath << "with identity" << identity;
+
+ QStack<QString> pendingBinaries;
+ QSet<QString> pendingBinariesSet;
+ QSet<QString> signedBinaries;
+
+ // Create the root code-binary set. This set consists of the application
+ // executable(s) and the plugins.
+ QString appBundleAbsolutePath = QFileInfo(appBundlePath).absoluteFilePath();
+ QString rootBinariesPath = appBundleAbsolutePath + "/Contents/MacOS/";
+ QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files);
+ for (const QString &binary : foundRootBinaries) {
+ QString binaryPath = rootBinariesPath + binary;
+ pendingBinaries.push(binaryPath);
+ pendingBinariesSet.insert(binaryPath);
+ additionalBinariesContainingRpaths.append(binaryPath);
+ }
+
+ bool getAbsoltuePath = true;
+ QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/", getAbsoltuePath);
+ for (const QString &binary : foundPluginBinaries) {
+ pendingBinaries.push(binary);
+ pendingBinariesSet.insert(binary);
+ }
+
+ // Add frameworks for processing.
+ QStringList frameworkPaths = findAppFrameworkPaths(appBundlePath);
+ for (const QString &frameworkPath : frameworkPaths) {
+
+ // Prioritise first to sign any additional inner bundles found in the Helpers folder (e.g
+ // used by QtWebEngine).
+ QDirIterator helpersIterator(frameworkPath, QStringList() << QString::fromLatin1("Helpers"), QDir::Dirs | QDir::NoSymLinks, QDirIterator::Subdirectories);
+ while (helpersIterator.hasNext()) {
+ helpersIterator.next();
+ QString helpersPath = helpersIterator.filePath();
+ QStringList innerBundleNames = QDir(helpersPath).entryList(QStringList() << "*.app", QDir::Dirs);
+ for (const QString &innerBundleName : innerBundleNames)
+ signedBinaries += codesignBundle(identity,
+ helpersPath + "/" + innerBundleName,
+ additionalBinariesContainingRpaths);
+ }
+
+ // Also make sure to sign any libraries that will not be found by otool because they
+ // are not linked and won't be seen as a dependency.
+ QDirIterator librariesIterator(frameworkPath, QStringList() << QString::fromLatin1("Libraries"), QDir::Dirs | QDir::NoSymLinks, QDirIterator::Subdirectories);
+ while (librariesIterator.hasNext()) {
+ librariesIterator.next();
+ QString librariesPath = librariesIterator.filePath();
+ QStringList bundleFiles = findAppBundleFiles(librariesPath, getAbsoltuePath);
+ for (const QString &binary : bundleFiles) {
+ pendingBinaries.push(binary);
+ pendingBinariesSet.insert(binary);
+ }
+ }
+ }
+
+ // Sign all binaries; use otool to find and sign dependencies first.
+ while (!pendingBinaries.isEmpty()) {
+ QString binary = pendingBinaries.pop();
+ if (signedBinaries.contains(binary))
+ continue;
+
+ // Check if there are unsigned dependencies, sign these first.
+ QStringList dependencies = getBinaryDependencies(rootBinariesPath, binary,
+ additionalBinariesContainingRpaths);
+ dependencies = QSet<QString>(dependencies.begin(), dependencies.end())
+ .subtract(signedBinaries)
+ .subtract(pendingBinariesSet)
+ .values();
+
+ if (!dependencies.isEmpty()) {
+ pendingBinaries.push(binary);
+ pendingBinariesSet.insert(binary);
+ int dependenciesSkipped = 0;
+ for (const QString &dependency : std::as_const(dependencies)) {
+ // Skip dependencies that are outside the current app bundle, because this might
+ // cause a codesign error if the current bundle is part of the dependency (e.g.
+ // a bundle is part of a framework helper, and depends on that framework).
+ // The dependencies will be taken care of after the current bundle is signed.
+ if (!dependency.startsWith(appBundleAbsolutePath)) {
+ ++dependenciesSkipped;
+ LogNormal() << "Skipping outside dependency: " << dependency;
+ continue;
+ }
+ pendingBinaries.push(dependency);
+ pendingBinariesSet.insert(dependency);
+ }
+
+ // If all dependencies were skipped, make sure the binary is actually signed, instead
+ // of going into an infinite loop.
+ if (dependenciesSkipped == dependencies.size()) {
+ pendingBinaries.pop();
+ } else {
+ continue;
+ }
+ }
+
+ // Look for an entitlements file in the bundle to include when signing
+ extraEntitlements = findEntitlementsFile(appBundleAbsolutePath + "/Contents/Resources/");
+
+ // All dependencies are signed, now sign this binary.
+ codesignFile(identity, binary);
+ signedBinaries.insert(binary);
+ pendingBinariesSet.remove(binary);
+ }
+
+ LogNormal() << "Finished codesigning " << appBundlePath << "with identity" << identity;
+
+ // Verify code signature
+ QProcess codesign;
+ codesign.start("codesign", QStringList() << "--deep" << "-v" << appBundlePath);
+ codesign.waitForFinished(-1);
+ QByteArray err = codesign.readAllStandardError();
+ if (codesign.exitCode() > 0) {
+ LogError() << "codesign verification error:";
+ LogError() << err;
+ } else if (!err.isEmpty()) {
+ LogDebug() << err;
+ }
+
+ return signedBinaries;
+}
+
+void codesign(const QString &identity, const QString &appBundlePath) {
+ codesignBundle(identity, appBundlePath, QList<QString>());
+}
+
+void createDiskImage(const QString &appBundlePath, const QString &filesystemType)
+{
+ QString appBaseName = appBundlePath;
+ appBaseName.chop(4); // remove ".app" from end
+
+ QString dmgName = appBaseName + ".dmg";
+
+ QFile dmg(dmgName);
+
+ if (dmg.exists() && alwaysOwerwriteEnabled)
+ dmg.remove();
+
+ if (dmg.exists()) {
+ LogNormal() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName();
+ } else {
+ LogNormal() << "Creating disk image (.dmg) for" << appBundlePath;
+ }
+
+ LogNormal() << "Image will use" << filesystemType;
+
+ // More dmg options can be found in the hdiutil man page.
+ QStringList options = QStringList()
+ << "create" << dmgName
+ << "-srcfolder" << appBundlePath
+ << "-format" << "UDZO"
+ << "-fs" << filesystemType
+ << "-volname" << appBaseName;
+
+ QProcess hdutil;
+ hdutil.start("hdiutil", options);
+ hdutil.waitForFinished(-1);
+ if (hdutil.exitCode() != 0) {
+ LogError() << "Bundle creation error:" << hdutil.readAllStandardError();
+ }
+}
+
+void fixupFramework(const QString &frameworkName)
+{
+ // Expected framework name looks like "Foo.framework"
+ QStringList parts = frameworkName.split(".");
+ if (parts.count() < 2) {
+ LogError() << "fixupFramework: Unexpected framework name" << frameworkName;
+ return;
+ }
+
+ // Assume framework binary path is Foo.framework/Foo
+ QString frameworkBinary = frameworkName + QStringLiteral("/") + parts[0];
+
+ // Xcode expects to find Foo.framework/Versions/A when code
+ // signing, while qmake typically generates numeric versions.
+ // Create symlink to the actual version in the framework.
+ linkFilePrintStatus("Current", frameworkName + "/Versions/A");
+
+ // Set up @rpath structure.
+ changeIdentification("@rpath/" + frameworkBinary, frameworkBinary);
+ addRPath("@loader_path/../../Contents/Frameworks/", frameworkBinary);
+}
diff --git a/src/tools/macdeployqt/shared/shared.h b/src/tools/macdeployqt/shared/shared.h
new file mode 100644
index 0000000000..33384e868a
--- /dev/null
+++ b/src/tools/macdeployqt/shared/shared.h
@@ -0,0 +1,119 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef MAC_DEPLOMYMENT_SHARED_H
+#define MAC_DEPLOMYMENT_SHARED_H
+
+#include <QString>
+#include <QStringList>
+#include <QDebug>
+#include <QSet>
+#include <QVersionNumber>
+
+extern int logLevel;
+#define LogError() if (logLevel < 0) {} else qDebug() << "ERROR:"
+#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:"
+#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:"
+#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:"
+
+extern bool runStripEnabled;
+
+class FrameworkInfo
+{
+public:
+ bool isDylib;
+ QString frameworkDirectory;
+ QString frameworkName;
+ QString frameworkPath;
+ QString binaryDirectory;
+ QString binaryName;
+ QString binaryPath;
+ QString rpathUsed;
+ QString version;
+ QString installName;
+ QString deployedInstallName;
+ QString sourceFilePath;
+ QString frameworkDestinationDirectory;
+ QString binaryDestinationDirectory;
+
+ bool isDebugLibrary() const
+ {
+ if (isDylib)
+ return binaryName.contains(QStringLiteral("_debug."));
+ else
+ return binaryName.endsWith(QStringLiteral("_debug"));
+ }
+};
+
+class DylibInfo
+{
+public:
+ QString binaryPath;
+ QVersionNumber currentVersion;
+ QVersionNumber compatibilityVersion;
+};
+
+class OtoolInfo
+{
+public:
+ QString installName;
+ QString binaryPath;
+ QVersionNumber currentVersion;
+ QVersionNumber compatibilityVersion;
+ QList<DylibInfo> dependencies;
+};
+
+bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
+QDebug operator<<(QDebug debug, const FrameworkInfo &info);
+
+class ApplicationBundleInfo
+{
+ public:
+ QString path;
+ QString binaryPath;
+ QStringList libraryPaths;
+};
+
+class DeploymentInfo
+{
+public:
+ QString qtPath;
+ QString pluginPath;
+ QStringList deployedFrameworks;
+ QList<QString> rpathsUsed;
+ bool useLoaderPath;
+ bool isFramework;
+ bool isDebug;
+
+ bool containsModule(const QString &module, const QString &libInFix) const;
+};
+
+inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
+
+OtoolInfo findDependencyInfo(const QString &binaryPath);
+FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
+QString findAppBinary(const QString &appBundlePath);
+QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
+QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
+QString copyFramework(const FrameworkInfo &framework, const QString path);
+DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
+DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
+void createQtConf(const QString &appBundlePath);
+void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
+bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths);
+void changeIdentification(const QString &id, const QString &binaryPath);
+void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
+void runStrip(const QString &binaryPath);
+void stripAppBinary(const QString &bundlePath);
+QString findAppBinary(const QString &appBundlePath);
+QStringList findAppFrameworkNames(const QString &appBundlePath);
+QStringList findAppFrameworkPaths(const QString &appBundlePath);
+void codesignFile(const QString &identity, const QString &filePath);
+QSet<QString> codesignBundle(const QString &identity,
+ const QString &appBundlePath,
+ QList<QString> additionalBinariesContainingRpaths);
+void codesign(const QString &identity, const QString &appBundlePath);
+void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
+void fixupFramework(const QString &appBundlePath);
+
+
+#endif
diff --git a/src/tools/moc/CMakeLists.txt b/src/tools/moc/CMakeLists.txt
index 88dce045f8..b98b7ab4e9 100644
--- a/src/tools/moc/CMakeLists.txt
+++ b/src/tools/moc/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from moc.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## moc Tool:
@@ -6,10 +7,11 @@
qt_get_tool_target_name(target_name moc)
qt_internal_add_tool(${target_name}
- BOOTSTRAP
+ TRY_RUN
+ CORE_LIBRARY Bootstrap
TARGET_DESCRIPTION "Qt Meta Object Compiler"
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Core # special case
+ TOOLS_TARGET Core
SOURCES
cbordevice.h
collectjson.cpp collectjson.h
@@ -19,7 +21,6 @@ qt_internal_add_tool(${target_name}
outputrevision.h
parser.cpp parser.h
preprocessor.cpp preprocessor.h
- # qdatetime_p.h special case remove
symbols.h
token.cpp token.h
utils.h
@@ -27,17 +28,15 @@ qt_internal_add_tool(${target_name}
QT_MOC
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_FROM_BYTEARRAY
- QT_NO_COMPRESS
QT_NO_FOREACH
+ QT_NO_QPAIR
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
../../3rdparty/tinycbor/src
../shared
)
-
-#### Keys ignored in scope 1:.:.:moc.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt Meta Object Compiler"
-# _OPTION = "host_build"
+qt_internal_return_unless_building_tools()
## Scopes:
#####################################################################
diff --git a/src/tools/moc/cbordevice.h b/src/tools/moc/cbordevice.h
index dbfc537dd2..7668e4c0be 100644
--- a/src/tools/moc/cbordevice.h
+++ b/src/tools/moc/cbordevice.h
@@ -1,34 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CBORDEVICE_H
#define CBORDEVICE_H
+#include <QtCore/qtypes.h>
+
#include <memory>
#include <stdio.h>
diff --git a/src/tools/moc/collectjson.cpp b/src/tools/moc/collectjson.cpp
index 6577a3216b..d542e2abc4 100644
--- a/src/tools/moc/collectjson.cpp
+++ b/src/tools/moc/collectjson.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qfile.h>
#include <qjsonarray.h>
@@ -85,7 +60,7 @@ int collectJson(const QStringList &jsonFiles, const QString &outputFile, bool sk
QStringList jsonFilesSorted = jsonFiles;
jsonFilesSorted.sort();
- for (const QString &jsonFile : qAsConst(jsonFilesSorted)) {
+ for (const QString &jsonFile : std::as_const(jsonFilesSorted)) {
QFile f(jsonFile);
if (!f.open(QIODevice::ReadOnly)) {
fprintf(stderr, "Error opening %s for reading\n", qPrintable(jsonFile));
diff --git a/src/tools/moc/collectjson.h b/src/tools/moc/collectjson.h
index 3a33952a54..b16ae61519 100644
--- a/src/tools/moc/collectjson.h
+++ b/src/tools/moc/collectjson.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef COLLECTJSON_H
#define COLLECTJSON_H
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index a21e44ba8f..02e9ef178a 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "generator.h"
#include "cbordevice.h"
@@ -48,6 +23,8 @@
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
uint nameToBuiltinType(const QByteArray &name)
{
if (name.isEmpty())
@@ -80,11 +57,12 @@ QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING)
return nullptr;
}
- Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes,
+ Generator::Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes,
const QHash<QByteArray, QByteArray> &knownQObjectClasses,
const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile,
bool requireCompleteTypes)
- : out(outfile),
+ : parser(moc),
+ out(outfile),
cdef(classDef),
metaTypes(metaTypes),
knownQObjectClasses(knownQObjectClasses),
@@ -92,32 +70,53 @@ QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING)
requireCompleteTypes(requireCompleteTypes)
{
if (cdef->superclassList.size())
- purestSuperClass = cdef->superclassList.constFirst().first;
+ purestSuperClass = cdef->superclassList.constFirst().classname;
}
-static inline int lengthOfEscapeSequence(const QByteArray &s, int i)
+static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i)
{
- if (s.at(i) != '\\' || i >= s.length() - 1)
+ if (s.at(i) != '\\' || i >= s.size() - 1)
return 1;
- const int startPos = i;
+ const qsizetype startPos = i;
++i;
char ch = s.at(i);
if (ch == 'x') {
++i;
- while (i < s.length() && is_hex_char(s.at(i)))
+ while (i < s.size() && isHexDigit(s.at(i)))
++i;
- } else if (is_octal_char(ch)) {
+ } else if (isOctalDigit(ch)) {
while (i < startPos + 4
- && i < s.length()
- && is_octal_char(s.at(i))) {
+ && i < s.size()
+ && isOctalDigit(s.at(i))) {
++i;
}
} else { // single character escape sequence
- i = qMin(i + 1, s.length());
+ i = qMin(i + 1, s.size());
}
return i - startPos;
}
+// Prints \a s to \a out, breaking it into lines of at most ColumnWidth. The
+// opening and closing quotes are NOT included (it's up to the caller).
+static void printStringWithIndentation(FILE *out, const QByteArray &s)
+{
+ static constexpr int ColumnWidth = 72;
+ const qsizetype len = s.size();
+ qsizetype idx = 0;
+
+ do {
+ qsizetype spanLen = qMin(ColumnWidth - 2, len - idx);
+ // don't cut escape sequences at the end of a line
+ const qsizetype backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1);
+ if (backSlashPos >= idx) {
+ const qsizetype escapeLen = lengthOfEscapeSequence(s, backSlashPos);
+ spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, len - idx);
+ }
+ fprintf(out, "\n \"%.*s\"", int(spanLen), s.constData() + idx);
+ idx += spanLen;
+ } while (idx < len);
+}
+
void Generator::strreg(const QByteArray &s)
{
if (!strings.contains(s))
@@ -126,7 +125,7 @@ void Generator::strreg(const QByteArray &s)
int Generator::stridx(const QByteArray &s)
{
- int i = strings.indexOf(s);
+ int i = int(strings.indexOf(s));
Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings");
return i;
}
@@ -137,8 +136,8 @@ int Generator::stridx(const QByteArray &s)
static int aggregateParameterCount(const QList<FunctionDef> &list)
{
int sum = 0;
- for (int i = 0; i < list.count(); ++i)
- sum += list.at(i).arguments.count() + 1; // +1 for return type
+ for (const FunctionDef &def : list)
+ sum += int(def.arguments.size()) + 1; // +1 for return type
return sum;
}
@@ -175,14 +174,14 @@ bool Generator::registerableMetaType(const QByteArray &propertyType)
#undef STREAM_1ARG_TEMPLATE
;
for (const QByteArray &oneArgTemplateType : oneArgTemplates) {
- QByteArray ba = oneArgTemplateType + "<";
+ const QByteArray ba = oneArgTemplateType + "<";
if (propertyType.startsWith(ba) && propertyType.endsWith(">")) {
- const int argumentSize = propertyType.size() - oneArgTemplateType.size() - 1
+ const qsizetype argumentSize = propertyType.size() - ba.size()
// The closing '>'
- 1
// templates inside templates have an extra whitespace char to strip.
- (propertyType.at(propertyType.size() - 2) == ' ' ? 1 : 0 );
- const QByteArray templateArg = propertyType.mid(oneArgTemplateType.size() + 1, argumentSize);
+ const QByteArray templateArg = propertyType.sliced(ba.size(), argumentSize);
return isBuiltinType(templateArg) || registerableMetaType(templateArg);
}
}
@@ -195,12 +194,28 @@ static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArra
{
if (qualifiedName == name)
return true;
- int index = qualifiedName.indexOf("::");
+ const qsizetype index = qualifiedName.indexOf("::");
if (index == -1)
return false;
return qualifiedNameEquals(qualifiedName.mid(index+2), name);
}
+static QByteArray generateQualifiedClassNameIdentifier(const QByteArray &identifier)
+{
+ QByteArray qualifiedClassNameIdentifier = identifier;
+
+ // Remove ':'s in the name, but be sure not to create any illegal
+ // identifiers in the process. (Don't replace with '_', because
+ // that will create problems with things like NS_::_class.)
+ qualifiedClassNameIdentifier.replace("::", "SCOPE");
+
+ // Also, avoid any leading/trailing underscores (we'll concatenate
+ // the generated name with other prefixes/suffixes, and these latter
+ // may already include an underscore, leading to two underscores)
+ qualifiedClassNameIdentifier = "CLASS" + qualifiedClassNameIdentifier + "ENDCLASS";
+ return qualifiedClassNameIdentifier;
+}
+
void Generator::generateCode()
{
bool isQObject = (cdef->classname == "QObject");
@@ -209,8 +224,7 @@ void Generator::generateCode()
// filter out undeclared enumerators and sets
{
QList<EnumDef> enumList;
- for (int i = 0; i < cdef->enumList.count(); ++i) {
- EnumDef def = cdef->enumList.at(i);
+ for (EnumDef def : std::as_const(cdef->enumList)) {
if (cdef->enumDeclarations.contains(def.name)) {
enumList += def;
}
@@ -237,128 +251,58 @@ void Generator::generateCode()
registerPropertyStrings();
registerEnumStrings();
- QByteArray qualifiedClassNameIdentifier = cdef->qualified;
- qualifiedClassNameIdentifier.replace(':', '_');
+ const bool hasStaticMetaCall =
+ (cdef->hasQObject || !cdef->methodList.isEmpty()
+ || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
+
+ const QByteArray qualifiedClassNameIdentifier = generateQualifiedClassNameIdentifier(cdef->qualified);
+
+ // ensure the qt_meta_stringdata_XXXX_t type is local
+ fprintf(out, "namespace {\n");
//
-// Build stringdata struct
+// Build the strings using QtMocHelpers::StringData
//
- const int constCharArraySizeLimit = 65535;
- fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData());
- fprintf(out, " const uint offsetsAndSize[%d];\n", int(strings.size()*2));
- {
- int stringDataLength = 0;
- int stringDataCounter = 0;
- for (int i = 0; i < strings.size(); ++i) {
- int thisLength = strings.at(i).length() + 1;
- stringDataLength += thisLength;
- if (stringDataLength / constCharArraySizeLimit) {
- // save previous stringdata and start computing the next one.
- fprintf(out, " char stringdata%d[%d];\n", stringDataCounter++, stringDataLength - thisLength);
- stringDataLength = thisLength;
- }
- }
- fprintf(out, " char stringdata%d[%d];\n", stringDataCounter, stringDataLength);
- }
- fprintf(out, "};\n");
-
- // Macro that expands into a QByteArrayData. The offset member is
- // calculated from 1) the offset of the actual characters in the
- // stringdata.stringdata member, and 2) the stringdata.data index of the
- // QByteArrayData being defined. This calculation relies on the
- // QByteArrayData::data() implementation returning simply "this + offset".
- fprintf(out, "#define QT_MOC_LITERAL(ofs, len) \\\n"
- " uint(offsetof(qt_meta_stringdata_%s_t, stringdata0) + ofs), len \n",
- qualifiedClassNameIdentifier.constData());
-
- fprintf(out, "static const qt_meta_stringdata_%s_t qt_meta_stringdata_%s = {\n",
+ fprintf(out, "\n#ifdef QT_MOC_HAS_STRINGDATA\n"
+ "struct qt_meta_stringdata_%s_t {};\n"
+ "constexpr auto qt_meta_stringdata_%s = QtMocHelpers::stringData(",
qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData());
- fprintf(out, " {\n");
{
- int idx = 0;
- for (int i = 0; i < strings.size(); ++i) {
- const QByteArray &str = strings.at(i);
- fprintf(out, "QT_MOC_LITERAL(%d, %d)", idx, int(str.length()));
- if (i != strings.size() - 1)
- fputc(',', out);
- const QByteArray comment = str.length() > 32 ? str.left(29) + "..." : str;
- fprintf(out, " // \"%s\"\n", comment.size() ? comment.constData() : "");
- idx += str.length() + 1;
- for (int j = 0; j < str.length(); ++j) {
- if (str.at(j) == '\\') {
- int cnt = lengthOfEscapeSequence(str, j) - 1;
- idx -= cnt;
- j += cnt;
- }
- }
+ char comma = 0;
+ for (const QByteArray &str : strings) {
+ if (comma)
+ fputc(comma, out);
+ printStringWithIndentation(out, str);
+ comma = ',';
}
- fprintf(out, "\n },\n");
}
-
-//
-// Build stringdata array
-//
- fprintf(out, " \"");
- int col = 0;
- int len = 0;
- int stringDataLength = 0;
- for (int i = 0; i < strings.size(); ++i) {
- QByteArray s = strings.at(i);
- len = s.length();
- stringDataLength += len + 1;
- if (stringDataLength >= constCharArraySizeLimit) {
- fprintf(out, "\",\n \"");
- stringDataLength = len + 1;
- col = 0;
- } else if (i)
- fputs("\\0", out); // add \0 at the end of each string
-
- if (col && col + len >= 72) {
- fprintf(out, "\"\n \"");
- col = 0;
- } else if (len && s.at(0) >= '0' && s.at(0) <= '9') {
- fprintf(out, "\"\"");
- len += 2;
- }
- int idx = 0;
- while (idx < s.length()) {
- if (idx > 0) {
- col = 0;
- fprintf(out, "\"\n \"");
- }
- int spanLen = qMin(70, s.length() - idx);
- // don't cut escape sequences at the end of a line
- int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1);
- if (backSlashPos >= idx) {
- int escapeLen = lengthOfEscapeSequence(s, backSlashPos);
- spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx);
- }
- fprintf(out, "%.*s", spanLen, s.constData() + idx);
- idx += spanLen;
- col += spanLen;
- }
- col += len + 2;
- }
-
-// Terminate stringdata struct
- fprintf(out, "\"\n};\n");
- fprintf(out, "#undef QT_MOC_LITERAL\n\n");
+ fprintf(out, "\n);\n"
+ "#else // !QT_MOC_HAS_STRINGDATA\n");
+ fprintf(out, "#error \"qtmochelpers.h not found or too old.\"\n");
+ fprintf(out, "#endif // !QT_MOC_HAS_STRINGDATA\n");
+ fprintf(out, "} // unnamed namespace\n\n");
//
// build the data array
//
int index = MetaObjectPrivateFieldCount;
- fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData());
+ fprintf(out, "Q_CONSTINIT static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData());
fprintf(out, "\n // content:\n");
fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision));
fprintf(out, " %4d, // classname\n", stridx(cdef->qualified));
- fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.count()), int(cdef->classInfoList.count() ? index : 0));
- index += cdef->classInfoList.count() * 2;
+ fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.size()), int(cdef->classInfoList.size() ? index : 0));
+ index += cdef->classInfoList.size() * 2;
+
+ qsizetype methodCount = 0;
+ if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount)
+ || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) {
+ parser->error("internal limit exceeded: the total number of member functions"
+ " (including signals and slots) is too big.");
+ }
- int methodCount = cdef->signalList.count() + cdef->slotList.count() + cdef->methodList.count();
- fprintf(out, " %4d, %4d, // methods\n", methodCount, methodCount ? index : 0);
+ fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0);
index += methodCount * QMetaObjectPrivate::IntsPerMethod;
if (cdef->revisionedMethods)
index += methodCount;
@@ -369,16 +313,17 @@ void Generator::generateCode()
+ aggregateParameterCount(cdef->constructorList);
index += totalParameterCount * 2 // types and parameter names
- methodCount // return "parameters" don't have names
- - cdef->constructorList.count(); // "this" parameters don't have names
+ - int(cdef->constructorList.size()); // "this" parameters don't have names
- fprintf(out, " %4d, %4d, // properties\n", int(cdef->propertyList.count()), int(cdef->propertyList.count() ? index : 0));
- index += cdef->propertyList.count() * QMetaObjectPrivate::IntsPerProperty;
- fprintf(out, " %4d, %4d, // enums/sets\n", int(cdef->enumList.count()), cdef->enumList.count() ? index : 0);
+ fprintf(out, " %4d, %4d, // properties\n", int(cdef->propertyList.size()), int(cdef->propertyList.size() ? index : 0));
+ index += cdef->propertyList.size() * QMetaObjectPrivate::IntsPerProperty;
+ fprintf(out, " %4d, %4d, // enums/sets\n", int(cdef->enumList.size()), cdef->enumList.size() ? index : 0);
int enumsIndex = index;
- for (int i = 0; i < cdef->enumList.count(); ++i)
- index += 5 + (cdef->enumList.at(i).values.count() * 2);
- fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.count()) : 0,
+ for (const EnumDef &def : std::as_const(cdef->enumList))
+ index += QMetaObjectPrivate::IntsPerEnum + (def.values.size() * 2);
+
+ fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0,
isConstructible ? index : 0);
int flags = 0;
@@ -388,7 +333,7 @@ void Generator::generateCode()
flags |= PropertyAccessInStaticMetaCall;
}
fprintf(out, " %4d, // flags\n", flags);
- fprintf(out, " %4d, // signalCount\n", int(cdef->signalList.count()));
+ fprintf(out, " %4d, // signalCount\n", int(cdef->signalList.size()));
//
@@ -396,8 +341,14 @@ void Generator::generateCode()
//
generateClassInfos();
- // all property metatypes, + 1 for the type of the current class itself
- int initialMetaTypeOffset = cdef->propertyList.count() + 1;
+ qsizetype propEnumCount = 0;
+ // all property metatypes + all enum metatypes + 1 for the type of the current class itself
+ if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount)
+ || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount)
+ || propEnumCount >= std::numeric_limits<int>::max()) {
+ parser->error("internal limit exceeded: number of property and enum metatypes is too big.");
+ }
+ int initialMetaTypeOffset = int(propEnumCount);
//
// Build signals array first, otherwise the signal indices would be wrong
@@ -454,30 +405,20 @@ void Generator::generateCode()
fprintf(out, "\n 0 // eod\n};\n\n");
//
-// Generate internal qt_static_metacall() function
-//
- const bool hasStaticMetaCall =
- (cdef->hasQObject || !cdef->methodList.isEmpty()
- || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
- if (hasStaticMetaCall)
- generateStaticMetacall();
-
-//
// Build extra array
//
QList<QByteArray> extraList;
QMultiHash<QByteArray, QByteArray> knownExtraMetaObject(knownGadgets);
knownExtraMetaObject.unite(knownQObjectClasses);
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
if (isBuiltinType(p.type))
continue;
if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>'))
continue;
- int s = p.type.lastIndexOf("::");
+ const qsizetype s = p.type.lastIndexOf("::");
if (s <= 0)
continue;
@@ -488,7 +429,7 @@ void Generator::generateCode()
QByteArray thisScope = cdef->qualified;
do {
- int s = thisScope.lastIndexOf("::");
+ const qsizetype s = thisScope.lastIndexOf("::");
thisScope = thisScope.left(s);
QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope;
scopeIt = knownExtraMetaObject.constFind(currentScope);
@@ -514,7 +455,7 @@ void Generator::generateCode()
for (auto it = cdef->enumDeclarations.keyBegin(),
end = cdef->enumDeclarations.keyEnd(); it != end; ++it) {
const QByteArray &enumKey = *it;
- int s = enumKey.lastIndexOf("::");
+ const qsizetype s = enumKey.lastIndexOf("::");
if (s > 0) {
QByteArray scope = enumKey.left(s);
if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope))
@@ -527,28 +468,29 @@ void Generator::generateCode()
//
if (!extraList.isEmpty()) {
- fprintf(out, "static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n",
+ fprintf(out, "Q_CONSTINIT static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n",
qualifiedClassNameIdentifier.constData());
- for (int i = 0; i < extraList.count(); ++i) {
- fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", extraList.at(i).constData());
- }
+ for (const QByteArray &ba : std::as_const(extraList))
+ fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", ba.constData());
+
fprintf(out, " nullptr\n};\n\n");
}
//
// Finally create and initialize the static meta object
//
- fprintf(out, "const QMetaObject %s::staticMetaObject = { {\n", cdef->qualified.constData());
+ fprintf(out, "Q_CONSTINIT const QMetaObject %s::staticMetaObject = { {\n",
+ cdef->qualified.constData());
if (isQObject)
fprintf(out, " nullptr,\n");
else if (cdef->superclassList.size() && !cdef->hasQGadget && !cdef->hasQNamespace) // for qobject, we know the super class must have a static metaobject
fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", purestSuperClass.constData());
else if (cdef->superclassList.size()) // for gadgets we need to query at compile time for it
- fprintf(out, " QtPrivate::MetaObjectForType<%s>::value(),\n", purestSuperClass.constData());
+ fprintf(out, " QtPrivate::MetaObjectForType<%s>::value,\n", purestSuperClass.constData());
else
fprintf(out, " nullptr,\n");
- fprintf(out, " qt_meta_stringdata_%s.offsetsAndSize,\n"
+ fprintf(out, " qt_meta_stringdata_%s.offsetsAndSizes,\n"
" qt_meta_data_%s,\n", qualifiedClassNameIdentifier.constData(),
qualifiedClassNameIdentifier.constData());
if (hasStaticMetaCall)
@@ -561,63 +503,75 @@ void Generator::generateCode()
else
fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData());
- bool needsComma = false;
+ const char *comma = "";
const bool requireCompleteness = requireCompleteTypes || cdef->requireCompleteMethodTypes;
+ auto stringForType = [requireCompleteness](const QByteArray &type, bool forceComplete) -> QByteArray {
+ const char *forceCompleteType = forceComplete ? ", std::true_type>" : ", std::false_type>";
+ if (requireCompleteness)
+ return type;
+ return "QtPrivate::TypeAndForceComplete<" % type % forceCompleteType;
+ };
if (!requireCompleteness) {
- fprintf(out, "qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t\n", qualifiedClassNameIdentifier.constData());
- needsComma = true;
+ fprintf(out, " qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t", qualifiedClassNameIdentifier.constData());
+ comma = ",";
} else {
- fprintf(out, "qt_metaTypeArray<\n");
+ fprintf(out, " qt_metaTypeArray<");
}
// metatypes for properties
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
- if (requireCompleteness)
- fprintf(out, "%s%s", needsComma ? ", " : "", p.type.data());
- else
- fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::true_type>", needsComma ? ", " : "", p.type.data());
- needsComma = true;
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
+ fprintf(out, "%s\n // property '%s'\n %s",
+ comma, p.name.constData(), stringForType(p.type, true).constData());
+ comma = ",";
+ }
+
+ // metatypes for enums
+ for (const EnumDef &e : std::as_const(cdef->enumList)) {
+ fprintf(out, "%s\n // enum '%s'\n %s",
+ comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData());
+ comma = ",";
}
+
// type name for the Q_OJBECT/GADGET itself, void for namespaces
auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void";
- if (requireCompleteness)
- fprintf(out, "%s%s", needsComma ? ", " : "", ownType);
- else
- fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::true_type>", needsComma ? ", " : "", ownType);
+ fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s",
+ comma, stringForType(ownType, true).constData());
+ comma = ",";
// metatypes for all exposed methods
- // no need to check for needsComma any longer, as we always need one due to the classname being present
- for (const QList<FunctionDef> &methodContainer :
- { cdef->signalList, cdef->slotList, cdef->methodList }) {
- for (int i = 0; i< methodContainer.count(); ++i) {
- const FunctionDef& fdef = methodContainer.at(i);
- if (requireCompleteness)
- fprintf(out, ", %s", fdef.type.name.data());
- else
- fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", fdef.type.name.data());
- for (const auto &argument: fdef.arguments) {
- if (requireCompleteness)
- fprintf(out, ", %s", argument.type.name.data());
- else
- fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", argument.type.name.data());
- }
+ // because we definitely printed something above, this section doesn't need comma control
+ const auto allMethods = {&cdef->signalList, &cdef->slotList, &cdef->methodList};
+ for (const QList<FunctionDef> *methodContainer : allMethods) {
+ for (const FunctionDef &fdef : *methodContainer) {
+ fprintf(out, ",\n // method '%s'\n %s",
+ fdef.name.constData(), stringForType(fdef.type.name, false).constData());
+ for (const auto &argument: fdef.arguments)
+ fprintf(out, ",\n %s", stringForType(argument.type.name, false).constData());
}
- fprintf(out, "\n");
}
- for (int i = 0; i< cdef->constructorList.count(); ++i) {
- const FunctionDef& fdef = cdef->constructorList.at(i);
+
+ // but constructors have no return types, so this needs comma control again
+ for (const FunctionDef &fdef : std::as_const(cdef->constructorList)) {
+ if (fdef.arguments.isEmpty())
+ continue;
+
+ fprintf(out, "%s\n // constructor '%s'", comma, fdef.name.constData());
+ comma = "";
for (const auto &argument: fdef.arguments) {
- if (requireCompleteness)
- fprintf(out, ", %s", argument.type.name.data());
- else
- fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", argument.type.name.data());
+ fprintf(out, "%s\n %s", comma,
+ stringForType(argument.type.name, false).constData());
+ comma = ",";
}
}
- fprintf(out, "\n");
- fprintf(out, ">,\n");
+ fprintf(out, "\n >,\n");
fprintf(out, " nullptr\n} };\n\n");
+//
+// Generate internal qt_static_metacall() function
+//
+ if (hasStaticMetaCall)
+ generateStaticMetacall();
+
if (!cdef->hasQObject)
return;
@@ -633,18 +587,24 @@ void Generator::generateCode()
fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n"
" return static_cast<void*>(this);\n",
qualifiedClassNameIdentifier.constData());
- for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one
- if (cdef->superclassList.at(i).second == FunctionDef::Private)
- continue;
- const char *cname = cdef->superclassList.at(i).first.constData();
- fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n",
- cname, cname);
+
+ // for all superclasses but the first one
+ if (cdef->superclassList.size() > 1) {
+ auto it = cdef->superclassList.cbegin() + 1;
+ const auto end = cdef->superclassList.cend();
+ for (; it != end; ++it) {
+ if (it->access == FunctionDef::Private)
+ continue;
+ const char *cname = it->classname.constData();
+ fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n",
+ cname, cname);
+ }
}
- for (int i = 0; i < cdef->interfaceList.size(); ++i) {
- const QList<ClassDef::Interface> &iface = cdef->interfaceList.at(i);
- for (int j = 0; j < iface.size(); ++j) {
+
+ for (const QList<ClassDef::Interface> &iface : std::as_const(cdef->interfaceList)) {
+ for (qsizetype j = 0; j < iface.size(); ++j) {
fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData());
- for (int k = j; k >= 0; --k)
+ for (qsizetype k = j; k >= 0; --k)
fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData());
fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData());
}
@@ -665,8 +625,8 @@ void Generator::generateCode()
//
// Generate internal signal functions
//
- for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex)
- generateSignal(&cdef->signalList[signalindex], signalindex);
+ for (int signalindex = 0; signalindex < int(cdef->signalList.size()); ++signalindex)
+ generateSignal(&cdef->signalList.at(signalindex), signalindex);
//
// Generate plugin meta data
@@ -677,12 +637,29 @@ void Generator::generateCode()
// Generate function to make sure the non-class signals exist in the parent classes
//
if (!cdef->nonClassSignalList.isEmpty()) {
- fprintf(out, "// If you get a compile error in this function it can be because either\n");
- fprintf(out, "// a) You are using a NOTIFY signal that does not exist. Fix it.\n");
- fprintf(out, "// b) You are using a NOTIFY signal that does exist (in a parent class) but has a non-empty parameter list. This is a moc limitation.\n");
- fprintf(out, "[[maybe_unused]] static void checkNotifySignalValidity_%s(%s *t) {\n", qualifiedClassNameIdentifier.constData(), cdef->qualified.constData());
- for (const QByteArray &nonClassSignal : qAsConst(cdef->nonClassSignalList))
- fprintf(out, " t->%s();\n", nonClassSignal.constData());
+ fprintf(out, "namespace CheckNotifySignalValidity_%s {\n", qualifiedClassNameIdentifier.constData());
+ for (const QByteArray &nonClassSignal : std::as_const(cdef->nonClassSignalList)) {
+ const auto propertyIt = std::find_if(cdef->propertyList.constBegin(),
+ cdef->propertyList.constEnd(),
+ [&nonClassSignal](const PropertyDef &p) {
+ return nonClassSignal == p.notify;
+ });
+ // must find something, otherwise checkProperties wouldn't have inserted an entry into nonClassSignalList
+ Q_ASSERT(propertyIt != cdef->propertyList.constEnd());
+ fprintf(out, "template<typename T> using has_nullary_%s = decltype(std::declval<T>().%s());\n",
+ nonClassSignal.constData(),
+ nonClassSignal.constData());
+ const auto &propertyType = propertyIt->type;
+ fprintf(out, "template<typename T> using has_unary_%s = decltype(std::declval<T>().%s(std::declval<%s>()));\n",
+ nonClassSignal.constData(),
+ nonClassSignal.constData(),
+ propertyType.constData());
+ fprintf(out, "static_assert(qxp::is_detected_v<has_nullary_%s, %s> || qxp::is_detected_v<has_unary_%s, %s>,\n"
+ " \"NOTIFY signal %s does not exist in class (or is private in its parent)\");\n",
+ nonClassSignal.constData(), cdef->qualified.constData(),
+ nonClassSignal.constData(), cdef->qualified.constData(),
+ nonClassSignal.constData());
+ }
fprintf(out, "}\n");
}
}
@@ -690,8 +667,7 @@ void Generator::generateCode()
void Generator::registerClassInfoStrings()
{
- for (int i = 0; i < cdef->classInfoList.size(); ++i) {
- const ClassInfoDef &c = cdef->classInfoList.at(i);
+ for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) {
strreg(c.name);
strreg(c.value);
}
@@ -704,25 +680,19 @@ void Generator::generateClassInfos()
fprintf(out, "\n // classinfo: key, value\n");
- for (int i = 0; i < cdef->classInfoList.size(); ++i) {
- const ClassInfoDef &c = cdef->classInfoList.at(i);
+ for (const ClassInfoDef &c : std::as_const(cdef->classInfoList))
fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value));
- }
}
void Generator::registerFunctionStrings(const QList<FunctionDef> &list)
{
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
-
+ for (const FunctionDef &f : list) {
strreg(f.name);
if (!isBuiltinType(f.normalizedType))
strreg(f.normalizedType);
strreg(f.tag);
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
+ for (const ArgumentDef &a : f.arguments) {
if (!isBuiltinType(a.normalizedType))
strreg(a.normalizedType);
strreg(a.name);
@@ -743,9 +713,7 @@ void Generator::generateFunctions(const QList<FunctionDef> &list, const char *fu
return;
fprintf(out, "\n // %ss: name, argc, parameters, tag, flags, initial metatype offsets\n", functype);
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
-
+ for (const FunctionDef &f : list) {
QByteArray comment;
uint flags = type;
if (f.access == FunctionDef::Private) {
@@ -780,7 +748,7 @@ void Generator::generateFunctions(const QList<FunctionDef> &list, const char *fu
comment.append(" | MethodIsConst ");
}
- int argc = f.arguments.count();
+ const int argc = int(f.arguments.size());
fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x, %4d /* %s */,\n",
stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, initialMetatypeOffset, comment.constData());
@@ -792,12 +760,10 @@ void Generator::generateFunctions(const QList<FunctionDef> &list, const char *fu
void Generator::generateFunctionRevisions(const QList<FunctionDef> &list, const char *functype)
{
- if (list.count())
+ if (list.size())
fprintf(out, "\n // %ss: revision\n", functype);
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
+ for (const FunctionDef &f : list)
fprintf(out, " %4d,\n", f.revision);
- }
}
void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const char *functype)
@@ -805,25 +771,22 @@ void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const
if (list.isEmpty())
return;
fprintf(out, "\n // %ss: parameters\n", functype);
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
+ for (const FunctionDef &f : list) {
fprintf(out, " ");
// Types
- int argsCount = f.arguments.count();
- for (int j = -1; j < argsCount; ++j) {
- if (j > -1)
- fputc(' ', out);
- const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType;
- generateTypeInfo(typeName, /*allowEmptyName=*/f.isConstructor);
+ const bool allowEmptyName = f.isConstructor;
+ generateTypeInfo(f.normalizedType, allowEmptyName);
+ fputc(',', out);
+ for (const ArgumentDef &arg : f.arguments) {
+ fputc(' ', out);
+ generateTypeInfo(arg.normalizedType, allowEmptyName);
fputc(',', out);
}
// Parameter names
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &arg = f.arguments.at(j);
+ for (const ArgumentDef &arg : f.arguments)
fprintf(out, " %4d,", stridx(arg.name));
- }
fprintf(out, "\n");
}
@@ -856,8 +819,7 @@ void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName
void Generator::registerPropertyStrings()
{
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
strreg(p.name);
if (!isBuiltinType(p.type))
strreg(p.type);
@@ -870,10 +832,9 @@ void Generator::generateProperties()
// Create meta data
//
- if (cdef->propertyList.count())
+ if (cdef->propertyList.size())
fprintf(out, "\n // properties: name, type, flags\n");
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
uint flags = Invalid;
if (!isBuiltinType(p.type))
flags |= EnumOrFlag;
@@ -917,7 +878,7 @@ void Generator::generateProperties()
int notifyId = p.notifyId;
if (p.notifyId < -1) {
// signal is in parent class
- const int indexInStrings = strings.indexOf(p.notify);
+ const int indexInStrings = int(strings.indexOf(p.notify));
notifyId = indexInStrings | IsUnresolvedSignal;
}
fprintf(out, ", 0x%.8x, uint(%d), %d,\n", flags, notifyId, p.revision);
@@ -926,13 +887,12 @@ void Generator::generateProperties()
void Generator::registerEnumStrings()
{
- for (int i = 0; i < cdef->enumList.count(); ++i) {
- const EnumDef &e = cdef->enumList.at(i);
+ for (const EnumDef &e : std::as_const(cdef->enumList)) {
strreg(e.name);
if (!e.enumName.isNull())
strreg(e.enumName);
- for (int j = 0; j < e.values.count(); ++j)
- strreg(e.values.at(j));
+ for (const QByteArray &val : e.values)
+ strreg(val);
}
}
@@ -942,9 +902,9 @@ void Generator::generateEnums(int index)
return;
fprintf(out, "\n // enums: name, alias, flags, count, data\n");
- index += 5 * cdef->enumList.count();
+ index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size();
int i;
- for (i = 0; i < cdef->enumList.count(); ++i) {
+ for (i = 0; i < cdef->enumList.size(); ++i) {
const EnumDef &e = cdef->enumList.at(i);
int flags = 0;
if (cdef->enumDeclarations.value(e.name))
@@ -955,16 +915,14 @@ void Generator::generateEnums(int index)
stridx(e.name),
e.enumName.isNull() ? stridx(e.name) : stridx(e.enumName),
flags,
- int(e.values.count()),
+ int(e.values.size()),
index);
- index += e.values.count() * 2;
+ index += e.values.size() * 2;
}
fprintf(out, "\n // enum data: key, value\n");
- for (i = 0; i < cdef->enumList.count(); ++i) {
- const EnumDef &e = cdef->enumList.at(i);
- for (int j = 0; j < e.values.count(); ++j) {
- const QByteArray &val = e.values.at(j);
+ for (const EnumDef &e : std::as_const(cdef->enumList)) {
+ for (const QByteArray &val : e.values) {
QByteArray code = cdef->qualified.constData();
if (e.isEnumClass)
code += "::" + (e.enumName.isNull() ? e.name : e.enumName);
@@ -1022,8 +980,6 @@ void Generator::generateMetacall()
}
if (cdef->propertyList.size()) {
-
- fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");
if (needElse)
fprintf(out, "else ");
fprintf(out,
@@ -1031,8 +987,7 @@ void Generator::generateMetacall()
" || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty\n"
" || _c == QMetaObject::RegisterPropertyMetaType) {\n"
" qt_static_metacall(this, _c, _id, _a);\n"
- " _id -= %d;\n }", int(cdef->propertyList.count()));
- fprintf(out, "\n#endif // QT_NO_PROPERTIES");
+ " _id -= %d;\n }", int(cdef->propertyList.size()));
}
if (methodList.size() || cdef->propertyList.size())
fprintf(out, "\n ");
@@ -1040,10 +995,11 @@ void Generator::generateMetacall()
}
+// ### Qt 7 (6.x?): remove
QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper()
{
QMultiMap<QByteArray, int> automaticPropertyMetaTypes;
- for (int i = 0; i < cdef->propertyList.size(); ++i) {
+ for (int i = 0; i < int(cdef->propertyList.size()); ++i) {
const QByteArray propertyType = cdef->propertyList.at(i).type;
if (registerableMetaType(propertyType) && !isBuiltinType(propertyType))
automaticPropertyMetaTypes.insert(propertyType, i);
@@ -1057,7 +1013,7 @@ Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList)
QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes;
for (int i = 0; i < methodList.size(); ++i) {
const FunctionDef &f = methodList.at(i);
- for (int j = 0; j < f.arguments.count(); ++j) {
+ for (int j = 0; j < f.arguments.size(); ++j) {
const QByteArray argType = f.arguments.at(j).normalizedType;
if (registerableMetaType(argType) && !isBuiltinType(argType))
methodsWithAutomaticTypes[i].insert(argType, j);
@@ -1074,33 +1030,46 @@ void Generator::generateStaticMetacall()
bool needElse = false;
bool isUsed_a = false;
+ const auto generateCtorArguments = [&](int ctorindex) {
+ const FunctionDef &f = cdef->constructorList.at(ctorindex);
+ Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed
+ int offset = 1;
+
+ const auto begin = f.arguments.cbegin();
+ const auto end = f.arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
+ fprintf(out, ",");
+ fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))",
+ a.typeNameForCast.constData(), offset++);
+ }
+ };
+
if (!cdef->constructorList.isEmpty()) {
fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n");
fprintf(out, " switch (_id) {\n");
- for (int ctorindex = 0; ctorindex < cdef->constructorList.count(); ++ctorindex) {
+ const int ctorend = int(cdef->constructorList.size());
+ for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
fprintf(out, " case %d: { %s *_r = new %s(", ctorindex,
cdef->classname.constData(), cdef->classname.constData());
- const FunctionDef &f = cdef->constructorList.at(ctorindex);
- int offset = 1;
-
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
- if (j)
- fprintf(out, ",");
- fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++);
- }
- if (f.isPrivateSignal) {
- if (argsCount > 0)
- fprintf(out, ", ");
- fprintf(out, "%s", QByteArray("QPrivateSignal()").constData());
- }
+ generateCtorArguments(ctorindex);
fprintf(out, ");\n");
fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n",
(cdef->hasQGadget || cdef->hasQNamespace) ? "void" : "QObject");
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
+ fprintf(out, " } else if (_c == QMetaObject::ConstructInPlace) {\n");
+ fprintf(out, " switch (_id) {\n");
+ for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
+ fprintf(out, " case %d: { new (_a[0]) %s(",
+ ctorindex, cdef->classname.constData());
+ generateCtorArguments(ctorindex);
+ fprintf(out, "); } break;\n");
+ }
+ fprintf(out, " default: break;\n");
+ fprintf(out, " }\n");
fprintf(out, " }");
needElse = true;
isUsed_a = true;
@@ -1142,16 +1111,17 @@ void Generator::generateStaticMetacall()
if (f.isRawSlot) {
fprintf(out, "QMethodRawArguments{ _a }");
} else {
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
- if (j)
+ const auto begin = f.arguments.cbegin();
+ const auto end = f.arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
fprintf(out, ",");
fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++);
isUsed_a = true;
}
if (f.isPrivateSignal) {
- if (argsCount > 0)
+ if (!f.arguments.isEmpty())
fprintf(out, ", ");
fprintf(out, "%s", "QPrivateSignal()");
}
@@ -1204,7 +1174,7 @@ void Generator::generateStaticMetacall()
fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n");
fprintf(out, " int *result = reinterpret_cast<int *>(_a[0]);\n");
bool anythingUsed = false;
- for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) {
+ for (int methodindex = 0; methodindex < int(cdef->signalList.size()); ++methodindex) {
const FunctionDef &f = cdef->signalList.at(methodindex);
if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic)
continue;
@@ -1212,15 +1182,16 @@ void Generator::generateStaticMetacall()
fprintf(out, " {\n");
fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData());
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
- if (j)
+ const auto begin = f.arguments.cbegin();
+ const auto end = f.arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
fprintf(out, ", ");
fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData());
}
if (f.isPrivateSignal) {
- if (argsCount > 0)
+ if (!f.arguments.isEmpty())
fprintf(out, ", ");
fprintf(out, "%s", "QPrivateSignal");
}
@@ -1228,7 +1199,7 @@ void Generator::generateStaticMetacall()
fprintf(out, ") const;\n");
else
fprintf(out, ");\n");
- fprintf(out, " if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&%s::%s)) {\n",
+ fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n",
cdef->classname.constData(), f.name.constData());
fprintf(out, " *result = %d;\n", methodindex);
fprintf(out, " return;\n");
@@ -1260,7 +1231,7 @@ void Generator::generateStaticMetacall()
fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", lastKey.constData());
}
fprintf(out, " }\n");
- fprintf(out, " }\n");
+ fprintf(out, " } ");
isUsed_a = true;
needElse = true;
}
@@ -1271,8 +1242,7 @@ void Generator::generateStaticMetacall()
bool needSet = false;
bool needReset = false;
bool hasBindableProperties = false;
- for (int i = 0; i < cdef->propertyList.size(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
needGet |= !p.read.isEmpty() || !p.member.isEmpty();
if (!p.read.isEmpty() || !p.member.isEmpty())
needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec
@@ -1282,10 +1252,8 @@ void Generator::generateStaticMetacall()
needReset |= !p.reset.isEmpty();
hasBindableProperties |= !p.bind.isEmpty();
}
- fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");
-
if (needElse)
- fprintf(out, "else ");
+ fprintf(out, " else ");
fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n");
auto setupMemberAccess = [this]() {
@@ -1305,7 +1273,7 @@ void Generator::generateStaticMetacall()
if (needTempVarForGet)
fprintf(out, " void *_v = _a[0];\n");
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (p.read.isEmpty() && p.member.isEmpty())
continue;
@@ -1323,6 +1291,9 @@ void Generator::generateStaticMetacall()
else if (cdef->enumDeclarations.value(p.type, false))
fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n",
propindex, prefix.constData(), p.read.constData());
+ else if (p.read == "default")
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.bind.constData());
else if (!p.read.isEmpty())
fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n",
propindex, p.type.constData(), prefix.constData(), p.read.constData());
@@ -1343,7 +1314,7 @@ void Generator::generateStaticMetacall()
setupMemberAccess();
fprintf(out, " void *_v = _a[0];\n");
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (p.constant)
continue;
@@ -1356,6 +1327,12 @@ void Generator::generateStaticMetacall()
if (cdef->enumDeclarations.value(p.type, false)) {
fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n",
propindex, prefix.constData(), p.write.constData());
+ } else if (p.write == "default") {
+ fprintf(out, " case %d: {\n", propindex);
+ fprintf(out, " %s%s().setValue(*reinterpret_cast< %s*>(_v));\n",
+ prefix.constData(), p.bind.constData(), p.type.constData());
+ fprintf(out, " break;\n");
+ fprintf(out, " }\n");
} else if (!p.write.isEmpty()) {
fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n",
propindex, prefix.constData(), p.write.constData(), p.type.constData());
@@ -1390,15 +1367,15 @@ void Generator::generateStaticMetacall()
if (needReset) {
setupMemberAccess();
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.reset.endsWith(')'))
+ if (p.reset.isEmpty())
continue;
QByteArray prefix = "_t->";
if (p.inPrivateClass.size()) {
prefix += p.inPrivateClass + "->";
}
- fprintf(out, " case %d: %s%s; break;\n",
+ fprintf(out, " case %d: %s%s(); break;\n",
propindex, prefix.constData(), p.reset.constData());
}
fprintf(out, " default: break;\n");
@@ -1411,7 +1388,7 @@ void Generator::generateStaticMetacall()
if (hasBindableProperties) {
setupMemberAccess();
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (p.bind.isEmpty())
continue;
@@ -1428,7 +1405,6 @@ void Generator::generateStaticMetacall()
fprintf(out, " }\n");
}
fprintf(out, " }");
- fprintf(out, "\n#endif // QT_NO_PROPERTIES");
needElse = true;
}
@@ -1445,10 +1421,10 @@ void Generator::generateStaticMetacall()
if (!isUsed_a)
fprintf(out, " (void)_a;\n");
- fprintf(out, "}\n\n");
+ fprintf(out, "}\n");
}
-void Generator::generateSignal(FunctionDef *def,int index)
+void Generator::generateSignal(const FunctionDef *def, int index)
{
if (def->wasCloned || def->isAbstract)
return;
@@ -1472,9 +1448,11 @@ void Generator::generateSignal(FunctionDef *def,int index)
}
int offset = 1;
- for (int j = 0; j < def->arguments.count(); ++j) {
- const ArgumentDef &a = def->arguments.at(j);
- if (j)
+ const auto begin = def->arguments.cbegin();
+ const auto end = def->arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
fputs(", ", out);
if (a.type.name.size())
fputs(a.type.name.constData(), out);
@@ -1505,7 +1483,7 @@ void Generator::generateSignal(FunctionDef *def,int index)
}
int i;
for (i = 1; i < offset; ++i)
- if (i <= def->arguments.count() && def->arguments.at(i - 1).type.isVolatile)
+ if (i <= def->arguments.size() && def->arguments.at(i - 1).type.isVolatile)
fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t%d)))", i);
else
fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t%d)))", i);
@@ -1564,8 +1542,7 @@ static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v)
return cbor_encode_double(parent, d);
}
}
- Q_UNREACHABLE();
- return CborUnknownError;
+ Q_UNREACHABLE_RETURN(CborUnknownError);
}
void Generator::generatePluginMetaData()
@@ -1573,62 +1550,76 @@ void Generator::generatePluginMetaData()
if (cdef->pluginData.iid.isEmpty())
return;
- fprintf(out, "\nQT_PLUGIN_METADATA_SECTION\n"
- "static constexpr unsigned char qt_pluginMetaData_%s[] = {\n"
- " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
- " // metadata version, Qt version, architectural requirements\n"
- " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),",
- cdef->classname.constData());
+ auto outputCborData = [this]() {
+ CborDevice dev(out);
+ CborEncoder enc;
+ cbor_encoder_init_writer(&enc, CborDevice::callback, &dev);
+ CborEncoder map;
+ cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
- CborDevice dev(out);
- CborEncoder enc;
- cbor_encoder_init_writer(&enc, CborDevice::callback, &dev);
-
- CborEncoder map;
- cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
+ dev.nextItem("\"IID\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
+ cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());
- dev.nextItem("\"IID\"");
- cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
- cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());
+ dev.nextItem("\"className\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName));
+ cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size());
- dev.nextItem("\"className\"");
- cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName));
- cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size());
-
- QJsonObject o = cdef->pluginData.metaData.object();
- if (!o.isEmpty()) {
- dev.nextItem("\"MetaData\"");
- cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData));
- jsonObjectToCbor(&map, o);
- }
+ QJsonObject o = cdef->pluginData.metaData.object();
+ if (!o.isEmpty()) {
+ dev.nextItem("\"MetaData\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData));
+ jsonObjectToCbor(&map, o);
+ }
- if (!cdef->pluginData.uri.isEmpty()) {
- dev.nextItem("\"URI\"");
- cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI));
- cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size());
- }
+ if (!cdef->pluginData.uri.isEmpty()) {
+ dev.nextItem("\"URI\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI));
+ cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size());
+ }
- // Add -M args from the command line:
- for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) {
- const QJsonArray &a = it.value();
- QByteArray key = it.key().toUtf8();
- dev.nextItem(QByteArray("command-line \"" + key + "\"").constData());
- cbor_encode_text_string(&map, key.constData(), key.size());
- jsonArrayToCbor(&map, a);
- }
+ // Add -M args from the command line:
+ for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) {
+ const QJsonArray &a = it.value();
+ QByteArray key = it.key().toUtf8();
+ dev.nextItem(QByteArray("command-line \"" + key + "\"").constData());
+ cbor_encode_text_string(&map, key.constData(), key.size());
+ jsonArrayToCbor(&map, a);
+ }
- // Close the CBOR map manually
- dev.nextItem();
- cbor_encoder_close_container(&enc, &map);
- fputs("\n};\n", out);
+ // Close the CBOR map manually
+ dev.nextItem();
+ cbor_encoder_close_container(&enc, &map);
+ };
// 'Use' all namespaces.
- int pos = cdef->qualified.indexOf("::");
+ qsizetype pos = cdef->qualified.indexOf("::");
for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) )
fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData());
- fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n",
+
+ fputs("\n#ifdef QT_MOC_EXPORT_PLUGIN_V2", out);
+
+ // Qt 6.3+ output
+ fprintf(out, "\nstatic constexpr unsigned char qt_pluginMetaDataV2_%s[] = {",
+ cdef->classname.constData());
+ outputCborData();
+ fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN_V2(%s, %s, qt_pluginMetaDataV2_%s)\n",
+ cdef->qualified.constData(), cdef->classname.constData(), cdef->classname.constData());
+
+ // compatibility with Qt 6.0-6.2
+ fprintf(out, "#else\nQT_PLUGIN_METADATA_SECTION\n"
+ "Q_CONSTINIT static constexpr unsigned char qt_pluginMetaData_%s[] = {\n"
+ " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
+ " // metadata version, Qt version, architectural requirements\n"
+ " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),",
+ cdef->classname.constData());
+ outputCborData();
+ fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN(%s, %s)\n"
+ "#endif // QT_MOC_EXPORT_PLUGIN_V2\n",
cdef->qualified.constData(), cdef->classname.constData());
+
+ fputs("\n", out);
}
QT_WARNING_DISABLE_GCC("-Wunused-function")
diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h
index 35128cb543..2d4d69ca05 100644
--- a/src/tools/moc/generator.h
+++ b/src/tools/moc/generator.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef GENERATOR_H
#define GENERATOR_H
@@ -35,16 +10,19 @@ QT_BEGIN_NAMESPACE
class Generator
{
+ Moc *parser = nullptr;
FILE *out;
ClassDef *cdef;
QList<uint> meta_data;
public:
- Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes,
+ Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes,
const QHash<QByteArray, QByteArray> &knownQObjectClasses,
const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = nullptr,
bool requireCompleteTypes = false);
void generateCode();
+ qsizetype registeredStringsCount() { return strings.size(); };
+
private:
bool registerableMetaType(const QByteArray &propertyType);
void registerClassInfoStrings();
@@ -62,7 +40,7 @@ private:
void generateProperties();
void generateMetacall();
void generateStaticMetacall();
- void generateSignal(FunctionDef *def, int index);
+ void generateSignal(const FunctionDef *def, int index);
void generatePluginMetaData();
QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper();
QMap<int, QMultiMap<QByteArray, int>>
diff --git a/src/tools/moc/keywords.cpp b/src/tools/moc/keywords.cpp
index cc7d747f5b..6a1f58490f 100644
--- a/src/tools/moc/keywords.cpp
+++ b/src/tools/moc/keywords.cpp
@@ -1,41 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// auto generated
// DO NOT EDIT.
static const short keyword_trans[][128] = {
- {0,0,0,0,0,0,0,0,0,579,576,0,0,0,0,0,
+ {0,0,0,0,0,0,0,0,0,618,615,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 579,252,577,580,8,38,239,578,25,26,236,234,30,235,27,237,
+ 618,252,616,619,8,38,239,617,25,26,236,234,30,235,27,237,
22,22,22,22,22,22,22,22,22,22,34,41,23,39,24,43,
0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,21,8,8,8,8,8,8,8,8,8,31,582,32,238,8,
+ 8,21,8,8,8,8,8,8,8,8,8,31,621,32,238,8,
0,1,2,3,4,5,6,7,8,9,8,8,10,11,12,13,
14,8,15,16,17,18,19,20,8,8,8,36,245,37,248,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -116,7 +91,7 @@ static const short keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,290,222,0,0,497,0,0,0,
+ 0,0,0,0,0,0,0,0,290,222,0,0,524,0,0,0,
0,0,0,0,55,0,0,330,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -155,7 +130,7 @@ static const short keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,521,0,0,0,0,0,0,0,0,0,0,357,
+ 0,0,0,0,401,0,0,0,0,0,0,0,0,0,0,357,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -177,7 +152,7 @@ static const short keyword_trans[][128] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,42,0,0,0,28,0,
- 585,585,585,585,585,585,585,585,585,585,0,0,0,0,0,0,
+ 624,624,624,624,624,624,624,624,624,624,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -336,7 +311,7 @@ static const short keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,584,0,0,0,0,583,
+ 0,0,0,0,0,0,0,0,0,0,623,0,0,0,0,622,
0,0,0,0,0,0,0,0,0,0,0,0,0,258,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -372,29 +347,29 @@ static const short keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,494,0,0,0,300,0,0,0,0,0,0,0,0,0,0,
+ 0,521,0,0,0,300,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,475,424,408,416,380,0,484,0,0,0,565,364,358,
- 386,0,557,472,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,502,451,435,443,380,0,511,0,0,0,604,364,358,
+ 393,0,596,499,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,394,0,0,0,
- 0,0,387,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,421,0,0,0,
+ 0,0,394,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,511,0,0,0,0,0,388,
+ 0,0,0,0,0,0,0,0,0,538,0,0,0,0,0,395,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
@@ -402,48 +377,64 @@ static const short keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,403,0,0,0,0,0,0,0,0,0,0,0,548,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,412,0,0,0,0,0,0,0,0,0,0,0,413,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,582,0,0,0,0,0,415,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,420,0,0,0,0,0,0,0,0,0,0,0,421,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,454,432,0,0,437,0,0,0,446,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,439,0,0,0,0,0,0,0,0,0,0,0,440,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,540,0,473,0,0,0,501,0,0,507,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,447,0,0,0,0,0,0,0,0,0,0,0,448,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,481,459,0,0,464,0,0,0,473,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,486,0,533,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,565,0,500,0,0,0,528,0,0,534,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 549,0,0,517,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,513,0,558,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 574,0,0,544,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
@@ -841,85 +832,85 @@ static const struct
{CHARACTER, 0, 71, 383, CHARACTER},
{CHARACTER, 0, 69, 384, CHARACTER},
{CHARACTER, 0, 84, 385, CHARACTER},
- {Q_GADGET_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 44, 0, 0, CHARACTER},
- {CHARACTER, 45, 0, 0, CHARACTER},
+ {Q_GADGET_TOKEN, 0, 95, 386, CHARACTER},
+ {CHARACTER, 0, 69, 387, CHARACTER},
+ {CHARACTER, 0, 88, 388, CHARACTER},
{CHARACTER, 0, 80, 389, CHARACTER},
- {CHARACTER, 0, 69, 390, CHARACTER},
+ {CHARACTER, 0, 79, 390, CHARACTER},
{CHARACTER, 0, 82, 391, CHARACTER},
{CHARACTER, 0, 84, 392, CHARACTER},
- {CHARACTER, 0, 89, 393, CHARACTER},
+ {Q_GADGET_EXPORT_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 44, 0, 0, CHARACTER},
+ {CHARACTER, 45, 0, 0, CHARACTER},
+ {CHARACTER, 0, 80, 396, CHARACTER},
+ {CHARACTER, 0, 69, 397, CHARACTER},
+ {CHARACTER, 0, 82, 398, CHARACTER},
+ {CHARACTER, 0, 84, 399, CHARACTER},
+ {CHARACTER, 0, 89, 400, CHARACTER},
{Q_PROPERTY_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 85, 395, CHARACTER},
- {CHARACTER, 0, 71, 396, CHARACTER},
- {CHARACTER, 0, 73, 397, CHARACTER},
- {CHARACTER, 0, 78, 398, CHARACTER},
- {CHARACTER, 0, 95, 399, CHARACTER},
- {CHARACTER, 0, 77, 400, CHARACTER},
- {CHARACTER, 0, 69, 401, CHARACTER},
- {CHARACTER, 0, 84, 402, CHARACTER},
- {CHARACTER, 0, 65, 403, CHARACTER},
- {CHARACTER, 0, 68, 404, CHARACTER},
- {CHARACTER, 0, 65, 405, CHARACTER},
- {CHARACTER, 0, 84, 406, CHARACTER},
- {CHARACTER, 0, 65, 407, CHARACTER},
- {Q_PLUGIN_METADATA_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 78, 409, CHARACTER},
+ {CHARACTER, 0, 95, 402, CHARACTER},
+ {CHARACTER, 46, 0, 0, CHARACTER},
+ {CHARACTER, 0, 78, 404, CHARACTER},
+ {CHARACTER, 0, 79, 405, CHARACTER},
+ {CHARACTER, 0, 78, 406, CHARACTER},
+ {CHARACTER, 0, 89, 407, CHARACTER},
+ {CHARACTER, 0, 77, 408, CHARACTER},
+ {CHARACTER, 0, 79, 409, CHARACTER},
{CHARACTER, 0, 85, 410, CHARACTER},
- {CHARACTER, 0, 77, 411, CHARACTER},
- {Q_ENUM_TOKEN, 46, 0, 0, CHARACTER},
+ {CHARACTER, 0, 83, 411, CHARACTER},
+ {CHARACTER, 0, 95, 412, CHARACTER},
+ {CHARACTER, 0, 80, 413, CHARACTER},
+ {CHARACTER, 0, 82, 414, CHARACTER},
+ {CHARACTER, 47, 0, 0, CHARACTER},
+ {CHARACTER, 0, 80, 416, CHARACTER},
+ {CHARACTER, 0, 69, 417, CHARACTER},
+ {CHARACTER, 0, 82, 418, CHARACTER},
+ {CHARACTER, 0, 84, 419, CHARACTER},
+ {CHARACTER, 0, 89, 420, CHARACTER},
+ {QT_ANONYMOUS_PROPERTY_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 85, 422, CHARACTER},
+ {CHARACTER, 0, 71, 423, CHARACTER},
+ {CHARACTER, 0, 73, 424, CHARACTER},
+ {CHARACTER, 0, 78, 425, CHARACTER},
+ {CHARACTER, 0, 95, 426, CHARACTER},
+ {CHARACTER, 0, 77, 427, CHARACTER},
+ {CHARACTER, 0, 69, 428, CHARACTER},
+ {CHARACTER, 0, 84, 429, CHARACTER},
+ {CHARACTER, 0, 65, 430, CHARACTER},
+ {CHARACTER, 0, 68, 431, CHARACTER},
+ {CHARACTER, 0, 65, 432, CHARACTER},
+ {CHARACTER, 0, 84, 433, CHARACTER},
+ {CHARACTER, 0, 65, 434, CHARACTER},
+ {Q_PLUGIN_METADATA_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 78, 436, CHARACTER},
+ {CHARACTER, 0, 85, 437, CHARACTER},
+ {CHARACTER, 0, 77, 438, CHARACTER},
+ {Q_ENUM_TOKEN, 48, 0, 0, CHARACTER},
{Q_ENUMS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 78, 414, CHARACTER},
- {CHARACTER, 0, 83, 415, CHARACTER},
+ {CHARACTER, 0, 78, 441, CHARACTER},
+ {CHARACTER, 0, 83, 442, CHARACTER},
{Q_ENUM_NS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 76, 417, CHARACTER},
- {CHARACTER, 0, 65, 418, CHARACTER},
- {CHARACTER, 0, 71, 419, CHARACTER},
- {Q_FLAG_TOKEN, 47, 0, 0, CHARACTER},
+ {CHARACTER, 0, 76, 444, CHARACTER},
+ {CHARACTER, 0, 65, 445, CHARACTER},
+ {CHARACTER, 0, 71, 446, CHARACTER},
+ {Q_FLAG_TOKEN, 49, 0, 0, CHARACTER},
{Q_FLAGS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 78, 422, CHARACTER},
- {CHARACTER, 0, 83, 423, CHARACTER},
+ {CHARACTER, 0, 78, 449, CHARACTER},
+ {CHARACTER, 0, 83, 450, CHARACTER},
{Q_FLAG_NS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 69, 425, CHARACTER},
- {CHARACTER, 0, 67, 426, CHARACTER},
- {CHARACTER, 0, 76, 427, CHARACTER},
- {CHARACTER, 0, 65, 428, CHARACTER},
- {CHARACTER, 0, 82, 429, CHARACTER},
- {CHARACTER, 0, 69, 430, CHARACTER},
- {CHARACTER, 0, 95, 431, CHARACTER},
- {CHARACTER, 48, 0, 0, CHARACTER},
- {CHARACTER, 0, 76, 433, CHARACTER},
- {CHARACTER, 0, 65, 434, CHARACTER},
- {CHARACTER, 0, 71, 435, CHARACTER},
- {CHARACTER, 0, 83, 436, CHARACTER},
- {Q_DECLARE_FLAGS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 78, 438, CHARACTER},
- {CHARACTER, 0, 84, 439, CHARACTER},
- {CHARACTER, 0, 69, 440, CHARACTER},
- {CHARACTER, 0, 82, 441, CHARACTER},
- {CHARACTER, 0, 70, 442, CHARACTER},
- {CHARACTER, 0, 65, 443, CHARACTER},
- {CHARACTER, 0, 67, 444, CHARACTER},
- {CHARACTER, 0, 69, 445, CHARACTER},
- {Q_DECLARE_INTERFACE_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 69, 447, CHARACTER},
- {CHARACTER, 0, 84, 448, CHARACTER},
- {CHARACTER, 0, 65, 449, CHARACTER},
- {CHARACTER, 0, 84, 450, CHARACTER},
- {CHARACTER, 0, 89, 451, CHARACTER},
- {CHARACTER, 0, 80, 452, CHARACTER},
- {CHARACTER, 0, 69, 453, CHARACTER},
- {Q_DECLARE_METATYPE_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 88, 455, CHARACTER},
- {CHARACTER, 0, 84, 456, CHARACTER},
+ {CHARACTER, 0, 69, 452, CHARACTER},
+ {CHARACTER, 0, 67, 453, CHARACTER},
+ {CHARACTER, 0, 76, 454, CHARACTER},
+ {CHARACTER, 0, 65, 455, CHARACTER},
+ {CHARACTER, 0, 82, 456, CHARACTER},
{CHARACTER, 0, 69, 457, CHARACTER},
- {CHARACTER, 0, 78, 458, CHARACTER},
- {CHARACTER, 0, 83, 459, CHARACTER},
- {CHARACTER, 0, 73, 460, CHARACTER},
- {CHARACTER, 0, 79, 461, CHARACTER},
- {CHARACTER, 0, 78, 462, CHARACTER},
- {CHARACTER, 0, 95, 463, CHARACTER},
- {CHARACTER, 0, 73, 464, CHARACTER},
+ {CHARACTER, 0, 95, 458, CHARACTER},
+ {CHARACTER, 50, 0, 0, CHARACTER},
+ {CHARACTER, 0, 76, 460, CHARACTER},
+ {CHARACTER, 0, 65, 461, CHARACTER},
+ {CHARACTER, 0, 71, 462, CHARACTER},
+ {CHARACTER, 0, 83, 463, CHARACTER},
+ {Q_DECLARE_FLAGS_TOKEN, 0, 0, 0, CHARACTER},
{CHARACTER, 0, 78, 465, CHARACTER},
{CHARACTER, 0, 84, 466, CHARACTER},
{CHARACTER, 0, 69, 467, CHARACTER},
@@ -927,116 +918,155 @@ static const struct
{CHARACTER, 0, 70, 469, CHARACTER},
{CHARACTER, 0, 65, 470, CHARACTER},
{CHARACTER, 0, 67, 471, CHARACTER},
- {CHARACTER, 0, 69, 445, CHARACTER},
- {CHARACTER, 49, 0, 0, CHARACTER},
- {CHARACTER, 0, 84, 474, CHARACTER},
- {CHARACTER, 0, 83, 420, CHARACTER},
- {CHARACTER, 0, 76, 476, CHARACTER},
- {CHARACTER, 0, 65, 477, CHARACTER},
- {CHARACTER, 0, 83, 478, CHARACTER},
- {CHARACTER, 0, 83, 479, CHARACTER},
- {CHARACTER, 0, 73, 480, CHARACTER},
- {CHARACTER, 0, 78, 481, CHARACTER},
- {CHARACTER, 0, 70, 482, CHARACTER},
- {CHARACTER, 0, 79, 483, CHARACTER},
- {Q_CLASSINFO_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 69, 472, CHARACTER},
+ {Q_DECLARE_INTERFACE_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 69, 474, CHARACTER},
+ {CHARACTER, 0, 84, 475, CHARACTER},
+ {CHARACTER, 0, 65, 476, CHARACTER},
+ {CHARACTER, 0, 84, 477, CHARACTER},
+ {CHARACTER, 0, 89, 478, CHARACTER},
+ {CHARACTER, 0, 80, 479, CHARACTER},
+ {CHARACTER, 0, 69, 480, CHARACTER},
+ {Q_DECLARE_METATYPE_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 88, 482, CHARACTER},
+ {CHARACTER, 0, 84, 483, CHARACTER},
+ {CHARACTER, 0, 69, 484, CHARACTER},
{CHARACTER, 0, 78, 485, CHARACTER},
- {CHARACTER, 50, 0, 0, CHARACTER},
- {CHARACTER, 0, 69, 487, CHARACTER},
- {CHARACTER, 0, 82, 488, CHARACTER},
- {CHARACTER, 0, 70, 489, CHARACTER},
- {CHARACTER, 0, 65, 490, CHARACTER},
- {CHARACTER, 0, 67, 491, CHARACTER},
- {CHARACTER, 0, 69, 492, CHARACTER},
- {CHARACTER, 0, 83, 493, CHARACTER},
+ {CHARACTER, 0, 83, 486, CHARACTER},
+ {CHARACTER, 0, 73, 487, CHARACTER},
+ {CHARACTER, 0, 79, 488, CHARACTER},
+ {CHARACTER, 0, 78, 489, CHARACTER},
+ {CHARACTER, 0, 95, 490, CHARACTER},
+ {CHARACTER, 0, 73, 491, CHARACTER},
+ {CHARACTER, 0, 78, 492, CHARACTER},
+ {CHARACTER, 0, 84, 493, CHARACTER},
+ {CHARACTER, 0, 69, 494, CHARACTER},
+ {CHARACTER, 0, 82, 495, CHARACTER},
+ {CHARACTER, 0, 70, 496, CHARACTER},
+ {CHARACTER, 0, 65, 497, CHARACTER},
+ {CHARACTER, 0, 67, 498, CHARACTER},
+ {CHARACTER, 0, 69, 472, CHARACTER},
+ {CHARACTER, 51, 0, 0, CHARACTER},
+ {CHARACTER, 0, 84, 501, CHARACTER},
+ {CHARACTER, 0, 83, 447, CHARACTER},
+ {CHARACTER, 0, 76, 503, CHARACTER},
+ {CHARACTER, 0, 65, 504, CHARACTER},
+ {CHARACTER, 0, 83, 505, CHARACTER},
+ {CHARACTER, 0, 83, 506, CHARACTER},
+ {CHARACTER, 0, 73, 507, CHARACTER},
+ {CHARACTER, 0, 78, 508, CHARACTER},
+ {CHARACTER, 0, 70, 509, CHARACTER},
+ {CHARACTER, 0, 79, 510, CHARACTER},
+ {Q_CLASSINFO_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 78, 512, CHARACTER},
+ {CHARACTER, 52, 0, 0, CHARACTER},
+ {CHARACTER, 0, 69, 514, CHARACTER},
+ {CHARACTER, 0, 82, 515, CHARACTER},
+ {CHARACTER, 0, 70, 516, CHARACTER},
+ {CHARACTER, 0, 65, 517, CHARACTER},
+ {CHARACTER, 0, 67, 518, CHARACTER},
+ {CHARACTER, 0, 69, 519, CHARACTER},
+ {CHARACTER, 0, 83, 520, CHARACTER},
{Q_INTERFACES_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 108, 495, CHARACTER},
- {CHARACTER, 0, 115, 496, CHARACTER},
+ {CHARACTER, 0, 108, 522, CHARACTER},
+ {CHARACTER, 0, 115, 523, CHARACTER},
{SIGNALS, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 111, 498, CHARACTER},
- {CHARACTER, 0, 116, 499, CHARACTER},
- {CHARACTER, 0, 115, 500, CHARACTER},
+ {CHARACTER, 0, 111, 525, CHARACTER},
+ {CHARACTER, 0, 116, 526, CHARACTER},
+ {CHARACTER, 0, 115, 527, CHARACTER},
{SLOTS, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 71, 502, CHARACTER},
- {CHARACTER, 0, 78, 503, CHARACTER},
- {CHARACTER, 0, 65, 504, CHARACTER},
- {CHARACTER, 0, 76, 505, CHARACTER},
- {Q_SIGNAL_TOKEN, 0, 83, 506, CHARACTER},
+ {CHARACTER, 0, 71, 529, CHARACTER},
+ {CHARACTER, 0, 78, 530, CHARACTER},
+ {CHARACTER, 0, 65, 531, CHARACTER},
+ {CHARACTER, 0, 76, 532, CHARACTER},
+ {Q_SIGNAL_TOKEN, 0, 83, 533, CHARACTER},
{Q_SIGNALS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 79, 508, CHARACTER},
- {CHARACTER, 0, 84, 509, CHARACTER},
- {Q_SLOT_TOKEN, 0, 83, 510, CHARACTER},
+ {CHARACTER, 0, 79, 535, CHARACTER},
+ {CHARACTER, 0, 84, 536, CHARACTER},
+ {Q_SLOT_TOKEN, 0, 83, 537, CHARACTER},
{Q_SLOTS_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 86, 512, CHARACTER},
- {CHARACTER, 0, 65, 513, CHARACTER},
- {CHARACTER, 0, 84, 514, CHARACTER},
- {CHARACTER, 0, 69, 515, CHARACTER},
- {CHARACTER, 0, 95, 516, CHARACTER},
- {CHARACTER, 51, 0, 0, CHARACTER},
- {CHARACTER, 0, 76, 518, CHARACTER},
- {CHARACTER, 0, 79, 519, CHARACTER},
- {CHARACTER, 0, 84, 520, CHARACTER},
+ {CHARACTER, 0, 86, 539, CHARACTER},
+ {CHARACTER, 0, 65, 540, CHARACTER},
+ {CHARACTER, 0, 84, 541, CHARACTER},
+ {CHARACTER, 0, 69, 542, CHARACTER},
+ {CHARACTER, 0, 95, 543, CHARACTER},
+ {CHARACTER, 53, 0, 0, CHARACTER},
+ {CHARACTER, 0, 76, 545, CHARACTER},
+ {CHARACTER, 0, 79, 546, CHARACTER},
+ {CHARACTER, 0, 84, 547, CHARACTER},
{Q_PRIVATE_SLOT_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 95, 522, CHARACTER},
- {CHARACTER, 0, 77, 523, CHARACTER},
- {CHARACTER, 0, 79, 524, CHARACTER},
- {CHARACTER, 0, 67, 525, CHARACTER},
- {CHARACTER, 0, 95, 526, CHARACTER},
- {CHARACTER, 0, 67, 527, CHARACTER},
- {CHARACTER, 0, 79, 528, CHARACTER},
- {CHARACTER, 0, 77, 529, CHARACTER},
- {CHARACTER, 0, 80, 530, CHARACTER},
- {CHARACTER, 0, 65, 531, CHARACTER},
- {CHARACTER, 0, 84, 532, CHARACTER},
+ {CHARACTER, 0, 79, 549, CHARACTER},
+ {CHARACTER, 0, 67, 550, CHARACTER},
+ {CHARACTER, 0, 95, 551, CHARACTER},
+ {CHARACTER, 0, 67, 552, CHARACTER},
+ {CHARACTER, 0, 79, 553, CHARACTER},
+ {CHARACTER, 0, 77, 554, CHARACTER},
+ {CHARACTER, 0, 80, 555, CHARACTER},
+ {CHARACTER, 0, 65, 556, CHARACTER},
+ {CHARACTER, 0, 84, 557, CHARACTER},
{Q_MOC_COMPAT_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 79, 534, CHARACTER},
- {CHARACTER, 0, 75, 535, CHARACTER},
- {CHARACTER, 0, 65, 536, CHARACTER},
- {CHARACTER, 0, 66, 537, CHARACTER},
- {CHARACTER, 0, 76, 538, CHARACTER},
- {CHARACTER, 0, 69, 539, CHARACTER},
+ {CHARACTER, 0, 79, 559, CHARACTER},
+ {CHARACTER, 0, 75, 560, CHARACTER},
+ {CHARACTER, 0, 65, 561, CHARACTER},
+ {CHARACTER, 0, 66, 562, CHARACTER},
+ {CHARACTER, 0, 76, 563, CHARACTER},
+ {CHARACTER, 0, 69, 564, CHARACTER},
{Q_INVOKABLE_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 82, 541, CHARACTER},
- {CHARACTER, 0, 73, 542, CHARACTER},
- {CHARACTER, 0, 80, 543, CHARACTER},
- {CHARACTER, 0, 84, 544, CHARACTER},
- {CHARACTER, 0, 65, 545, CHARACTER},
- {CHARACTER, 0, 66, 546, CHARACTER},
- {CHARACTER, 0, 76, 547, CHARACTER},
- {CHARACTER, 0, 69, 548, CHARACTER},
+ {CHARACTER, 0, 82, 566, CHARACTER},
+ {CHARACTER, 0, 73, 567, CHARACTER},
+ {CHARACTER, 0, 80, 568, CHARACTER},
+ {CHARACTER, 0, 84, 569, CHARACTER},
+ {CHARACTER, 0, 65, 570, CHARACTER},
+ {CHARACTER, 0, 66, 571, CHARACTER},
+ {CHARACTER, 0, 76, 572, CHARACTER},
+ {CHARACTER, 0, 69, 573, CHARACTER},
{Q_SCRIPTABLE_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 82, 550, CHARACTER},
- {CHARACTER, 0, 79, 551, CHARACTER},
- {CHARACTER, 0, 80, 552, CHARACTER},
- {CHARACTER, 0, 69, 553, CHARACTER},
- {CHARACTER, 0, 82, 554, CHARACTER},
- {CHARACTER, 0, 84, 555, CHARACTER},
- {CHARACTER, 0, 89, 556, CHARACTER},
+ {CHARACTER, 0, 82, 575, CHARACTER},
+ {CHARACTER, 0, 79, 576, CHARACTER},
+ {CHARACTER, 0, 80, 577, CHARACTER},
+ {CHARACTER, 0, 69, 578, CHARACTER},
+ {CHARACTER, 0, 82, 579, CHARACTER},
+ {CHARACTER, 0, 84, 580, CHARACTER},
+ {CHARACTER, 0, 89, 581, CHARACTER},
{Q_PRIVATE_PROPERTY_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 69, 558, CHARACTER},
- {CHARACTER, 0, 86, 559, CHARACTER},
- {CHARACTER, 0, 73, 560, CHARACTER},
- {CHARACTER, 0, 83, 561, CHARACTER},
- {CHARACTER, 0, 73, 562, CHARACTER},
- {CHARACTER, 0, 79, 563, CHARACTER},
- {CHARACTER, 0, 78, 564, CHARACTER},
+ {CHARACTER, 0, 86, 583, CHARACTER},
+ {CHARACTER, 0, 65, 584, CHARACTER},
+ {CHARACTER, 0, 84, 585, CHARACTER},
+ {CHARACTER, 0, 69, 586, CHARACTER},
+ {CHARACTER, 0, 95, 587, CHARACTER},
+ {CHARACTER, 0, 80, 588, CHARACTER},
+ {CHARACTER, 0, 82, 589, CHARACTER},
+ {CHARACTER, 0, 79, 590, CHARACTER},
+ {CHARACTER, 0, 80, 591, CHARACTER},
+ {CHARACTER, 0, 69, 592, CHARACTER},
+ {CHARACTER, 0, 82, 593, CHARACTER},
+ {CHARACTER, 0, 84, 594, CHARACTER},
+ {CHARACTER, 0, 89, 595, CHARACTER},
+ {QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN, 0, 0, 0, CHARACTER},
+ {CHARACTER, 0, 69, 597, CHARACTER},
+ {CHARACTER, 0, 86, 598, CHARACTER},
+ {CHARACTER, 0, 73, 599, CHARACTER},
+ {CHARACTER, 0, 83, 600, CHARACTER},
+ {CHARACTER, 0, 73, 601, CHARACTER},
+ {CHARACTER, 0, 79, 602, CHARACTER},
+ {CHARACTER, 0, 78, 603, CHARACTER},
{Q_REVISION_TOKEN, 0, 0, 0, CHARACTER},
- {CHARACTER, 0, 79, 566, CHARACTER},
- {CHARACTER, 0, 67, 567, CHARACTER},
- {CHARACTER, 0, 95, 568, CHARACTER},
- {CHARACTER, 0, 73, 569, CHARACTER},
- {CHARACTER, 0, 78, 570, CHARACTER},
- {CHARACTER, 0, 67, 571, CHARACTER},
- {CHARACTER, 0, 76, 572, CHARACTER},
- {CHARACTER, 0, 85, 573, CHARACTER},
- {CHARACTER, 0, 68, 574, CHARACTER},
- {CHARACTER, 0, 69, 575, CHARACTER},
+ {CHARACTER, 0, 79, 605, CHARACTER},
+ {CHARACTER, 0, 67, 606, CHARACTER},
+ {CHARACTER, 0, 95, 607, CHARACTER},
+ {CHARACTER, 0, 73, 608, CHARACTER},
+ {CHARACTER, 0, 78, 609, CHARACTER},
+ {CHARACTER, 0, 67, 610, CHARACTER},
+ {CHARACTER, 0, 76, 611, CHARACTER},
+ {CHARACTER, 0, 85, 612, CHARACTER},
+ {CHARACTER, 0, 68, 613, CHARACTER},
+ {CHARACTER, 0, 69, 614, CHARACTER},
{Q_MOC_INCLUDE_TOKEN, 0, 0, 0, CHARACTER},
{NEWLINE, 0, 0, 0, NOTOKEN},
{QUOTE, 0, 0, 0, NOTOKEN},
{SINGLEQUOTE, 0, 0, 0, NOTOKEN},
{WHITESPACE, 0, 0, 0, NOTOKEN},
- {HASH, 0, 35, 581, HASH},
+ {HASH, 0, 35, 620, HASH},
{PP_HASHHASH, 0, 0, 0, NOTOKEN},
{BACKSLASH, 0, 0, 0, NOTOKEN},
{CPP_COMMENT, 0, 0, 0, NOTOKEN},
diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp
index 3e98fbf2b8..bb51352519 100644
--- a/src/tools/moc/main.cpp
+++ b/src/tools/moc/main.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <depfile_shared.h>
#include "preprocessor.h"
@@ -48,6 +23,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*
This function looks at two file names and returns the name of the
infile with a path relative to outfile.
@@ -153,7 +130,7 @@ static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments,
allArguments.reserve(arguments.size());
for (const QString &argument : arguments) {
// "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
- if (argument.startsWith(QLatin1Char('@'))) {
+ if (argument.startsWith(u'@')) {
QString optionsFile = argument;
optionsFile.remove(0, 1);
if (optionsFile.isEmpty()) {
@@ -306,6 +283,10 @@ int runMoc(int argc, char **argv)
jsonOption.setDescription(QStringLiteral("In addition to generating C++ code, create a machine-readable JSON file in a file that matches the output file and an extra .json extension."));
parser.addOption(jsonOption);
+ QCommandLineOption debugIncludesOption(QStringLiteral("debug-includes"));
+ debugIncludesOption.setDescription(QStringLiteral("Display debug messages of each considered include path."));
+ parser.addOption(debugIncludesOption);
+
QCommandLineOption collectOption(QStringLiteral("collect-json"));
collectOption.setDescription(QStringLiteral("Instead of processing C++ code, collect previously generated JSON output into a single file."));
parser.addOption(collectOption);
@@ -349,8 +330,8 @@ int runMoc(int argc, char **argv)
if (parser.isSet(collectOption))
return collectJson(files, output, hasOptionFiles);
- if (files.count() > 1) {
- error(qPrintable(QLatin1String("Too many input files specified: '") + files.join(QLatin1String("' '")) + QLatin1Char('\'')));
+ if (files.size() > 1) {
+ error(qPrintable("Too many input files specified: '"_L1 + files.join("' '"_L1) + u'\''));
parser.showHelp(1);
} else if (!files.isEmpty()) {
filename = files.first();
@@ -358,6 +339,7 @@ int runMoc(int argc, char **argv)
const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
pp.preprocessOnly = parser.isSet(preprocessOption);
+ pp.setDebugIncludes(parser.isSet(debugIncludesOption));
if (parser.isSet(noIncludeOption)) {
moc.noInclude = true;
autoInclude = false;
@@ -385,7 +367,7 @@ int runMoc(int argc, char **argv)
for (const QString &path : includePaths)
pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
QString compilerFlavor = parser.value(compilerFlavorOption);
- if (compilerFlavor.isEmpty() || compilerFlavor == QLatin1String("unix")) {
+ if (compilerFlavor.isEmpty() || compilerFlavor == "unix"_L1) {
// traditional Unix compilers use both CPATH and CPLUS_INCLUDE_PATH
// $CPATH feeds to #include <...> and #include "...", whereas
// CPLUS_INCLUDE_PATH is equivalent to GCC's -isystem, so we parse later
@@ -395,14 +377,14 @@ int runMoc(int argc, char **argv)
const auto cplus_include_path = qgetenv("CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : cplus_include_path)
pp.includes += Preprocessor::IncludePath(p);
- } else if (compilerFlavor == QLatin1String("msvc")) {
+ } else if (compilerFlavor == "msvc"_L1) {
// MSVC uses one environment variable: INCLUDE
const auto include = qgetenv("INCLUDE").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : include)
pp.includes += Preprocessor::IncludePath(p);
} else {
- error(qPrintable(QLatin1String("Unknown compiler flavor '") + compilerFlavor +
- QLatin1String("'; valid values are: msvc, unix.")));
+ error(qPrintable("Unknown compiler flavor '"_L1 + compilerFlavor +
+ "'; valid values are: msvc, unix."_L1));
parser.showHelp(1);
}
@@ -417,7 +399,7 @@ int runMoc(int argc, char **argv)
for (const QString &arg : defines) {
QByteArray name = arg.toLocal8Bit();
QByteArray value("1");
- int eq = name.indexOf('=');
+ const qsizetype eq = name.indexOf('=');
if (eq >= 0) {
value = name.mid(eq + 1);
name = name.left(eq);
@@ -441,16 +423,16 @@ int runMoc(int argc, char **argv)
pp.macros.remove(macro);
}
const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
- if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(QLatin1String("n")))
+ if (parser.isSet(noNotesOption) || noNotesCompatValues.contains("n"_L1))
moc.displayNotes = false;
- if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains(QLatin1String("w")))
+ if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains("w"_L1))
moc.displayWarnings = moc.displayNotes = false;
if (autoInclude) {
- int spos = filename.lastIndexOf(QDir::separator());
- int ppos = filename.lastIndexOf(QLatin1Char('.'));
+ qsizetype spos = filename.lastIndexOf(QDir::separator());
+ qsizetype ppos = filename.lastIndexOf(u'.');
// spos >= -1 && ppos > spos => ppos >= 0
- moc.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != QLatin1Char('h'));
+ moc.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != u'h');
}
if (defaultInclude) {
if (moc.includePath.isEmpty()) {
@@ -467,11 +449,14 @@ int runMoc(int argc, char **argv)
if (filename.isEmpty()) {
filename = QStringLiteral("standard input");
- in.open(stdin, QIODevice::ReadOnly);
+ if (!in.open(stdin, QIODevice::ReadOnly)) {
+ fprintf(stderr, "moc: cannot open standard input: %s\n", qPrintable(in.errorString()));
+ return 1;
+ }
} else {
in.setFileName(filename);
if (!in.open(QIODevice::ReadOnly)) {
- fprintf(stderr, "moc: %s: No such file\n", qPrintable(filename));
+ fprintf(stderr, "moc: cannot open %s: %s\n", qPrintable(filename), qPrintable(in.errorString()));
return 1;
}
moc.filename = filename.toLocal8Bit();
@@ -479,13 +464,13 @@ int runMoc(int argc, char **argv)
const auto metadata = parser.values(metadataOption);
for (const QString &md : metadata) {
- int split = md.indexOf(QLatin1Char('='));
+ qsizetype split = md.indexOf(u'=');
QString key = md.left(split);
QString value = md.mid(split + 1);
if (split == -1 || key.isEmpty() || value.isEmpty()) {
error("missing key or value for option '-M'");
- } else if (key.indexOf(QLatin1Char('.')) != -1) {
+ } else if (key.indexOf(u'.') != -1) {
// Don't allow keys with '.' for now, since we might need this
// format later for more advanced meta data API
error("A key cannot contain the letter '.' for option '-M'");
@@ -499,6 +484,17 @@ int runMoc(int argc, char **argv)
moc.currentFilenames.push(filename.toLocal8Bit());
moc.includes = pp.includes;
+ if (Q_UNLIKELY(parser.isSet(debugIncludesOption))) {
+ fprintf(stderr, "debug-includes: include search list:\n");
+
+ for (auto &includePath : pp.includes) {
+ fprintf(stderr, "debug-includes: '%s' framework: %d \n",
+ includePath.path.constData(),
+ includePath.isFrameworkPath);
+ }
+ fprintf(stderr, "debug-includes: end of search list.\n");
+ }
+
// 1. preprocess
const auto includeFiles = parser.values(includeOption);
QStringList validIncludesFiles;
@@ -543,12 +539,15 @@ int runMoc(int argc, char **argv)
if (!out)
#endif
{
- fprintf(stderr, "moc: Cannot create %s\n", QFile::encodeName(output).constData());
+ const auto fopen_errno = errno;
+ fprintf(stderr, "moc: Cannot create %s. Error: %s\n",
+ QFile::encodeName(output).constData(),
+ strerror(fopen_errno));
return 1;
}
if (parser.isSet(jsonOption)) {
- const QString jsonOutputFileName = output + QLatin1String(".json");
+ const QString jsonOutputFileName = output + ".json"_L1;
FILE *f;
#if defined(_MSC_VER)
if (_wfopen_s(&f, reinterpret_cast<const wchar_t *>(jsonOutputFileName.utf16()), L"w") != 0)
@@ -556,9 +555,12 @@ int runMoc(int argc, char **argv)
f = fopen(QFile::encodeName(jsonOutputFileName).constData(), "w");
if (!f)
#endif
- fprintf(stderr, "moc: Cannot create JSON output file %s. %s\n",
+ {
+ const auto fopen_errno = errno;
+ fprintf(stderr, "moc: Cannot create JSON output file %s. Error: %s\n",
QFile::encodeName(jsonOutputFileName).constData(),
- strerror(errno));
+ strerror(fopen_errno));
+ }
jsonOutput.reset(f);
}
} else { // use stdout
@@ -589,7 +591,7 @@ int runMoc(int argc, char **argv)
if (parser.isSet(depFilePathOption)) {
depOutputFileName = parser.value(depFilePathOption);
} else if (outputToFile) {
- depOutputFileName = output + QLatin1String(".d");
+ depOutputFileName = output + ".d"_L1;
} else {
fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n");
}
@@ -603,9 +605,12 @@ int runMoc(int argc, char **argv)
depFileHandleRaw = fopen(QFile::encodeName(depOutputFileName).constData(), "w");
if (!depFileHandleRaw)
#endif
- fprintf(stderr, "moc: Cannot create dep output file '%s'. %s\n",
+ {
+ const auto fopen_errno = errno;
+ fprintf(stderr, "moc: Cannot create dep output file '%s'. Error: %s\n",
QFile::encodeName(depOutputFileName).constData(),
- strerror(errno));
+ strerror(fopen_errno));
+ }
depFileHandle.reset(depFileHandleRaw);
if (!depFileHandle.isNull()) {
diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp
index 56440129c2..3cbe331f14 100644
--- a/src/tools/moc/moc.cpp
+++ b/src/tools/moc/moc.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "moc.h"
#include "generator.h"
@@ -43,12 +18,23 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// only moc needs this function
static QByteArray normalizeType(const QByteArray &ba)
{
return ba.size() ? normalizeTypeInternal(ba.constBegin(), ba.constEnd()) : ba;
}
+const QByteArray &Moc::toFullyQualified(const QByteArray &name) const noexcept
+{
+ if (auto it = knownQObjectClasses.find(name); it != knownQObjectClasses.end())
+ return it.value();
+ if (auto it = knownGadgets.find(name); it != knownGadgets.end())
+ return it.value();
+ return name;
+}
+
bool Moc::parseClassHead(ClassDef *def)
{
// figure out whether this is a class declaration, or only a
@@ -63,6 +49,9 @@ bool Moc::parseClassHead(ClassDef *def)
return false;
} while (token);
+ // support attributes like "class [[deprecated]]] name"
+ skipCxxAttributes();
+
if (!test(IDENTIFIER)) // typedef struct { ... }
return false;
QByteArray name = lexem();
@@ -107,17 +96,17 @@ bool Moc::parseClassHead(ClassDef *def)
else
test(PUBLIC);
test(VIRTUAL);
- const QByteArray type = parseType().name;
+ const Type type = parseType();
// ignore the 'class Foo : BAR(Baz)' case
if (test(LPAREN)) {
until(RPAREN);
} else {
- def->superclassList += qMakePair(type, access);
+ def->superclassList.push_back({type.name, toFullyQualified(type.name), access});
}
} while (test(COMMA));
if (!def->superclassList.isEmpty()
- && knownGadgets.contains(def->superclassList.constFirst().first)) {
+ && knownGadgets.contains(def->superclassList.constFirst().classname)) {
// Q_GADGET subclasses are treated as Q_GADGETs
knownGadgets.insert(def->classname, def->qualified);
knownGadgets.insert(def->qualified, def->qualified);
@@ -263,7 +252,7 @@ bool Moc::parseEnum(EnumDef *def)
}
if (test(COLON)) { // C++11 strongly typed enum
// enum Foo : unsigned long { ... };
- parseType(); //ignore the result
+ def->type = normalizeType(parseType().name);
}
if (!test(LBRACE))
return false;
@@ -320,7 +309,7 @@ void Moc::parseFunctionArguments(FunctionDef *def)
arg.rightType += lexem();
}
arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType));
- arg.typeNameForCast = normalizeType(QByteArray(noRef(arg.type.name) + "(*)" + arg.rightType));
+ arg.typeNameForCast = QByteArray("std::add_pointer_t<"+arg.normalizedType+">");
if (test(EQ))
arg.isDefault = true;
def->arguments += arg;
@@ -338,6 +327,9 @@ void Moc::parseFunctionArguments(FunctionDef *def)
def->arguments.removeLast();
def->isRawSlot = true;
}
+
+ if (Q_UNLIKELY(def->arguments.size() >= std::numeric_limits<int>::max()))
+ error("number of function arguments exceeds std::numeric_limits<int>::max()");
}
bool Moc::testFunctionAttribute(FunctionDef *def)
@@ -388,7 +380,7 @@ QTypeRevision Moc::parseRevision()
revisionString.remove(0, 1);
revisionString.chop(1);
const QList<QByteArray> majorMinor = revisionString.split(',');
- switch (majorMinor.length()) {
+ switch (majorMinor.size()) {
case 1: {
bool ok = false;
const int revision = revisionString.toInt(&ok);
@@ -429,8 +421,7 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro)
def->isVirtual = false;
def->isStatic = false;
//skip modifiers and attributes
- while (test(INLINE) || (test(STATIC) && (def->isStatic = true) == true) ||
- (test(VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual
+ while (testForFunctionModifiers(def)
|| skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
bool templateFunction = (lookup() == TEMPLATE);
def->type = parseType();
@@ -441,31 +432,29 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro)
error();
}
bool scopedFunctionName = false;
- if (test(LPAREN)) {
- def->name = def->type.name;
- scopedFunctionName = def->type.isScoped;
- def->type = Type("int");
- } else {
- Type tempType = parseType();;
- while (!tempType.name.isEmpty() && lookup() != LPAREN) {
- if (testFunctionAttribute(def->type.firstToken, def))
- ; // fine
- else if (def->type.firstToken == Q_SIGNALS_TOKEN)
- error();
- else if (def->type.firstToken == Q_SLOTS_TOKEN)
- error();
- else {
- if (!def->tag.isEmpty())
- def->tag += ' ';
- def->tag += def->type.name;
- }
- def->type = tempType;
- tempType = parseType();
+ // we might have modifiers and attributes after a tag
+ // note that testFunctionAttribute is handled further below,
+ // and revisions and attributes must come first
+ while (testForFunctionModifiers(def)) {}
+ Type tempType = parseType();
+ while (!tempType.name.isEmpty() && lookup() != LPAREN) {
+ if (testFunctionAttribute(def->type.firstToken, def))
+ ; // fine
+ else if (def->type.firstToken == Q_SIGNALS_TOKEN)
+ error();
+ else if (def->type.firstToken == Q_SLOTS_TOKEN)
+ error();
+ else {
+ if (!def->tag.isEmpty())
+ def->tag += ' ';
+ def->tag += def->type.name;
}
- next(LPAREN, "Not a signal or slot declaration");
- def->name = tempType.name;
- scopedFunctionName = tempType.isScoped;
+ def->type = tempType;
+ tempType = parseType();
}
+ next(LPAREN, "Not a signal or slot declaration");
+ def->name = tempType.name;
+ scopedFunctionName = tempType.isScoped;
if (!test(RPAREN)) {
parseFunctionArguments(def);
@@ -529,14 +518,20 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro)
return true;
}
+bool Moc::testForFunctionModifiers(FunctionDef *def)
+{
+ return test(EXPLICIT) || test(INLINE) ||
+ (test(STATIC) && (def->isStatic = true)) ||
+ (test(VIRTUAL) && (def->isVirtual = true));
+}
+
// like parseFunction, but never aborts with an error
bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
{
def->isVirtual = false;
def->isStatic = false;
//skip modifiers and attributes
- while (test(EXPLICIT) || test(INLINE) || (test(STATIC) && (def->isStatic = true) == true) ||
- (test(VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual
+ while (testForFunctionModifiers(def)
|| skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
bool tilde = test(TILDE);
def->type = parseType();
@@ -551,10 +546,15 @@ bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
def->isConstructor = !tilde;
def->type = Type();
} else {
- def->type = Type("int");
+ // missing type name? => Skip
+ return false;
}
} else {
- Type tempType = parseType();;
+ // ### TODO: The condition before testForFunctionModifiers shoulnd't be necessary,
+ // but otherwise we end up with misparses
+ if (def->isSlot || def->isSignal || def->isInvokable)
+ while (testForFunctionModifiers(def)) {}
+ Type tempType = parseType();
while (!tempType.name.isEmpty() && lookup() != LPAREN) {
if (testFunctionAttribute(def->type.firstToken, def))
; // fine
@@ -601,6 +601,63 @@ bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
return true;
}
+inline void handleDefaultArguments(QList<FunctionDef> *functionList, FunctionDef &function)
+{
+ // support a function with a default argument by pretending there is an
+ // overload without the argument (the original function is the overload with
+ // all arguments present)
+ while (function.arguments.size() > 0 && function.arguments.constLast().isDefault) {
+ function.wasCloned = true;
+ function.arguments.removeLast();
+ *functionList += function;
+ }
+}
+
+void Moc::prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const
+{
+ auto it = namespaceList.crbegin();
+ const auto rend = namespaceList.crend();
+ for (; it != rend; ++it) {
+ if (inNamespace(&*it))
+ def.qualified.prepend(it->classname + "::");
+ }
+}
+
+void Moc::checkListSizes(const ClassDef &def)
+{
+ if (Q_UNLIKELY(def.nonClassSignalList.size() > std::numeric_limits<int>::max()))
+ error("number of signals defined in parent class(es) exceeds "
+ "std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.propertyList.size() > std::numeric_limits<int>::max()))
+ error("number of bindable properties exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.classInfoList.size() > std::numeric_limits<int>::max()))
+ error("number of times Q_CLASSINFO macro is used exceeds "
+ "std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.enumList.size() > std::numeric_limits<int>::max()))
+ error("number of enumerations exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.superclassList.size() > std::numeric_limits<int>::max()))
+ error("number of super classes exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.constructorList.size() > std::numeric_limits<int>::max()))
+ error("number of constructor parameters exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.signalList.size() > std::numeric_limits<int>::max()))
+ error("number of signals exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.slotList.size() > std::numeric_limits<int>::max()))
+ error("number of declared slots exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.methodList.size() > std::numeric_limits<int>::max()))
+ error("number of methods exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.publicList.size() > std::numeric_limits<int>::max()))
+ error("number of public functions declared in this class exceeds "
+ "std::numeric_limits<int>::max().");
+}
void Moc::parse()
{
@@ -610,11 +667,17 @@ void Moc::parse()
Token t = next();
switch (t) {
case NAMESPACE: {
- int rewind = index;
+ qsizetype rewind = index;
if (test(IDENTIFIER)) {
QByteArray nsName = lexem();
QByteArrayList nested;
while (test(SCOPE)) {
+ /* treat (C++20's) namespace A::inline B {} as A::B
+ this is mostly to not break compilation when encountering such
+ a construct in a header; the interaction of Qt's meta-macros with
+ inline namespaces is still rather poor.
+ */
+ test(INLINE);
next(IDENTIFIER);
nested.append(nsName);
nsName = lexem();
@@ -636,11 +699,8 @@ void Moc::parse()
def.end = index;
index = def.begin + 1;
- for (int i = namespaceList.size() - 1; i >= 0; --i) {
- if (inNamespace(&namespaceList.at(i))) {
- def.qualified.prepend(namespaceList.at(i).classname + "::");
- }
- }
+ prependNamespaces(def, namespaceList);
+
for (const QByteArray &ns : nested) {
NamespaceDef parentNs;
parentNs.classname = ns;
@@ -655,8 +715,10 @@ void Moc::parse()
switch (next()) {
case NAMESPACE:
if (test(IDENTIFIER)) {
- while (test(SCOPE))
+ while (test(SCOPE)) {
+ test(INLINE); // ignore inline namespaces
next(IDENTIFIER);
+ }
if (test(EQ)) {
// namespace Foo = Bar::Baz;
until(SEMIC);
@@ -770,6 +832,12 @@ void Moc::parse()
case Q_OBJECT_TOKEN:
def.hasQObject = true;
break;
+ case Q_GADGET_EXPORT_TOKEN:
+ next(LPAREN);
+ while (test(IDENTIFIER))
+ {}
+ next(RPAREN);
+ Q_FALLTHROUGH();
case Q_GADGET_TOKEN:
def.hasQGadget = true;
break;
@@ -780,9 +848,7 @@ void Moc::parse()
if (!def.hasQObject && !def.hasQGadget)
continue;
- for (int i = namespaceList.size() - 1; i >= 0; --i)
- if (inNamespace(&namespaceList.at(i)))
- def.qualified.prepend(namespaceList.at(i).classname + "::");
+ prependNamespaces(def, namespaceList);
QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
classHash.insert(def.classname, def.qualified);
@@ -795,10 +861,9 @@ void Moc::parse()
continue;
ClassDef def;
if (parseClassHead(&def)) {
+ prependNamespaces(def, namespaceList);
+
FunctionDef::Access access = FunctionDef::Private;
- for (int i = namespaceList.size() - 1; i >= 0; --i)
- if (inNamespace(&namespaceList.at(i)))
- def.qualified.prepend(namespaceList.at(i).classname + "::");
while (inClass(&def) && hasNext()) {
switch ((t = next())) {
case PRIVATE:
@@ -847,13 +912,22 @@ void Moc::parse()
if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
error("Class contains Q_OBJECT macro but does not inherit from QObject");
break;
+ case Q_GADGET_EXPORT_TOKEN:
+ next(LPAREN);
+ while (test(IDENTIFIER))
+ {}
+ next(RPAREN);
+ Q_FALLTHROUGH();
case Q_GADGET_TOKEN:
def.hasQGadget = true;
if (templateClass)
error("Template classes not supported by Q_GADGET");
break;
case Q_PROPERTY_TOKEN:
- parseProperty(&def);
+ parseProperty(&def, Named);
+ break;
+ case QT_ANONYMOUS_PROPERTY_TOKEN:
+ parseProperty(&def, Anonymous);
break;
case Q_PLUGIN_METADATA_TOKEN:
parsePluginData(&def);
@@ -888,7 +962,10 @@ void Moc::parse()
parseSlotInPrivate(&def, access);
break;
case Q_PRIVATE_PROPERTY_TOKEN:
- parsePrivateProperty(&def);
+ parsePrivateProperty(&def, Named);
+ break;
+ case QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN:
+ parsePrivateProperty(&def, Anonymous);
break;
case ENUM: {
EnumDef enumDef;
@@ -901,16 +978,12 @@ void Moc::parse()
default:
FunctionDef funcDef;
funcDef.access = access;
- int rewind = index--;
+ qsizetype rewind = index--;
if (parseMaybeFunction(&def, &funcDef)) {
if (funcDef.isConstructor) {
if ((access == FunctionDef::Public) && funcDef.isInvokable) {
def.constructorList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def.constructorList += funcDef;
- }
+ handleDefaultArguments(&def.constructorList, funcDef);
}
} else if (funcDef.isDestructor) {
// don't care about destructors
@@ -919,29 +992,17 @@ void Moc::parse()
def.publicList += funcDef;
if (funcDef.isSlot) {
def.slotList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def.slotList += funcDef;
- }
+ handleDefaultArguments(&def.slotList, funcDef);
if (funcDef.revision > 0)
++def.revisionedMethods;
} else if (funcDef.isSignal) {
def.signalList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def.signalList += funcDef;
- }
+ handleDefaultArguments(&def.signalList, funcDef);
if (funcDef.revision > 0)
++def.revisionedMethods;
} else if (funcDef.isInvokable) {
def.methodList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def.methodList += funcDef;
- }
+ handleDefaultArguments(&def.methodList, funcDef);
if (funcDef.revision > 0)
++def.revisionedMethods;
}
@@ -966,16 +1027,20 @@ void Moc::parse()
if (!def.pluginData.iid.isEmpty())
def.pluginData.metaArgs = metaArgs;
- checkSuperClasses(&def);
+ if (def.hasQObject && !def.superclassList.isEmpty())
+ checkSuperClasses(&def);
+
checkProperties(&def);
+ checkListSizes(def);
+
classList += def;
QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
classHash.insert(def.classname, def.qualified);
classHash.insert(def.qualified, def.qualified);
}
}
- for (const auto &n : qAsConst(namespaceList)) {
+ for (const auto &n : std::as_const(namespaceList)) {
if (!n.hasQNamespace)
continue;
ClassDef def;
@@ -988,8 +1053,10 @@ void Moc::parse()
if (it != classList.end()) {
it->classInfoList += def.classInfoList;
+ Q_ASSERT(it->classInfoList.size() <= std::numeric_limits<int>::max());
it->enumDeclarations.insert(def.enumDeclarations);
it->enumList += def.enumList;
+ Q_ASSERT(it->enumList.size() <= std::numeric_limits<int>::max());
it->flagAliases.insert(def.flagAliases);
} else {
knownGadgets.insert(def.classname, def.qualified);
@@ -1067,25 +1134,27 @@ static QByteArrayList requiredQtContainers(const QList<ClassDef> &classes)
void Moc::generate(FILE *out, FILE *jsonOutput)
{
- QByteArray fn = filename;
- int i = filename.length()-1;
- while (i > 0 && filename.at(i - 1) != '/' && filename.at(i - 1) != '\\')
- --i; // skip path
- if (i >= 0)
- fn = filename.mid(i);
+ QByteArrayView fn = QByteArrayView(filename);
+
+ auto isSlash = [](char ch) { return ch == '/' || ch == '\\'; };
+ auto rit = std::find_if(fn.crbegin(), fn.crend(), isSlash);
+ if (rit != fn.crend())
+ fn = fn.last(rit - fn.crbegin());
+
fprintf(out, "/****************************************************************************\n"
"** Meta object code from reading C++ file '%s'\n**\n" , fn.constData());
fprintf(out, "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR);
fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
"*****************************************************************************/\n\n");
- fprintf(out, "#include <memory>\n"); // For std::addressof
+ // include header(s) of user class definitions at _first_ to allow
+ // for preprocessor definitions possibly affecting standard headers.
+ // see https://codereview.qt-project.org/c/qt/qtbase/+/445937
if (!noInclude) {
if (includePath.size() && !includePath.endsWith('/'))
includePath += '/';
- for (int i = 0; i < includeFiles.size(); ++i) {
- QByteArray inc = includeFiles.at(i);
- if (inc.at(0) != '<' && inc.at(0) != '"') {
+ for (QByteArray inc : std::as_const(includeFiles)) {
+ if (!inc.isEmpty() && inc.at(0) != '<' && inc.at(0) != '"') {
if (includePath.size() && includePath != "./")
inc.prepend(includePath);
inc = '\"' + inc + '\"';
@@ -1096,7 +1165,6 @@ void Moc::generate(FILE *out, FILE *jsonOutput)
if (classList.size() && classList.constFirst().classname == "Qt")
fprintf(out, "#include <QtCore/qobject.h>\n");
- fprintf(out, "#include <QtCore/qbytearray.h>\n"); // For QByteArrayData
fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type
if (mustIncludeQPluginH)
fprintf(out, "#include <QtCore/qplugin.h>\n");
@@ -1105,6 +1173,10 @@ void Moc::generate(FILE *out, FILE *jsonOutput)
for (const QByteArray &qtContainer : qtContainers)
fprintf(out, "#include <QtCore/%s>\n", qtContainer.constData());
+ fprintf(out, "\n#include <QtCore/qtmochelpers.h>\n");
+
+ fprintf(out, "\n#include <memory>\n\n"); // For std::addressof
+ fprintf(out, "\n#include <QtCore/qxptype_traits.h>\n"); // is_detected
fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
"#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData());
@@ -1115,32 +1187,44 @@ void Moc::generate(FILE *out, FILE *jsonOutput)
" much.)\"\n", QT_VERSION_STR);
fprintf(out, "#endif\n\n");
- fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
+#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
+ fprintf(out, "#ifndef Q_CONSTINIT\n"
+ "#define Q_CONSTINIT\n"
+ "#endif\n\n");
+#endif
+
fprintf(out, "QT_WARNING_PUSH\n");
fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n");
+ fprintf(out, "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n");
fputs("", out);
- for (i = 0; i < classList.size(); ++i) {
- Generator generator(&classList[i], metaTypes, knownQObjectClasses, knownGadgets, out, requireCompleteTypes);
+ for (ClassDef &def : classList) {
+ Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, out,
+ requireCompleteTypes);
generator.generateCode();
+
+ // generator.generateCode() should have already registered all strings
+ if (Q_UNLIKELY(generator.registeredStringsCount() >= std::numeric_limits<int>::max())) {
+ error("internal limit exceeded: number of parsed strings is too big.");
+ exit(EXIT_FAILURE);
+ }
}
fputs("", out);
fprintf(out, "QT_WARNING_POP\n");
- fprintf(out, "QT_END_MOC_NAMESPACE\n");
if (jsonOutput) {
QJsonObject mocData;
- mocData[QLatin1String("outputRevision")] = mocOutputRevision;
- mocData[QLatin1String("inputFile")] = QLatin1String(fn.constData());
+ mocData["outputRevision"_L1] = mocOutputRevision;
+ mocData["inputFile"_L1] = QLatin1StringView(fn.constData());
QJsonArray classesJsonFormatted;
- for (const ClassDef &cdef: qAsConst(classList))
+ for (const ClassDef &cdef: std::as_const(classList))
classesJsonFormatted.append(cdef.toJson());
if (!classesJsonFormatted.isEmpty())
- mocData[QLatin1String("classes")] = classesJsonFormatted;
+ mocData["classes"_L1] = classesJsonFormatted;
QJsonDocument jsonDoc(mocData);
fputs(jsonDoc.toJson().constData(), jsonOutput);
@@ -1185,11 +1269,7 @@ void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
++def->revisionedMethods;
}
def->slotList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def->slotList += funcDef;
- }
+ handleDefaultArguments(&def->slotList, funcDef);
}
}
@@ -1233,15 +1313,11 @@ void Moc::parseSignals(ClassDef *def)
++def->revisionedMethods;
}
def->signalList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def->signalList += funcDef;
- }
+ handleDefaultArguments(&def->signalList, funcDef);
}
}
-void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex)
+void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::PropertyMode mode)
{
propDef.location = index;
propDef.relativeIndex = propertyIndex;
@@ -1271,8 +1347,10 @@ void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex)
propDef.type = type;
- next();
- propDef.name = lexem();
+ if (mode == Moc::Named) {
+ next();
+ propDef.name = lexem();
+ }
parsePropertyAttributes(propDef);
}
@@ -1289,7 +1367,8 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
};
while (test(IDENTIFIER)) {
- const QByteArray l = lexem();
+ const Symbol &lsym = symbol();
+ const QByteArray l = lsym.lexem();
if (l[0] == 'C' && l == "CONSTANT") {
propDef.constant = true;
continue;
@@ -1312,11 +1391,15 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
QByteArray v, v2;
if (test(LPAREN)) {
v = lexemUntil(RPAREN);
- v = v.mid(1, v.length() - 2); // removes the '(' and ')'
+ v = v.mid(1, v.size() - 2); // removes the '(' and ')'
} else if (test(INTEGER_LITERAL)) {
v = lexem();
if (l != "REVISION")
- error(1);
+ error(lsym);
+ } else if (test(DEFAULT)) {
+ v = lexem();
+ if (l != "READ" && l != "WRITE")
+ error(lsym);
} else {
next(IDENTIFIER);
v = lexem();
@@ -1330,21 +1413,21 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
if (l == "MEMBER")
propDef.member = v;
else
- error(2);
+ error(lsym);
break;
case 'R':
if (l == "READ")
propDef.read = v;
else if (l == "RESET")
- propDef.reset = v + v2;
+ propDef.reset = v;
else if (l == "REVISION") {
bool ok = false;
const int minor = v.toInt(&ok);
if (!ok || !QTypeRevision::isValidSegment(minor))
- error(1);
+ error(lsym);
propDef.revision = QTypeRevision::fromMinorVersion(minor).toEncodedVersion<int>();
} else
- error(2);
+ error(lsym);
break;
case 'S':
if (l == "SCRIPTABLE") {
@@ -1354,27 +1437,27 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
propDef.stored = v + v2;
checkIsFunction(propDef.stored, "STORED");
} else
- error(2);
+ error(lsym);
break;
- case 'W': if (l != "WRITE") error(2);
+ case 'W': if (l != "WRITE") error(lsym);
propDef.write = v;
break;
- case 'B': if (l != "BINDABLE") error(2);
+ case 'B': if (l != "BINDABLE") error(lsym);
propDef.bind = v;
break;
- case 'D': if (l != "DESIGNABLE") error(2);
+ case 'D': if (l != "DESIGNABLE") error(lsym);
propDef.designable = v + v2;
checkIsFunction(propDef.designable, "DESIGNABLE");
break;
- case 'N': if (l != "NOTIFY") error(2);
+ case 'N': if (l != "NOTIFY") error(lsym);
propDef.notify = v;
break;
- case 'U': if (l != "USER") error(2);
+ case 'U': if (l != "USER") error(lsym);
propDef.user = v + v2;
checkIsFunction(propDef.user, "USER");
break;
default:
- error(2);
+ error(lsym);
}
}
if (propDef.constant && !propDef.write.isNull()) {
@@ -1395,13 +1478,25 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
propDef.constant = false;
warning(msg.constData());
}
+ if (propDef.read == "default" && propDef.bind.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is not BINDable but default-READable. READ will be ignored.";
+ propDef.read = "";
+ warning(msg.constData());
+ }
+ if (propDef.write == "default" && propDef.bind.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is not BINDable but default-WRITEable. WRITE will be ignored.";
+ propDef.write = "";
+ warning(msg.constData());
+ }
}
-void Moc::parseProperty(ClassDef *def)
+void Moc::parseProperty(ClassDef *def, Moc::PropertyMode mode)
{
next(LPAREN);
PropertyDef propDef;
- createPropertyDef(propDef, int(def->propertyList.size()));
+ createPropertyDef(propDef, int(def->propertyList.size()), mode);
next(RPAREN);
def->propertyList += propDef;
@@ -1422,9 +1517,11 @@ void Moc::parsePluginData(ClassDef *def)
} else if (l == "FILE") {
next(STRING_LITERAL);
QByteArray metaDataFile = unquotedLexem();
- QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top().constData())).dir(), QString::fromLocal8Bit(metaDataFile.constData()));
- for (int j = 0; j < includes.size() && !fi.exists(); ++j) {
- const IncludePath &p = includes.at(j);
+ QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top())).dir(),
+ QString::fromLocal8Bit(metaDataFile));
+ for (const IncludePath &p : std::as_const(includes)) {
+ if (fi.exists())
+ break;
if (p.isFrameworkPath)
continue;
@@ -1487,7 +1584,7 @@ QByteArray Moc::parsePropertyAccessor()
return accessor;
}
-void Moc::parsePrivateProperty(ClassDef *def)
+void Moc::parsePrivateProperty(ClassDef *def, Moc::PropertyMode mode)
{
next(LPAREN);
PropertyDef propDef;
@@ -1495,7 +1592,7 @@ void Moc::parsePrivateProperty(ClassDef *def)
next(COMMA);
- createPropertyDef(propDef, int(def->propertyList.size()));
+ createPropertyDef(propDef, int(def->propertyList.size()), mode);
def->propertyList += propDef;
}
@@ -1593,7 +1690,7 @@ void Moc::parseInterfaces(ClassDef *def)
}
}
// resolve from classnames to interface ids
- for (int i = 0; i < iface.count(); ++i) {
+ for (qsizetype i = 0; i < iface.size(); ++i) {
const QByteArray iid = interface2IdMap.value(iface.at(i).className);
if (iid.isEmpty())
error("Undefined interface");
@@ -1662,11 +1759,7 @@ void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
funcDef.access = access;
parseFunction(&funcDef, true);
def->slotList += funcDef;
- while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
- funcDef.wasCloned = true;
- funcDef.arguments.removeLast();
- def->slotList += funcDef;
- }
+ handleDefaultArguments(&def->slotList, funcDef);
if (funcDef.revision > 0)
++def->revisionedMethods;
@@ -1674,7 +1767,7 @@ void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
QByteArray Moc::lexemUntil(Token target)
{
- int from = index;
+ qsizetype from = index;
until(target);
QByteArray s;
while (from <= index) {
@@ -1708,9 +1801,9 @@ bool Moc::until(Token target) {
}
//when searching commas within the default argument, we should take care of template depth (anglecount)
- // unfortunatelly, we do not have enough semantic information to know if '<' is the operator< or
+ // unfortunately, we do not have enough semantic information to know if '<' is the operator< or
// the beginning of a template type. so we just use heuristics.
- int possible = -1;
+ qsizetype possible = -1;
while (index < symbols.size()) {
Token t = symbols.at(index++).token;
@@ -1774,7 +1867,8 @@ bool Moc::until(Token target) {
void Moc::checkSuperClasses(ClassDef *def)
{
- const QByteArray firstSuperclass = def->superclassList.value(0).first;
+ Q_ASSERT(!def->superclassList.isEmpty());
+ const QByteArray &firstSuperclass = def->superclassList.at(0).classname;
if (!knownQObjectClasses.contains(firstSuperclass)) {
// enable once we /require/ include paths
@@ -1789,8 +1883,18 @@ void Moc::checkSuperClasses(ClassDef *def)
#endif
return;
}
- for (int i = 1; i < def->superclassList.count(); ++i) {
- const QByteArray superClass = def->superclassList.at(i).first;
+
+ auto isRegisteredInterface = [&def](QByteArrayView super) {
+ auto matchesSuperClass = [&super](const auto &ifaces) {
+ return !ifaces.isEmpty() && ifaces.first().className == super;
+ };
+ return std::any_of(def->interfaceList.cbegin(), def->interfaceList.cend(), matchesSuperClass);
+ };
+
+ const auto end = def->superclassList.cend();
+ auto it = def->superclassList.cbegin() + 1;
+ for (; it != end; ++it) {
+ const QByteArray &superClass = it->classname;
if (knownQObjectClasses.contains(superClass)) {
const QByteArray msg
= "Class "
@@ -1804,14 +1908,7 @@ void Moc::checkSuperClasses(ClassDef *def)
}
if (interface2IdMap.contains(superClass)) {
- bool registeredInterface = false;
- for (int i = 0; i < def->interfaceList.count(); ++i)
- if (def->interfaceList.at(i).constFirst().className == superClass) {
- registeredInterface = true;
- break;
- }
-
- if (!registeredInterface) {
+ if (!isRegisteredInterface(superClass)) {
const QByteArray msg
= "Class "
+ def->classname
@@ -1829,34 +1926,30 @@ void Moc::checkSuperClasses(ClassDef *def)
void Moc::checkProperties(ClassDef *cdef)
{
//
- // specify get function, for compatibiliy we accept functions
+ // specify get function, for compatibility we accept functions
// returning pointers, or const char * for QByteArray.
//
- QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.count());
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- PropertyDef &p = cdef->propertyList[i];
+ QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.size());
+ auto hasNoAttributes = [&](const PropertyDef &p) {
if (definedProperties.hasSeen(p.name)) {
QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + ".";
warning(msg.constData());
}
if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) {
- const int rewind = index;
- if (p.location >= 0)
- index = p.location;
QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member"
", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.";
- warning(msg.constData());
- index = rewind;
- if (p.write.isEmpty()) {
- cdef->propertyList.removeAt(i);
- --i;
- }
- continue;
+ const auto &sym = p.location >= 0 ? symbolAt(p.location) : Symbol();
+ warning(sym, msg.constData());
+ if (p.write.isEmpty())
+ return true;
}
+ return false;
+ };
+ cdef->propertyList.removeIf(hasNoAttributes);
- for (int j = 0; j < cdef->publicList.count(); ++j) {
- const FunctionDef &f = cdef->publicList.at(j);
+ for (PropertyDef &p : cdef->propertyList) {
+ for (const FunctionDef &f : std::as_const(cdef->publicList)) {
if (f.name != p.read)
continue;
if (!f.isConst) // get functions must be const
@@ -1882,7 +1975,7 @@ void Moc::checkProperties(ClassDef *cdef)
}
if (!p.notify.isEmpty()) {
int notifyId = -1;
- for (int j = 0; j < cdef->signalList.count(); ++j) {
+ for (int j = 0; j < int(cdef->signalList.size()); ++j) {
const FunctionDef &f = cdef->signalList.at(j);
if (f.name != p.notify) {
continue;
@@ -1893,12 +1986,12 @@ void Moc::checkProperties(ClassDef *cdef)
}
p.notifyId = notifyId;
if (notifyId == -1) {
- int index = cdef->nonClassSignalList.indexOf(p.notify);
+ const int index = int(cdef->nonClassSignalList.indexOf(p.notify));
if (index == -1) {
cdef->nonClassSignalList << p.notify;
- p.notifyId = -1 - cdef->nonClassSignalList.count();
+ p.notifyId = int(-1 - cdef->nonClassSignalList.size());
} else {
- p.notifyId = -2 - index;
+ p.notifyId = int(-2 - index);
}
}
}
@@ -1908,19 +2001,19 @@ void Moc::checkProperties(ClassDef *cdef)
QJsonObject ClassDef::toJson() const
{
QJsonObject cls;
- cls[QLatin1String("className")] = QString::fromUtf8(classname.constData());
- cls[QLatin1String("qualifiedClassName")] = QString::fromUtf8(qualified.constData());
+ cls["className"_L1] = QString::fromUtf8(classname.constData());
+ cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData());
QJsonArray classInfos;
- for (const auto &info: qAsConst(classInfoList)) {
+ for (const auto &info: std::as_const(classInfoList)) {
QJsonObject infoJson;
- infoJson[QLatin1String("name")] = QString::fromUtf8(info.name);
- infoJson[QLatin1String("value")] = QString::fromUtf8(info.value);
+ infoJson["name"_L1] = QString::fromUtf8(info.name);
+ infoJson["value"_L1] = QString::fromUtf8(info.value);
classInfos.append(infoJson);
}
if (classInfos.size())
- cls[QLatin1String("classInfos")] = classInfos;
+ cls["classInfos"_L1] = classInfos;
const auto appendFunctions = [&cls](const QString &type, const QList<FunctionDef> &funcs) {
QJsonArray jsonFuncs;
@@ -1932,59 +2025,59 @@ QJsonObject ClassDef::toJson() const
cls[type] = jsonFuncs;
};
- appendFunctions(QLatin1String("signals"), signalList);
- appendFunctions(QLatin1String("slots"), slotList);
- appendFunctions(QLatin1String("constructors"), constructorList);
- appendFunctions(QLatin1String("methods"), methodList);
+ appendFunctions("signals"_L1, signalList);
+ appendFunctions("slots"_L1, slotList);
+ appendFunctions("constructors"_L1, constructorList);
+ appendFunctions("methods"_L1, methodList);
QJsonArray props;
- for (const PropertyDef &propDef: qAsConst(propertyList))
+ for (const PropertyDef &propDef: std::as_const(propertyList))
props.append(propDef.toJson());
if (!props.isEmpty())
- cls[QLatin1String("properties")] = props;
+ cls["properties"_L1] = props;
if (hasQObject)
- cls[QLatin1String("object")] = true;
+ cls["object"_L1] = true;
if (hasQGadget)
- cls[QLatin1String("gadget")] = true;
+ cls["gadget"_L1] = true;
if (hasQNamespace)
- cls[QLatin1String("namespace")] = true;
+ cls["namespace"_L1] = true;
QJsonArray superClasses;
- for (const auto &super: qAsConst(superclassList)) {
- const auto name = super.first;
- const auto access = super.second;
+ for (const auto &super: std::as_const(superclassList)) {
QJsonObject superCls;
- superCls[QLatin1String("name")] = QString::fromUtf8(name);
- FunctionDef::accessToJson(&superCls, access);
+ superCls["name"_L1] = QString::fromUtf8(super.classname);
+ if (super.classname != super.qualified)
+ superCls["fullyQualifiedName"_L1] = QString::fromUtf8(super.qualified);
+ FunctionDef::accessToJson(&superCls, super.access);
superClasses.append(superCls);
}
if (!superClasses.isEmpty())
- cls[QLatin1String("superClasses")] = superClasses;
+ cls["superClasses"_L1] = superClasses;
QJsonArray enums;
- for (const EnumDef &enumDef: qAsConst(enumList))
+ for (const EnumDef &enumDef: std::as_const(enumList))
enums.append(enumDef.toJson(*this));
if (!enums.isEmpty())
- cls[QLatin1String("enums")] = enums;
+ cls["enums"_L1] = enums;
QJsonArray ifaces;
for (const QList<Interface> &ifaceList : interfaceList) {
QJsonArray jsonList;
for (const Interface &iface: ifaceList) {
QJsonObject ifaceJson;
- ifaceJson[QLatin1String("id")] = QString::fromUtf8(iface.interfaceId);
- ifaceJson[QLatin1String("className")] = QString::fromUtf8(iface.className);
+ ifaceJson["id"_L1] = QString::fromUtf8(iface.interfaceId);
+ ifaceJson["className"_L1] = QString::fromUtf8(iface.className);
jsonList.append(ifaceJson);
}
ifaces.append(jsonList);
}
if (!ifaces.isEmpty())
- cls[QLatin1String("interfaces")] = ifaces;
+ cls["interfaces"_L1] = ifaces;
return cls;
}
@@ -1992,22 +2085,25 @@ QJsonObject ClassDef::toJson() const
QJsonObject FunctionDef::toJson() const
{
QJsonObject fdef;
- fdef[QLatin1String("name")] = QString::fromUtf8(name);
+ fdef["name"_L1] = QString::fromUtf8(name);
if (!tag.isEmpty())
- fdef[QLatin1String("tag")] = QString::fromUtf8(tag);
- fdef[QLatin1String("returnType")] = QString::fromUtf8(normalizedType);
+ fdef["tag"_L1] = QString::fromUtf8(tag);
+ fdef["returnType"_L1] = QString::fromUtf8(normalizedType);
QJsonArray args;
for (const ArgumentDef &arg: arguments)
args.append(arg.toJson());
if (!args.isEmpty())
- fdef[QLatin1String("arguments")] = args;
+ fdef["arguments"_L1] = args;
accessToJson(&fdef, access);
if (revision > 0)
- fdef[QLatin1String("revision")] = revision;
+ fdef["revision"_L1] = revision;
+
+ if (wasCloned)
+ fdef["isCloned"_L1] = true;
return fdef;
}
@@ -2015,30 +2111,30 @@ QJsonObject FunctionDef::toJson() const
void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs)
{
switch (acs) {
- case Private: (*obj)[QLatin1String("access")] = QLatin1String("private"); break;
- case Public: (*obj)[QLatin1String("access")] = QLatin1String("public"); break;
- case Protected: (*obj)[QLatin1String("access")] = QLatin1String("protected"); break;
+ case Private: (*obj)["access"_L1] = "private"_L1; break;
+ case Public: (*obj)["access"_L1] = "public"_L1; break;
+ case Protected: (*obj)["access"_L1] = "protected"_L1; break;
}
}
QJsonObject ArgumentDef::toJson() const
{
QJsonObject arg;
- arg[QLatin1String("type")] = QString::fromUtf8(normalizedType);
+ arg["type"_L1] = QString::fromUtf8(normalizedType);
if (!name.isEmpty())
- arg[QLatin1String("name")] = QString::fromUtf8(name);
+ arg["name"_L1] = QString::fromUtf8(name);
return arg;
}
QJsonObject PropertyDef::toJson() const
{
QJsonObject prop;
- prop[QLatin1String("name")] = QString::fromUtf8(name);
- prop[QLatin1String("type")] = QString::fromUtf8(type);
+ prop["name"_L1] = QString::fromUtf8(name);
+ prop["type"_L1] = QString::fromUtf8(type);
const auto jsonify = [&prop](const char *str, const QByteArray &member) {
if (!member.isEmpty())
- prop[QLatin1String(str)] = QString::fromUtf8(member);
+ prop[QLatin1StringView(str)] = QString::fromUtf8(member);
};
jsonify("member", member);
@@ -2057,7 +2153,7 @@ QJsonObject PropertyDef::toJson() const
value = false;
else
value = QString::fromUtf8(boolOrString); // function name to query at run-time
- prop[QLatin1String(str)] = value;
+ prop[QLatin1StringView(str)] = value;
};
jsonifyBoolOrString("designable", designable);
@@ -2065,12 +2161,12 @@ QJsonObject PropertyDef::toJson() const
jsonifyBoolOrString("stored", stored);
jsonifyBoolOrString("user", user);
- prop[QLatin1String("constant")] = constant;
- prop[QLatin1String("final")] = final;
- prop[QLatin1String("required")] = required;
- prop[QLatin1String("index")] = relativeIndex;
+ prop["constant"_L1] = constant;
+ prop["final"_L1] = final;
+ prop["required"_L1] = required;
+ prop["index"_L1] = relativeIndex;
if (revision > 0)
- prop[QLatin1String("revision")] = revision;
+ prop["revision"_L1] = revision;
return prop;
}
@@ -2078,19 +2174,40 @@ QJsonObject PropertyDef::toJson() const
QJsonObject EnumDef::toJson(const ClassDef &cdef) const
{
QJsonObject def;
- def[QLatin1String("name")] = QString::fromUtf8(name);
+ def["name"_L1] = QString::fromUtf8(name);
if (!enumName.isEmpty())
- def[QLatin1String("alias")] = QString::fromUtf8(enumName);
- def[QLatin1String("isFlag")] = cdef.enumDeclarations.value(name);
- def[QLatin1String("isClass")] = isEnumClass;
+ def["alias"_L1] = QString::fromUtf8(enumName);
+ if (!type.isEmpty())
+ def["type"_L1] = QString::fromUtf8(type);
+ def["isFlag"_L1] = cdef.enumDeclarations.value(name);
+ def["isClass"_L1] = isEnumClass;
QJsonArray valueArr;
for (const QByteArray &value: values)
valueArr.append(QString::fromUtf8(value));
if (!valueArr.isEmpty())
- def[QLatin1String("values")] = valueArr;
+ def["values"_L1] = valueArr;
return def;
}
+QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const
+{
+ if (name == cdef->classname) {
+ // The name of the enclosing namespace is the same as the enum class name
+ if (cdef->qualified.contains("::")) {
+ // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum
+ // class name and enclosing namespace, e.g.:
+ // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } }
+ return cdef->qualified % "::" % name;
+ } else {
+ // Just "B"; otherwise the compiler complains about the type "B::B" inside
+ // "B::staticMetaObject" in the generated code; e.g.:
+ // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) }
+ return name;
+ }
+ }
+ return cdef->classname % "::" % name;
+}
+
QT_END_NAMESPACE
diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h
index 1c2d6624fa..c1759fb0a3 100644
--- a/src/tools/moc/moc.h
+++ b/src/tools/moc/moc.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MOC_H
#define MOC_H
@@ -32,13 +7,13 @@
#include "parser.h"
#include <qstringlist.h>
#include <qmap.h>
-#include <qpair.h>
#include <qjsondocument.h>
#include <qjsonarray.h>
#include <qjsonobject.h>
-#include <qversionnumber.h>
+#include <qtyperevision.h>
#include <stdio.h>
-#include <ctype.h>
+
+#include <private/qtools_p.h>
QT_BEGIN_NAMESPACE
@@ -67,10 +42,12 @@ struct EnumDef
{
QByteArray name;
QByteArray enumName;
+ QByteArray type;
QList<QByteArray> values;
bool isEnumClass; // c++11 enum class
EnumDef() : isEnumClass(false) {}
QJsonObject toJson(const ClassDef &cdef) const;
+ QByteArray qualifiedType(const ClassDef *cdef) const;
};
Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE);
@@ -126,8 +103,10 @@ Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE);
struct PropertyDef
{
bool stdCppSet() const {
+ if (name.isEmpty())
+ return false;
QByteArray s("set");
- s += toupper(name[0]);
+ s += QtMiscUtils::toAsciiUpper(name[0]);
s += name.mid(1);
return (s == write);
}
@@ -142,7 +121,7 @@ struct PropertyDef
bool required = false;
int relativeIndex = -1; // property index in current metaobject
- int location = -1; // token index, used for error reporting
+ qsizetype location = -1; // token index, used for error reporting
QJsonObject toJson() const;
};
@@ -172,12 +151,19 @@ struct BaseDef {
QMap<QByteArray, bool> enumDeclarations;
QList<EnumDef> enumList;
QMap<QByteArray, QByteArray> flagAliases;
- int begin = 0;
- int end = 0;
+ qsizetype begin = 0;
+ qsizetype end = 0;
+};
+
+struct SuperClass {
+ QByteArray classname;
+ QByteArray qualified;
+ FunctionDef::Access access;
};
+Q_DECLARE_TYPEINFO(SuperClass, Q_RELOCATABLE_TYPE);
struct ClassDef : BaseDef {
- QList<QPair<QByteArray, FunctionDef::Access>> superclassList;
+ QList<SuperClass> superclassList;
struct Interface
{
@@ -221,6 +207,8 @@ Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE);
class Moc : public Parser
{
public:
+ enum PropertyMode { Named, Anonymous };
+
Moc()
: noInclude(false), mustIncludeQPluginH(false), requireCompleteTypes(false)
{}
@@ -253,6 +241,10 @@ public:
return index > def->begin && index < def->end - 1;
}
+ const QByteArray &toFullyQualified(const QByteArray &name) const noexcept;
+
+ void prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const;
+
Type parseType();
bool parseEnum(EnumDef *def);
@@ -262,9 +254,11 @@ public:
void parseSlots(ClassDef *def, FunctionDef::Access access);
void parseSignals(ClassDef *def);
- void parseProperty(ClassDef *def);
+ void parseProperty(ClassDef *def, PropertyMode mode);
void parsePluginData(ClassDef *def);
- void createPropertyDef(PropertyDef &def, int propertyIndex);
+
+ void createPropertyDef(PropertyDef &def, int propertyIndex, PropertyMode mode);
+
void parsePropertyAttributes(PropertyDef &propDef);
void parseEnumOrFlag(BaseDef *def, bool isFlag);
void parseFlag(BaseDef *def);
@@ -277,7 +271,7 @@ public:
void parseMocInclude();
void parseSlotInPrivate(ClassDef *def, FunctionDef::Access access);
QByteArray parsePropertyAccessor();
- void parsePrivateProperty(ClassDef *def);
+ void parsePrivateProperty(ClassDef *def, PropertyMode mode);
void parseFunctionArguments(FunctionDef *def);
@@ -295,14 +289,17 @@ public:
void checkSuperClasses(ClassDef *def);
void checkProperties(ClassDef* cdef);
+ bool testForFunctionModifiers(FunctionDef *def);
+
+ void checkListSizes(const ClassDef &def);
};
inline QByteArray noRef(const QByteArray &type)
{
if (type.endsWith('&')) {
if (type.endsWith("&&"))
- return type.left(type.length()-2);
- return type.left(type.length()-1);
+ return type.left(type.size()-2);
+ return type.left(type.size()-1);
}
return type;
}
diff --git a/src/tools/moc/outputrevision.h b/src/tools/moc/outputrevision.h
index e8d2d1b1b9..3bc5a872ed 100644
--- a/src/tools/moc/outputrevision.h
+++ b/src/tools/moc/outputrevision.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef OUTPUTREVISION_H
#define OUTPUTREVISION_H
diff --git a/src/tools/moc/parser.cpp b/src/tools/moc/parser.cpp
index 4b67e3a99a..1cfb8ce486 100644
--- a/src/tools/moc/parser.cpp
+++ b/src/tools/moc/parser.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "parser.h"
#include "utils.h"
@@ -33,42 +8,80 @@
QT_BEGIN_NAMESPACE
-#ifdef USE_LEXEM_STORE
-Symbol::LexemStore Symbol::lexemStore;
-#endif
-
static const char *error_msg = nullptr;
+/*! \internal
+ Base implementation for printing diagnostic messages.
+
+ For example:
+ "/path/to/file:line:column: error: %s\n"
+ '%s' is replaced by \a msg. (Currently "column" is always 1).
+
+ If sym.lineNum is -1, the line and column parts aren't printed:
+ "/path/to/file: error: %s\n"
+
+ \a formatStringSuffix specifies the type of the message e.g.:
+ "error: %s\n"
+ "warning: %s\n"
+ "note: %s\n"
+ "Parse error at %s\n" (from defaultErrorMsg())
+*/
+void Parser::printMsg(QByteArrayView formatStringSuffix, QByteArrayView msg, const Symbol &sym)
+{
+ if (sym.lineNum != -1) {
#ifdef Q_CC_MSVC
-#define ErrorFormatString "%s(%d:%d): "
+ QByteArray formatString = "%s(%d:%d): " + formatStringSuffix;
#else
-#define ErrorFormatString "%s:%d:%d: "
+ QByteArray formatString = "%s:%d:%d: " + formatStringSuffix;
#endif
+ fprintf(stderr, formatString.constData(),
+ currentFilenames.top().constData(), sym.lineNum, 1, msg.data());
+ } else {
+ QByteArray formatString = "%s: " + formatStringSuffix;
+ fprintf(stderr, formatString.constData(),
+ currentFilenames.top().constData(), msg.data());
+ }
+}
+
+void Parser::defaultErrorMsg(const Symbol &sym)
+{
+ if (sym.lineNum != -1)
+ printMsg("error: Parse error at \"%s\"\n", sym.lexem().data(), sym);
+ else
+ printMsg("error: could not parse file\n", "", sym);
+}
-void Parser::error(int rollback) {
- index -= rollback;
- error();
+void Parser::error(const Symbol &sym)
+{
+ defaultErrorMsg(sym);
+ exit(EXIT_FAILURE);
}
-void Parser::error(const char *msg) {
+
+void Parser::error(const char *msg)
+{
if (msg || error_msg)
- fprintf(stderr, ErrorFormatString "error: %s\n",
- currentFilenames.top().constData(), symbol().lineNum, 1, msg?msg:error_msg);
+ printMsg("error: %s\n",
+ msg ? msg : error_msg,
+ index > 0 ? symbol() : Symbol{});
else
- fprintf(stderr, ErrorFormatString "error: Parse error at \"%s\"\n",
- currentFilenames.top().constData(), symbol().lineNum, 1, symbol().lexem().data());
+ defaultErrorMsg(symbol());
+
exit(EXIT_FAILURE);
}
+void Parser::warning(const Symbol &sym, QByteArrayView msg)
+{
+ if (displayWarnings)
+ printMsg("warning: %s\n", msg, sym);
+}
+
void Parser::warning(const char *msg) {
- if (displayWarnings && msg)
- fprintf(stderr, ErrorFormatString "warning: %s\n",
- currentFilenames.top().constData(), qMax(0, index > 0 ? symbol().lineNum : 0), 1, msg);
+ warning(index > 0 ? symbol() : Symbol{}, msg);
}
void Parser::note(const char *msg) {
if (displayNotes && msg)
- fprintf(stderr, ErrorFormatString "note: %s\n",
- currentFilenames.top().constData(), qMax(0, index > 0 ? symbol().lineNum : 0), 1, msg);
+ printMsg("note: %s\n", msg, index > 0 ? symbol() : Symbol{});
}
QT_END_NAMESPACE
diff --git a/src/tools/moc/parser.h b/src/tools/moc/parser.h
index f014d7e6b5..6fe982a1ce 100644
--- a/src/tools/moc/parser.h
+++ b/src/tools/moc/parser.h
@@ -1,35 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PARSER_H
#define PARSER_H
#include "symbols.h"
+#include <QtCore/qbytearrayview.h>
#include <stack>
@@ -40,7 +16,7 @@ class Parser
public:
Parser():index(0), displayWarnings(true), displayNotes(true) {}
Symbols symbols;
- int index;
+ qsizetype index;
bool displayWarnings;
bool displayNotes;
@@ -68,11 +44,15 @@ public:
inline QByteArray lexem() { return symbols.at(index-1).lexem();}
inline QByteArray unquotedLexem() { return symbols.at(index-1).unquotedLexem();}
inline const Symbol &symbol() { return symbols.at(index-1);}
+ inline const Symbol &symbolAt(qsizetype idx) { return symbols.at(idx); }
- Q_NORETURN void error(int rollback);
+ Q_NORETURN void error(const Symbol &symbol);
Q_NORETURN void error(const char *msg = nullptr);
void warning(const char * = nullptr);
+ void warning(const Symbol &sym, QByteArrayView msg);
void note(const char * = nullptr);
+ void defaultErrorMsg(const Symbol &sym);
+ void printMsg(QByteArrayView formatStringSuffix, QByteArrayView msg, const Symbol &sym);
};
@@ -87,7 +67,7 @@ inline bool Parser::test(Token token)
inline Token Parser::lookup(int k)
{
- const int l = index - 1 + k;
+ const qsizetype l = index - 1 + k;
return l < symbols.size() ? symbols.at(l).token : NOTOKEN;
}
diff --git a/src/tools/moc/ppkeywords.cpp b/src/tools/moc/ppkeywords.cpp
index b94abf8cd8..dc13f3db95 100644
--- a/src/tools/moc/ppkeywords.cpp
+++ b/src/tools/moc/ppkeywords.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// auto generated
// DO NOT EDIT.
diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp
index c6e84c0913..11ea8d417e 100644
--- a/src/tools/moc/preprocessor.cpp
+++ b/src/tools/moc/preprocessor.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "preprocessor.h"
#include "utils.h"
@@ -33,9 +8,12 @@
#include <qfile.h>
#include <qdir.h>
#include <qfileinfo.h>
+#include <qvarlengtharray.h>
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
#include "ppkeywords.cpp"
#include "keywords.cpp"
@@ -236,7 +214,9 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
data -= 2;
break;
case DIGIT:
- while (is_digit_char(*data) || *data == '\'')
+ {
+ bool hasSeenTokenSeparator = false;;
+ while (isAsciiDigit(*data) || (hasSeenTokenSeparator = *data == '\''))
++data;
if (!*data || *data != '.') {
token = INTEGER_LITERAL;
@@ -245,22 +225,30 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
|| *data == 'b' || *data == 'B')
&& *lexem == '0') {
++data;
- while (is_hex_char(*data) || *data == '\'')
+ while (isHexDigit(*data) || (hasSeenTokenSeparator = *data == '\''))
+ ++data;
+ } else if (*data == 'L') // TODO: handle other suffixes
+ ++data;
+ if (!hasSeenTokenSeparator) {
+ while (is_ident_char(*data)) {
++data;
+ token = IDENTIFIER;
+ }
}
break;
}
token = FLOATING_LITERAL;
++data;
Q_FALLTHROUGH();
+ }
case FLOATING_LITERAL:
- while (is_digit_char(*data) || *data == '\'')
+ while (isAsciiDigit(*data) || *data == '\'')
++data;
if (*data == '+' || *data == '-')
++data;
if (*data == 'e' || *data == 'E') {
++data;
- while (is_digit_char(*data) || *data == '\'')
+ while (isAsciiDigit(*data) || *data == '\'')
++data;
}
if (*data == 'f' || *data == 'F'
@@ -339,16 +327,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
continue; //ignore
}
}
-#ifdef USE_LEXEM_STORE
- if (!Preprocessor::preprocessOnly
- && token != IDENTIFIER
- && token != STRING_LITERAL
- && token != FLOATING_LITERAL
- && token != INTEGER_LITERAL)
- symbols += Symbol(lineNum, token);
- else
-#endif
- symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
+ symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
} else { // Preprocessor
@@ -414,7 +393,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
token = PP_CHARACTER_LITERAL;
break;
case PP_DIGIT:
- while (is_digit_char(*data) || *data == '\'')
+ while (isAsciiDigit(*data) || *data == '\'')
++data;
if (!*data || *data != '.') {
token = PP_INTEGER_LITERAL;
@@ -422,22 +401,23 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
(*data == 'x' || *data == 'X')
&& *lexem == '0') {
++data;
- while (is_hex_char(*data) || *data == '\'')
+ while (isHexDigit(*data) || *data == '\'')
++data;
- }
+ } else if (*data == 'L') // TODO: handle other suffixes
+ ++data;
break;
}
token = PP_FLOATING_LITERAL;
++data;
Q_FALLTHROUGH();
case PP_FLOATING_LITERAL:
- while (is_digit_char(*data) || *data == '\'')
+ while (isAsciiDigit(*data) || *data == '\'')
++data;
if (*data == '+' || *data == '-')
++data;
if (*data == 'e' || *data == 'E') {
++data;
- while (is_digit_char(*data) || *data == '\'')
+ while (isAsciiDigit(*data) || *data == '\'')
++data;
}
if (*data == 'f' || *data == 'F'
@@ -519,22 +499,14 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
}
if (mode == PreparePreprocessorStatement)
continue;
-#ifdef USE_LEXEM_STORE
- if (token != PP_IDENTIFIER
- && token != PP_STRING_LITERAL
- && token != PP_FLOATING_LITERAL
- && token != PP_INTEGER_LITERAL)
- symbols += Symbol(lineNum, token);
- else
-#endif
- symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
+ symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
}
}
symbols += Symbol(); // eof symbol
return symbols;
}
-void Preprocessor::macroExpand(Symbols *into, Preprocessor *that, const Symbols &toExpand, int &index,
+void Preprocessor::macroExpand(Symbols *into, Preprocessor *that, const Symbols &toExpand, qsizetype &index,
int lineNum, bool one, const QSet<QByteArray> &excludeSymbols)
{
SymbolStack symbols;
@@ -642,19 +614,22 @@ Symbols Preprocessor::macroExpandIdentifier(Preprocessor *that, SymbolStack &sym
HashHash
} mode = Normal;
- for (int i = 0; i < macro.symbols.size(); ++i) {
- const Symbol &s = macro.symbols.at(i);
+ const auto end = macro.symbols.cend();
+ auto it = macro.symbols.cbegin();
+ const auto lastSym = std::prev(macro.symbols.cend(), !macro.symbols.isEmpty() ? 1 : 0);
+ for (; it != end; ++it) {
+ const Symbol &s = *it;
if (s.token == HASH || s.token == PP_HASHHASH) {
mode = (s.token == HASH ? Hash : HashHash);
continue;
}
- int index = macro.arguments.indexOf(s);
+ const qsizetype index = macro.arguments.indexOf(s);
if (mode == Normal) {
if (index >= 0 && index < arguments.size()) {
// each argument undoergoes macro expansion if it's not used as part of a # or ##
- if (i == macro.symbols.size() - 1 || macro.symbols.at(i + 1).token != PP_HASHHASH) {
+ if (it == lastSym || std::next(it)->token != PP_HASHHASH) {
Symbols arg = arguments.at(index);
- int idx = 1;
+ qsizetype idx = 1;
macroExpand(&expansion, that, arg, idx, lineNum, false, symbols.excludeSymbols());
} else {
expansion += arguments.at(index);
@@ -673,9 +648,9 @@ Symbols Preprocessor::macroExpandIdentifier(Preprocessor *that, SymbolStack &sym
const Symbols &arg = arguments.at(index);
QByteArray stringified;
- for (int i = 0; i < arg.size(); ++i) {
- stringified += arg.at(i).lexem();
- }
+ for (const Symbol &sym : arg)
+ stringified += sym.lexem();
+
stringified.replace('"', "\\\"");
stringified.prepend('"');
stringified.append('"');
@@ -709,8 +684,8 @@ Symbols Preprocessor::macroExpandIdentifier(Preprocessor *that, SymbolStack &sym
if (index >= 0 && index < arguments.size()) {
const Symbols &arg = arguments.at(index);
- for (int i = 1; i < arg.size(); ++i)
- expansion += arg.at(i);
+ if (!arg.isEmpty())
+ expansion.append(arg.cbegin() + 1, arg.cend());
}
}
mode = Normal;
@@ -951,7 +926,11 @@ int PP_Expression::primary_expression()
test(PP_RPAREN);
} else {
next();
- value = lexem().toInt(nullptr, 0);
+ const QByteArray &lex = lexem();
+ auto lexView = QByteArrayView(lex);
+ if (lex.endsWith('L'))
+ lexView.chop(1);
+ value = lexView.toInt(nullptr, 0);
}
return value;
}
@@ -990,7 +969,7 @@ static void mergeStringLiterals(Symbols *_symbols)
for (Symbols::iterator i = symbols.begin(); i != symbols.end(); ++i) {
if (i->token == STRING_LITERAL) {
Symbols::Iterator mergeSymbol = i;
- int literalsLength = mergeSymbol->len;
+ qsizetype literalsLength = mergeSymbol->len;
while (++i != symbols.end() && i->token == STRING_LITERAL)
literalsLength += i->len - 2; // no quotes
@@ -1004,7 +983,7 @@ static void mergeStringLiterals(Symbols *_symbols)
for (Symbols::iterator j = mergeSymbol + 1; j != i; ++j)
mergeSymbolLexem.append(j->lex.constData() + j->from + 1, j->len - 2); // append j->unquotedLexem()
mergeSymbolLexem.append('"');
- mergeSymbol->len = mergeSymbol->lex.length();
+ mergeSymbol->len = mergeSymbol->lex.size();
mergeSymbol->from = 0;
i = symbols.erase(mergeSymbol + 1, i);
}
@@ -1015,13 +994,21 @@ static void mergeStringLiterals(Symbols *_symbols)
}
static QByteArray searchIncludePaths(const QList<Parser::IncludePath> &includepaths,
- const QByteArray &include)
+ const QByteArray &include,
+ const bool debugIncludes)
{
QFileInfo fi;
- for (int j = 0; j < includepaths.size() && !fi.exists(); ++j) {
- const Parser::IncludePath &p = includepaths.at(j);
+
+ if (Q_UNLIKELY(debugIncludes)) {
+ fprintf(stderr, "debug-includes: searching for '%s'\n", include.constData());
+ }
+
+ for (const Parser::IncludePath &p : includepaths) {
+ if (fi.exists())
+ break;
+
if (p.isFrameworkPath) {
- const int slashPos = include.indexOf('/');
+ const qsizetype slashPos = include.indexOf('/');
if (slashPos == -1)
continue;
fi.setFile(QString::fromLocal8Bit(p.path + '/' + include.left(slashPos) + ".framework/Headers/"),
@@ -1029,6 +1016,12 @@ static QByteArray searchIncludePaths(const QList<Parser::IncludePath> &includepa
} else {
fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include));
}
+
+ if (Q_UNLIKELY(debugIncludes)) {
+ const auto candidate = fi.filePath().toLocal8Bit();
+ fprintf(stderr, "debug-includes: considering '%s'\n", candidate.constData());
+ }
+
// try again, maybe there's a file later in the include paths with the same name
// (186067)
if (fi.isDir()) {
@@ -1037,9 +1030,20 @@ static QByteArray searchIncludePaths(const QList<Parser::IncludePath> &includepa
}
}
- if (!fi.exists() || fi.isDir())
+ if (!fi.exists() || fi.isDir()) {
+ if (Q_UNLIKELY(debugIncludes)) {
+ fprintf(stderr, "debug-includes: can't find '%s'\n", include.constData());
+ }
return QByteArray();
- return fi.canonicalFilePath().toLocal8Bit();
+ }
+
+ const auto result = fi.canonicalFilePath().toLocal8Bit();
+
+ if (Q_UNLIKELY(debugIncludes)) {
+ fprintf(stderr, "debug-includes: found '%s'\n", result.constData());
+ }
+
+ return result;
}
QByteArray Preprocessor::resolveInclude(const QByteArray &include, const QByteArray &relativeTo)
@@ -1053,7 +1057,11 @@ QByteArray Preprocessor::resolveInclude(const QByteArray &include, const QByteAr
auto it = nonlocalIncludePathResolutionCache.find(include);
if (it == nonlocalIncludePathResolutionCache.end())
- it = nonlocalIncludePathResolutionCache.insert(include, searchIncludePaths(includes, include));
+ it = nonlocalIncludePathResolutionCache.insert(include,
+ searchIncludePaths(
+ includes,
+ include,
+ debugIncludes));
return it.value();
}
@@ -1096,7 +1104,7 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
continue;
Symbols saveSymbols = symbols;
- int saveIndex = index;
+ qsizetype saveIndex = index;
// phase 1: get rid of backslash-newlines
input = cleaned(input);
@@ -1131,14 +1139,14 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
} else {
macro.isFunction = false;
}
- int start = index;
+ qsizetype start = index;
until(PP_NEWLINE);
macro.symbols.reserve(index - start - 1);
// remove whitespace where there shouldn't be any:
// Before and after the macro, after a # and around ##
Token lastToken = HASH; // skip shitespace at the beginning
- for (int i = start; i < index - 1; ++i) {
+ for (qsizetype i = start; i < index - 1; ++i) {
Token token = symbols.at(i).token;
if (token == WHITESPACE) {
if (lastToken == PP_HASH || lastToken == HASH ||
@@ -1281,7 +1289,7 @@ void Preprocessor::parseDefineArguments(Macro *m)
if (!test(PP_RPAREN))
error("missing ')' in macro argument list");
break;
- } else if (!is_identifier(l.constData(), l.length())) {
+ } else if (!is_identifier(l.constData(), l.size())) {
error("Unexpected character in macro argument list.");
}
}
@@ -1318,4 +1326,10 @@ void Preprocessor::until(Token t)
;
}
+void Preprocessor::setDebugIncludes(bool value)
+{
+ debugIncludes = value;
+}
+
+
QT_END_NAMESPACE
diff --git a/src/tools/moc/preprocessor.h b/src/tools/moc/preprocessor.h
index 39f56d6e92..3509e83dce 100644
--- a/src/tools/moc/preprocessor.h
+++ b/src/tools/moc/preprocessor.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PREPROCESSOR_H
#define PREPROCESSOR_H
@@ -45,11 +20,7 @@ struct Macro
Symbols symbols;
};
-#ifdef USE_LEXEM_STORE
-typedef QByteArray MacroName;
-#else
typedef SubArray MacroName;
-#endif
typedef QHash<MacroName, Macro> Macros;
class QFile;
@@ -73,18 +44,22 @@ public:
void substituteUntilNewline(Symbols &substituted);
static Symbols macroExpandIdentifier(Preprocessor *that, SymbolStack &symbols, int lineNum, QByteArray *macroName);
- static void macroExpand(Symbols *into, Preprocessor *that, const Symbols &toExpand, int &index, int lineNum, bool one,
- const QSet<QByteArray> &excludeSymbols = QSet<QByteArray>());
+ static void macroExpand(Symbols *into, Preprocessor *that, const Symbols &toExpand,
+ qsizetype &index, int lineNum, bool one,
+ const QSet<QByteArray> &excludeSymbols = QSet<QByteArray>());
int evaluateCondition();
enum TokenizeMode { TokenizeCpp, TokenizePreprocessor, PreparePreprocessorStatement, TokenizePreprocessorStatement, TokenizeInclude, PrepareDefine, TokenizeDefine };
static Symbols tokenize(const QByteArray &input, int lineNum = 1, TokenizeMode mode = TokenizeCpp);
+ void setDebugIncludes(bool value);
+
private:
void until(Token);
void preprocess(const QByteArray &filename, Symbols &preprocessed);
+ bool debugIncludes = false;
};
QT_END_NAMESPACE
diff --git a/src/tools/moc/symbols.h b/src/tools/moc/symbols.h
index bbb1312cdc..869f7c793f 100644
--- a/src/tools/moc/symbols.h
+++ b/src/tools/moc/symbols.h
@@ -1,111 +1,62 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SYMBOLS_H
#define SYMBOLS_H
#include "token.h"
#include <qdebug.h>
-#include <qhash.h>
+#include <qhashfunctions.h>
#include <qlist.h>
#include <qstack.h>
#include <qstring.h>
+#include <qset.h>
QT_BEGIN_NAMESPACE
-//#define USE_LEXEM_STORE
-
struct SubArray
{
- inline SubArray():from(0),len(-1){}
+ inline SubArray() = default;
inline SubArray(const QByteArray &a):array(a),from(0), len(a.size()){}
inline SubArray(const char *s):array(s),from(0) { len = array.size(); }
- inline SubArray(const QByteArray &a, int from, int len):array(a), from(from), len(len){}
+ SubArray(const QByteArray &a, qsizetype from, qsizetype len)
+ : array(a), from(from), len(len)
+ {
+ }
QByteArray array;
- int from, len;
+ qsizetype from = 0;
+ qsizetype len = -1;
inline bool operator==(const SubArray &other) const {
if (len != other.len)
return false;
- for (int i = 0; i < len; ++i)
- if (array.at(from + i) != other.array.at(other.from + i))
- return false;
- return true;
+ const auto begin = array.cbegin() + from;
+ const auto end = begin + len;
+ const auto other_begin = other.array.cbegin() + other.from;
+ return std::equal(begin, end, other_begin);
}
};
-inline size_t qHash(const SubArray &key)
+inline size_t qHash(const SubArray &key, size_t seed = 0)
{
- return qHash(QLatin1String(key.array.constData() + key.from, key.len));
+ return qHash(QLatin1StringView(key.array.constData() + key.from, key.len), seed);
}
struct Symbol
{
-
-#ifdef USE_LEXEM_STORE
- typedef QHash<SubArray, QHashDummyValue> LexemStore;
- static LexemStore lexemStore;
-
- inline Symbol() : lineNum(-1),token(NOTOKEN){}
- inline Symbol(int lineNum, Token token):
- lineNum(lineNum), token(token){}
- inline Symbol(int lineNum, Token token, const QByteArray &lexem):
- lineNum(lineNum), token(token),lex(lexem){}
- inline Symbol(int lineNum, Token token, const QByteArray &lexem, int from, int len):
- lineNum(lineNum), token(token){
- LexemStore::const_iterator it = lexemStore.constFind(SubArray(lexem, from, len));
-
- if (it != lexemStore.constEnd()) {
- lex = it.key().array;
- } else {
- lex = lexem.mid(from, len);
- lexemStore.insert(lex, QHashDummyValue());
- }
+ inline Symbol() = default;
+ inline Symbol(int lineNum, Token token) : lineNum(lineNum), token(token) { }
+ inline Symbol(int lineNum, Token token, const QByteArray &lexem)
+ : lineNum(lineNum), token(token), lex(lexem), len(lex.size())
+ {
}
- int lineNum;
- Token token;
- inline QByteArray unquotedLexem() const { return lex.mid(1, lex.length()-2); }
- inline QByteArray lexem() const { return lex; }
- inline operator QByteArray() const { return lex; }
- QByteArray lex;
-
-#else
-
- inline Symbol() : lineNum(-1),token(NOTOKEN), from(0),len(-1) {}
- inline Symbol(int lineNum, Token token):
- lineNum(lineNum), token(token), from(0), len(-1) {}
- inline Symbol(int lineNum, Token token, const QByteArray &lexem):
- lineNum(lineNum), token(token), lex(lexem), from(0) { len = lex.size(); }
- inline Symbol(int lineNum, Token token, const QByteArray &lexem, int from, int len):
- lineNum(lineNum), token(token),lex(lexem),from(from), len(len){}
- int lineNum;
- Token token;
+ Symbol(int lineNum, Token token, const QByteArray &lexem, qsizetype from, qsizetype len)
+ : lineNum(lineNum), token(token), lex(lexem), from(from), len(len)
+ {
+ }
+ int lineNum = -1;
+ Token token = NOTOKEN;
inline QByteArray lexem() const { return lex.mid(from, len); }
inline QByteArray unquotedLexem() const { return lex.mid(from+1, len-2); }
inline operator SubArray() const { return SubArray(lex, from, len); }
@@ -114,9 +65,8 @@ struct Symbol
return SubArray(lex, from, len) == SubArray(o.lex, o.from, o.len);
}
QByteArray lex;
- int from, len;
-
-#endif
+ qsizetype from = 0;
+ qsizetype len = -1;
};
Q_DECLARE_TYPEINFO(Symbol, Q_RELOCATABLE_TYPE);
@@ -126,7 +76,7 @@ struct SafeSymbols {
Symbols symbols;
QByteArray expandedMacro;
QSet<QByteArray> excludedSymbols;
- int index;
+ qsizetype index;
};
Q_DECLARE_TYPEINFO(SafeSymbols, Q_RELOCATABLE_TYPE);
@@ -151,13 +101,13 @@ public:
inline QByteArray lexem() const { return symbol().lexem(); }
inline QByteArray unquotedLexem() { return symbol().unquotedLexem(); }
- bool dontReplaceSymbol(const QByteArray &name);
- QSet<QByteArray> excludeSymbols();
+ bool dontReplaceSymbol(const QByteArray &name) const;
+ QSet<QByteArray> excludeSymbols() const;
};
inline bool SymbolStack::test(Token token)
{
- int stackPos = size() - 1;
+ qsizetype stackPos = size() - 1;
while (stackPos >= 0 && at(stackPos).index >= at(stackPos).symbols.size())
--stackPos;
if (stackPos < 0)
@@ -169,21 +119,20 @@ inline bool SymbolStack::test(Token token)
return false;
}
-inline bool SymbolStack::dontReplaceSymbol(const QByteArray &name)
+inline bool SymbolStack::dontReplaceSymbol(const QByteArray &name) const
{
- for (int i = 0; i < size(); ++i) {
- if (name == at(i).expandedMacro || at(i).excludedSymbols.contains(name))
- return true;
- }
- return false;
+ auto matchesName = [&name](const SafeSymbols &sf) {
+ return name == sf.expandedMacro || sf.excludedSymbols.contains(name);
+ };
+ return std::any_of(cbegin(), cend(), matchesName);
}
-inline QSet<QByteArray> SymbolStack::excludeSymbols()
+inline QSet<QByteArray> SymbolStack::excludeSymbols() const
{
QSet<QByteArray> set;
- for (int i = 0; i < size(); ++i) {
- set << at(i).expandedMacro;
- set += at(i).excludedSymbols;
+ for (const SafeSymbols &sf : *this) {
+ set << sf.expandedMacro;
+ set += sf.excludedSymbols;
}
return set;
}
diff --git a/src/tools/moc/token.cpp b/src/tools/moc/token.cpp
index cf1aa102c7..bfefaff57b 100644
--- a/src/tools/moc/token.cpp
+++ b/src/tools/moc/token.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "token.h"
diff --git a/src/tools/moc/token.h b/src/tools/moc/token.h
index c11ec6a38c..a70808370d 100644
--- a/src/tools/moc/token.h
+++ b/src/tools/moc/token.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TOKEN_H
#define TOKEN_H
@@ -154,9 +129,11 @@ QT_BEGIN_NAMESPACE
F(RETURN) \
F(Q_OBJECT_TOKEN) \
F(Q_GADGET_TOKEN) \
+ F(Q_GADGET_EXPORT_TOKEN) \
F(Q_NAMESPACE_TOKEN) \
F(Q_NAMESPACE_EXPORT_TOKEN) \
F(Q_PROPERTY_TOKEN) \
+ F(QT_ANONYMOUS_PROPERTY_TOKEN) \
F(Q_PLUGIN_METADATA_TOKEN) \
F(Q_ENUMS_TOKEN) \
F(Q_ENUM_TOKEN) \
@@ -178,6 +155,7 @@ QT_BEGIN_NAMESPACE
F(Q_INVOKABLE_TOKEN) \
F(Q_SCRIPTABLE_TOKEN) \
F(Q_PRIVATE_PROPERTY_TOKEN) \
+ F(QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN) \
F(Q_REVISION_TOKEN) \
F(Q_MOC_INCLUDE_TOKEN) \
F(SPECIAL_TREATMENT_MARK) \
diff --git a/src/tools/moc/util/generate.sh b/src/tools/moc/util/generate.sh
index 5460d28924..6be06e5a91 100755
--- a/src/tools/moc/util/generate.sh
+++ b/src/tools/moc/util/generate.sh
@@ -1,37 +1,12 @@
#!/bin/sh
-#############################################################################
-##
-## Copyright (C) 2016 The Qt Company Ltd.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is the build configuration utility of the Qt Toolkit.
-##
-## $QT_BEGIN_LICENSE:GPL-EXCEPT$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 3 as published by the Free Software
-## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
+# Copyright (C) 2016 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
set -ex
qmake
make
-cat licenseheader.txt > ../keywords.cpp
-cat licenseheader.txt > ../ppkeywords.cpp
+cat licenseheader.cpp.in > ../keywords.cpp
+cat licenseheader.cpp.in > ../ppkeywords.cpp
./generate_keywords >> ../keywords.cpp
./generate_keywords preprocessor >> ../ppkeywords.cpp
diff --git a/src/tools/moc/util/generate_keywords.cpp b/src/tools/moc/util/generate_keywords.cpp
index 28c5eeff38..a6c85af9f1 100644
--- a/src/tools/moc/util/generate_keywords.cpp
+++ b/src/tools/moc/util/generate_keywords.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <stdio.h>
#include <string.h>
#include <qbytearray.h>
@@ -216,7 +191,9 @@ static const Keyword keywords[] = {
{ "Q_NAMESPACE", "Q_NAMESPACE_TOKEN" },
{ "Q_NAMESPACE_EXPORT", "Q_NAMESPACE_EXPORT_TOKEN" },
{ "Q_GADGET", "Q_GADGET_TOKEN" },
+ { "Q_GADGET_EXPORT", "Q_GADGET_EXPORT_TOKEN" },
{ "Q_PROPERTY", "Q_PROPERTY_TOKEN" },
+ { "QT_ANONYMOUS_PROPERTY", "QT_ANONYMOUS_PROPERTY_TOKEN" },
{ "Q_PLUGIN_METADATA", "Q_PLUGIN_METADATA_TOKEN" },
{ "Q_ENUMS", "Q_ENUMS_TOKEN" },
{ "Q_ENUM", "Q_ENUM_TOKEN" },
@@ -242,6 +219,7 @@ static const Keyword keywords[] = {
{ "Q_SLOT", "Q_SLOT_TOKEN" },
{ "Q_SCRIPTABLE", "Q_SCRIPTABLE_TOKEN" },
{ "Q_PRIVATE_PROPERTY", "Q_PRIVATE_PROPERTY_TOKEN" },
+ { "QT_ANONYMOUS_PRIVATE_PROPERTY", "QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN" },
{ "Q_REVISION", "Q_REVISION_TOKEN" },
{ "Q_MOC_INCLUDE", "Q_MOC_INCLUDE_TOKEN" },
{ "\n", "NEWLINE" },
diff --git a/src/tools/moc/util/generate_keywords.pro b/src/tools/moc/util/generate_keywords.pro
new file mode 100644
index 0000000000..e29738c18a
--- /dev/null
+++ b/src/tools/moc/util/generate_keywords.pro
@@ -0,0 +1,5 @@
+CONFIG -= moc
+CONFIG += cmdline
+QT = core
+
+SOURCES += generate_keywords.cpp
diff --git a/src/tools/moc/util/licenseheader.cpp.in b/src/tools/moc/util/licenseheader.cpp.in
new file mode 100644
index 0000000000..42958a66f5
--- /dev/null
+++ b/src/tools/moc/util/licenseheader.cpp.in
@@ -0,0 +1,3 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
diff --git a/src/tools/moc/util/licenseheader.txt b/src/tools/moc/util/licenseheader.txt
deleted file mode 100644
index b2b02f82eb..0000000000
--- a/src/tools/moc/util/licenseheader.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
diff --git a/src/tools/moc/utils.h b/src/tools/moc/utils.h
index 4cb1d90345..0b0d70f462 100644
--- a/src/tools/moc/utils.h
+++ b/src/tools/moc/utils.h
@@ -1,35 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef UTILS_H
#define UTILS_H
#include <QtCore/qglobal.h>
+#include <private/qtools_p.h>
+
+#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -45,49 +23,20 @@ inline bool is_space(char s)
inline bool is_ident_start(char s)
{
- return ((s >= 'a' && s <= 'z')
- || (s >= 'A' && s <= 'Z')
- || s == '_' || s == '$'
- );
+ using namespace QtMiscUtils;
+ return isAsciiLower(s) || isAsciiUpper(s) || s == '_' || s == '$';
}
inline bool is_ident_char(char s)
{
- return ((s >= 'a' && s <= 'z')
- || (s >= 'A' && s <= 'Z')
- || (s >= '0' && s <= '9')
- || s == '_' || s == '$'
- );
+ return QtMiscUtils::isAsciiLetterOrNumber(s) || s == '_' || s == '$';
}
-inline bool is_identifier(const char *s, int len)
+inline bool is_identifier(const char *s, qsizetype len)
{
if (len < 1)
return false;
- if (!is_ident_start(*s))
- return false;
- for (int i = 1; i < len; ++i)
- if (!is_ident_char(s[i]))
- return false;
- return true;
-}
-
-inline bool is_digit_char(char s)
-{
- return (s >= '0' && s <= '9');
-}
-
-inline bool is_octal_char(char s)
-{
- return (s >= '0' && s <= '7');
-}
-
-inline bool is_hex_char(char s)
-{
- return ((s >= 'a' && s <= 'f')
- || (s >= 'A' && s <= 'F')
- || (s >= '0' && s <= '9')
- );
+ return std::all_of(s, s + len, is_ident_char);
}
inline const char *skipQuote(const char *data)
diff --git a/src/tools/qdbuscpp2xml/CMakeLists.txt b/src/tools/qdbuscpp2xml/CMakeLists.txt
index 865995755b..781c1835bc 100644
--- a/src/tools/qdbuscpp2xml/CMakeLists.txt
+++ b/src/tools/qdbuscpp2xml/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from qdbuscpp2xml.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qdbuscpp2xml Tool:
@@ -6,8 +7,10 @@
qt_get_tool_target_name(target_name qdbuscpp2xml)
qt_internal_add_tool(${target_name}
+ TRY_RUN
+ TRY_RUN_FLAGS "-V"
TARGET_DESCRIPTION "Qt D-Bus C++ to XML Compiler"
- TOOLS_TARGET DBus # special case
+ TOOLS_TARGET DBus
SOURCES
../moc/cbordevice.h
../moc/collectjson.cpp ../moc/collectjson.h
@@ -26,24 +29,8 @@ qt_internal_add_tool(${target_name}
INCLUDE_DIRECTORIES
../moc
../moc/../../3rdparty/tinycbor/src
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::DBusPrivate
- # COMPILE_OPTIONS # special case
- # "$$QT_HOST_CFLAGS_DBUS"
- # QMAKE_TARGET_DESCRIPTION = "Qt D-Bus C++ to XML Compiler"
- # _LOADED = "qt_tool"
- # _OPTION = "host_build"
)
-
-#### Keys ignored in scope 1:.:.:qdbuscpp2xml.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt D-Bus C++ to XML Compiler"
-# _OPTION = "host_build"
-
-## Scopes:
-#####################################################################
-
-# special case begin
-# qt_internal_extend_target(qdbuscpp2xml CONDITION force_bootstrap [...])
-# qt_internal_extend_target(qdbuscpp2xml CONDITION NOT force_bootstrap [...])
-# special case end
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
index fad92994f9..3b7d73894b 100644
--- a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
+++ b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qbuffer.h>
#include <qbytearray.h>
@@ -40,6 +15,9 @@
#include <qdbusconnection.h> // for the Export* flags
#include <private/qdbusconnection_p.h> // for the qDBusCheckAsyncTag
+#include <private/qdbusmetatype_p.h> // for QDBusMetaTypeId::init()
+
+using namespace Qt::StringLiterals;
// copied from dbus-protocol.h:
static const char docTypeHeader[] =
@@ -60,7 +38,7 @@ static const char docTypeHeader[] =
#define PROGRAMNAME "qdbuscpp2xml"
#define PROGRAMVERSION "0.2"
-#define PROGRAMCOPYRIGHT "Copyright (C) 2021 The Qt Company Ltd."
+#define PROGRAMCOPYRIGHT QT_COPYRIGHT
static QString outputFile;
static int flags;
@@ -95,7 +73,7 @@ int qDBusParametersForMethod(const FunctionDef &mm, QList<QMetaType> &metaTypes,
static inline QString typeNameToXml(const char *typeName)
{
- QString plain = QLatin1String(typeName);
+ QString plain = QLatin1StringView(typeName);
return plain.toHtmlEscaped();
}
@@ -133,13 +111,13 @@ static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
qWarning() << qPrintable(errorMsg);
return QString(); // invalid form
}
- if (isSignal && inputCount + 1 != types.count())
+ if (isSignal && inputCount + 1 != types.size())
return QString(); // signal with output arguments?
if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message())
return QString(); // signal with QDBusMessage argument?
bool isScriptable = mm.isScriptable;
- for (int j = 1; j < types.count(); ++j) {
+ for (qsizetype j = 1; j < types.size(); ++j) {
// input parameter for a slot or output for a signal
if (types.at(j) == QDBusMetaTypeId::message()) {
isScriptable = true;
@@ -155,14 +133,14 @@ static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
const char *signature = QDBusMetaType::typeToSignature(QMetaType(types.at(j)));
xml += QString::fromLatin1(" <arg %1type=\"%2\" direction=\"%3\"/>\n")
.arg(name,
- QLatin1String(signature),
- isOutput ? QLatin1String("out") : QLatin1String("in"));
+ QLatin1StringView(signature),
+ isOutput ? "out"_L1 : "in"_L1);
// do we need to describe this argument?
if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
const char *typeName = QMetaType(types.at(j)).name();
xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
- .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
+ .arg(isOutput ? "Out"_L1 : "In"_L1)
.arg(isOutput && !isSignal ? j - inputCount : j - 1)
.arg(typeNameToXml(typeName));
}
@@ -180,12 +158,10 @@ static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
if (qDBusCheckAsyncTag(mm.tag.constData()))
// add the no-reply annotation
- xml += QLatin1String(" <annotation name=\"" ANNOTATION_NO_WAIT "\""
- " value=\"true\"/>\n");
+ xml += " <annotation name=\"" ANNOTATION_NO_WAIT "\" value=\"true\"/>\n"_L1;
QString retval = xml;
- retval += QString::fromLatin1(" </%1>\n")
- .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
+ retval += QString::fromLatin1(" </%1>\n").arg(isSignal ? "signal"_L1 : "method"_L1);
return retval;
}
@@ -209,6 +185,8 @@ static QString generateInterfaceXml(const ClassDef *mo)
access |= 1;
if (!mp.write.isEmpty())
access |= 2;
+ if (!mp.member.isEmpty())
+ access |= 3;
int typeId = QMetaType::fromName(mp.type).id();
if (!typeId) {
@@ -221,15 +199,15 @@ static QString generateInterfaceXml(const ClassDef *mo)
continue;
retval += QString::fromLatin1(" <property name=\"%1\" type=\"%2\" access=\"%3\"")
- .arg(QLatin1String(mp.name),
- QLatin1String(signature),
- QLatin1String(accessvalues[access]));
+ .arg(QLatin1StringView(mp.name),
+ QLatin1StringView(signature),
+ QLatin1StringView(accessvalues[access]));
if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
retval += QString::fromLatin1(">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
.arg(typeNameToXml(mp.type.constData()));
} else {
- retval += QLatin1String("/>\n");
+ retval += "/>\n"_L1;
}
}
}
@@ -272,17 +250,17 @@ QString qDBusInterfaceFromClassDef(const ClassDef *mo)
if (cid.name == QCLASSINFO_DBUS_INTERFACE)
return QString::fromUtf8(cid.value);
}
- interface = QLatin1String(mo->classname);
- interface.replace(QLatin1String("::"), QLatin1String("."));
+ interface = QLatin1StringView(mo->classname);
+ interface.replace("::"_L1, "."_L1);
- if (interface.startsWith(QLatin1String("QDBus"))) {
- interface.prepend(QLatin1String("org.qtproject.QtDBus."));
- } else if (interface.startsWith(QLatin1Char('Q')) &&
- interface.length() >= 2 && interface.at(1).isUpper()) {
+ if (interface.startsWith("QDBus"_L1)) {
+ interface.prepend("org.qtproject.QtDBus."_L1);
+ } else if (interface.startsWith(u'Q') &&
+ interface.size() >= 2 && interface.at(1).isUpper()) {
// assume it's Qt
- interface.prepend(QLatin1String("local.org.qtproject.Qt."));
+ interface.prepend("local.org.qtproject.Qt."_L1);
} else {
- interface.prepend(QLatin1String("local."));
+ interface.prepend("local."_L1);
}
return interface;
@@ -358,16 +336,16 @@ static std::deque<CustomType> s_customTypes;
static void parseCmdLine(QStringList &arguments)
{
flags = 0;
- for (int i = 0; i < arguments.count(); ++i) {
+ for (qsizetype i = 0; i < arguments.size(); ++i) {
const QString arg = arguments.at(i);
- if (arg == QLatin1String("--help"))
+ if (arg == "--help"_L1)
showHelp();
- if (!arg.startsWith(QLatin1Char('-')))
+ if (!arg.startsWith(u'-'))
continue;
- char c = arg.count() == 2 ? arg.at(1).toLatin1() : char(0);
+ char c = arg.size() == 2 ? arg.at(1).toLatin1() : char(0);
switch (c) {
case 'P':
flags |= QDBusConnection::ExportNonScriptableProperties;
@@ -398,13 +376,13 @@ static void parseCmdLine(QStringList &arguments)
break;
case 't':
- if (arguments.count() < i + 2) {
+ if (arguments.size() < i + 2) {
printf("-t expects a type=dbustype argument\n");
exit(1);
} else {
const QByteArray arg = arguments.takeAt(i + 1).toUtf8();
// lastIndexOf because the C++ type could contain '=' while the DBus type can't
- const int separator = arg.lastIndexOf('=');
+ const qsizetype separator = arg.lastIndexOf('=');
if (separator == -1) {
printf("-t expects a type=dbustype argument, but no '=' was found\n");
exit(1);
@@ -419,7 +397,7 @@ static void parseCmdLine(QStringList &arguments)
break;
case 'o':
- if (arguments.count() < i + 2 || arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
+ if (arguments.size() < i + 2 || arguments.at(i + 1).startsWith(u'-')) {
printf("-o expects a filename\n");
exit(1);
}
@@ -454,18 +432,30 @@ int main(int argc, char **argv)
args.append(QString::fromLocal8Bit(argv[n]));
parseCmdLine(args);
- QList<ClassDef> classes;
+ QDBusMetaTypeId::init();
- for (int i = 0; i < args.count(); ++i) {
- const QString arg = args.at(i);
+ QList<ClassDef> classes;
- if (arg.startsWith(QLatin1Char('-')))
+ if (args.isEmpty())
+ args << u"-"_s;
+ for (const auto &arg: std::as_const(args)) {
+ if (arg.startsWith(u'-') && arg.size() > 1)
continue;
- QFile f(arg);
- if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) {
+ QFile f;
+ bool fileIsOpen;
+ QString fileName;
+ if (arg == u'-') {
+ fileName = "stdin"_L1;
+ fileIsOpen = f.open(stdin, QIODevice::ReadOnly | QIODevice::Text);
+ } else {
+ fileName = arg;
+ f.setFileName(arg);
+ fileIsOpen = f.open(QIODevice::ReadOnly | QIODevice::Text);
+ }
+ if (!fileIsOpen) {
fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
- qPrintable(arg), qPrintable(f.errorString()));
+ qPrintable(fileName), qPrintable(f.errorString()));
return 1;
}
@@ -491,11 +481,15 @@ int main(int argc, char **argv)
QFile output;
if (outputFile.isEmpty()) {
- output.open(stdout, QIODevice::WriteOnly);
+ if (!output.open(stdout, QIODevice::WriteOnly)) {
+ fprintf(stderr, PROGRAMNAME ": could not open standard output: %s\n",
+ qPrintable(output.errorString()));
+ return 1;
+ }
} else {
output.setFileName(outputFile);
if (!output.open(QIODevice::WriteOnly)) {
- fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s",
+ fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s\n",
qPrintable(outputFile), qPrintable(output.errorString()));
return 1;
}
@@ -503,7 +497,7 @@ int main(int argc, char **argv)
output.write(docTypeHeader);
output.write("<node>\n");
- for (const ClassDef &cdef : qAsConst(classes)) {
+ for (const ClassDef &cdef : std::as_const(classes)) {
QString xml = qDBusGenerateClassDefXml(&cdef);
output.write(std::move(xml).toLocal8Bit());
}
diff --git a/src/tools/qdbusxml2cpp/CMakeLists.txt b/src/tools/qdbusxml2cpp/CMakeLists.txt
index 37327b34db..0da22ebfc6 100644
--- a/src/tools/qdbusxml2cpp/CMakeLists.txt
+++ b/src/tools/qdbusxml2cpp/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from qdbusxml2cpp.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qdbusxml2cpp Tool:
@@ -6,32 +7,16 @@
qt_get_tool_target_name(target_name qdbusxml2cpp)
qt_internal_add_tool(${target_name}
+ TRY_RUN
TARGET_DESCRIPTION "Qt D-Bus XML to C++ Compiler"
- TOOLS_TARGET DBus # special case
+ TOOLS_TARGET DBus
SOURCES
qdbusxml2cpp.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_FOREACH
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::DBusPrivate
- # COMPILE_OPTIONS # special case
- # "$$QT_HOST_CFLAGS_DBUS"
- # QMAKE_TARGET_DESCRIPTION = "Qt D-Bus XML to C++ Compiler"
- # _LOADED = "qt_tool"
- # _OPTION = "host_build"
)
-
-#### Keys ignored in scope 1:.:.:qdbusxml2cpp.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt D-Bus XML to C++ Compiler"
-# _OPTION = "host_build"
-
-## Scopes:
-#####################################################################
-
-# special case begin
-# qt_internal_extend_target(qdbusxml2cpp CONDITION NOT force_bootstrap [...])
-# qt_internal_extend_target(qdbusxml2cpp CONDITION NOT QT_FEATURE_commandlineparser AND NOT force_bootstrap [...])
-# qt_internal_extend_target(qdbusxml2cpp CONDITION force_bootstrap [...])
-# special case end
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
index bf079995d7..579604286c 100644
--- a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
+++ b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qbytearray.h>
#include <qcommandlineparser.h>
@@ -46,21 +21,75 @@
#define PROGRAMNAME "qdbusxml2cpp"
#define PROGRAMVERSION "0.8"
-#define PROGRAMCOPYRIGHT "Copyright (C) 2021 The Qt Company Ltd."
+#define PROGRAMCOPYRIGHT QT_COPYRIGHT
#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
-static QString globalClassName;
-static QString parentClassName;
-static QString proxyFile;
-static QString adaptorFile;
-static QString inputFile;
-static bool skipNamespaces;
-static bool verbose;
-static bool includeMocs;
-static QString commandLine;
-static QStringList includes;
-static QStringList wantedInterfaces;
+using namespace Qt::StringLiterals;
+
+class QDBusXmlToCpp final
+{
+public:
+ int run(const QCoreApplication &app);
+
+private:
+ class DiagnosticsReporter final : public QDBusIntrospection::DiagnosticsReporter
+ {
+ public:
+ void setFileName(const QString &fileName) { m_fileName = fileName; }
+ bool hadErrors() const { return m_hadErrors; }
+
+ void warning(const QDBusIntrospection::SourceLocation &location, const char *msg,
+ ...) override;
+ void error(const QDBusIntrospection::SourceLocation &location, const char *msg,
+ ...) override;
+ void note(const QDBusIntrospection::SourceLocation &location, const char *msg, ...)
+ Q_ATTRIBUTE_FORMAT_PRINTF(3, 4);
+
+ private:
+ QString m_fileName;
+ bool m_hadErrors = false;
+
+ void report(const QDBusIntrospection::SourceLocation &location, const char *msg, va_list ap,
+ const char *severity);
+ };
+
+ enum ClassType { Proxy, Adaptor };
+
+ void writeAdaptor(const QString &filename, const QDBusIntrospection::Interfaces &interfaces);
+ void writeProxy(const QString &filename, const QDBusIntrospection::Interfaces &interfaces);
+
+ QDBusIntrospection::Interfaces readInput();
+ void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces);
+ QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost);
+ QString classNameForInterface(const QString &interface, ClassType classType);
+ QByteArray qtTypeName(const QDBusIntrospection::SourceLocation &location,
+ const QString &signature,
+ const QDBusIntrospection::Annotations &annotations,
+ qsizetype paramId = -1, const char *direction = "Out");
+ void
+ writeArgList(QTextStream &ts, const QStringList &argNames,
+ const QDBusIntrospection::Annotations &annotations,
+ const QDBusIntrospection::Arguments &inputArgs,
+ const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments());
+ void writeSignalArgList(QTextStream &ts, const QStringList &argNames,
+ const QDBusIntrospection::Annotations &annotations,
+ const QDBusIntrospection::Arguments &outputArgs);
+ QString propertyGetter(const QDBusIntrospection::Property &property);
+ QString propertySetter(const QDBusIntrospection::Property &property);
+
+ QString globalClassName;
+ QString parentClassName;
+ QString inputFile;
+ bool skipNamespaces = false;
+ bool includeMocs = false;
+ QString commandLine;
+ QStringList includes;
+ QStringList globalIncludes;
+ QStringList wantedInterfaces;
+
+ DiagnosticsReporter reporter;
+};
static const char includeList[] =
"#include <QtCore/QByteArray>\n"
@@ -73,30 +102,71 @@ static const char includeList[] =
static const char forwardDeclarations[] =
"#include <QtCore/qcontainerfwd.h>\n";
-static QDBusIntrospection::Interfaces readInput()
+void QDBusXmlToCpp::DiagnosticsReporter::warning(const QDBusIntrospection::SourceLocation &location,
+ const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ report(location, msg, ap, "warning");
+ va_end(ap);
+}
+
+void QDBusXmlToCpp::DiagnosticsReporter::error(const QDBusIntrospection::SourceLocation &location,
+ const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ report(location, msg, ap, "error");
+ va_end(ap);
+ m_hadErrors = true;
+}
+
+void QDBusXmlToCpp::DiagnosticsReporter::note(const QDBusIntrospection::SourceLocation &location,
+ const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ report(location, msg, ap, "note");
+ va_end(ap);
+ m_hadErrors = true;
+}
+
+void QDBusXmlToCpp::DiagnosticsReporter::report(const QDBusIntrospection::SourceLocation &location,
+ const char *msg, va_list ap, const char *severity)
+{
+ fprintf(stderr, "%s:%lld:%lld: %s: ", qPrintable(m_fileName),
+ (long long int)location.lineNumber, (long long int)location.columnNumber + 1, severity);
+ vfprintf(stderr, msg, ap);
+}
+
+QDBusIntrospection::Interfaces QDBusXmlToCpp::readInput()
{
QFile input(inputFile);
- if (inputFile.isEmpty() || inputFile == QLatin1String("-")) {
- input.open(stdin, QIODevice::ReadOnly);
+ if (inputFile.isEmpty() || inputFile == "-"_L1) {
+ reporter.setFileName("<standard input>"_L1);
+ if (!input.open(stdin, QIODevice::ReadOnly)) {
+ fprintf(stderr, PROGRAMNAME ": could not open standard input: %s\n",
+ qPrintable(input.errorString()));
+ exit(1);
+ }
} else {
- input.open(QIODevice::ReadOnly);
+ reporter.setFileName(inputFile);
+ if (!input.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, PROGRAMNAME ": could not open input file '%s': %s\n",
+ qPrintable(inputFile), qPrintable(input.errorString()));
+ exit(1);
+ }
}
QByteArray data = input.readAll();
+ auto interfaces = QDBusIntrospection::parseInterfaces(QString::fromUtf8(data), &reporter);
+ if (reporter.hadErrors())
+ exit(1);
- // check if the input is already XML
- data = data.trimmed();
- if (data.startsWith("<!DOCTYPE ") || data.startsWith("<?xml") ||
- data.startsWith("<node") || data.startsWith("<interface"))
- // already XML
- return QDBusIntrospection::parseInterfaces(QString::fromUtf8(data));
-
- fprintf(stderr, "%s: Cannot process input: '%s'. Stop.\n",
- PROGRAMNAME, qPrintable(inputFile));
- exit(1);
+ return interfaces;
}
-static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
+void QDBusXmlToCpp::cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
{
if (!wantedInterfaces.isEmpty()) {
QDBusIntrospection::Interfaces::Iterator it = interfaces.begin();
@@ -108,18 +178,33 @@ static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
}
}
+static bool isSupportedSuffix(QStringView suffix)
+{
+ const QLatin1StringView candidates[] = {
+ "h"_L1,
+ "cpp"_L1,
+ "cc"_L1
+ };
+
+ for (auto candidate : candidates)
+ if (suffix == candidate)
+ return true;
+
+ return false;
+}
+
// produce a header name from the file name
static QString header(const QString &name)
{
- QStringList parts = name.split(QLatin1Char(':'));
- QString retval = parts.first();
+ QStringList parts = name.split(u':');
+ QString retval = parts.front();
- if (retval.isEmpty() || retval == QLatin1String("-"))
+ if (retval.isEmpty() || retval == "-"_L1)
return retval;
- if (!retval.endsWith(QLatin1String(".h")) && !retval.endsWith(QLatin1String(".cpp")) &&
- !retval.endsWith(QLatin1String(".cc")))
- retval.append(QLatin1String(".h"));
+ QFileInfo header{retval};
+ if (!isSupportedSuffix(header.suffix()))
+ retval.append(".h"_L1);
return retval;
}
@@ -127,15 +212,15 @@ static QString header(const QString &name)
// produce a cpp name from the file name
static QString cpp(const QString &name)
{
- QStringList parts = name.split(QLatin1Char(':'));
- QString retval = parts.last();
+ QStringList parts = name.split(u':');
+ QString retval = parts.back();
- if (retval.isEmpty() || retval == QLatin1String("-"))
+ if (retval.isEmpty() || retval == "-"_L1)
return retval;
- if (!retval.endsWith(QLatin1String(".h")) && !retval.endsWith(QLatin1String(".cpp")) &&
- !retval.endsWith(QLatin1String(".cc")))
- retval.append(QLatin1String(".cpp"));
+ QFileInfo source{retval};
+ if (!isSupportedSuffix(source.suffix()))
+ retval.append(".cpp"_L1);
return retval;
}
@@ -143,44 +228,78 @@ static QString cpp(const QString &name)
// produce a moc name from the file name
static QString moc(const QString &name)
{
- QString retval = header(name);
- if (retval.isEmpty())
- return retval;
+ QString retval;
+ const QStringList fileNames = name.split(u':');
+
+ if (fileNames.size() == 1) {
+ QFileInfo fi{fileNames.front()};
+ if (isSupportedSuffix(fi.suffix())) {
+ // Generates a file that contains the header and the implementation: include "filename.moc"
+ retval += fi.completeBaseName();
+ retval += ".moc"_L1;
+ } else {
+ // Separate source and header files are generated: include "moc_filename.cpp"
+ retval += "moc_"_L1;
+ retval += fi.fileName();
+ retval += ".cpp"_L1;
+ }
+ } else {
+ QString headerName = fileNames.front();
+ QString sourceName = fileNames.back();
+
+ if (sourceName.isEmpty() || sourceName == "-"_L1) {
+ // If only a header is generated, don't include anything
+ } else if (headerName.isEmpty() || headerName == "-"_L1) {
+ // If only source file is generated: include "moc_sourcename.cpp"
+ QFileInfo source{sourceName};
+
+ retval += "moc_"_L1;
+ retval += source.completeBaseName();
+ retval += ".cpp"_L1;
+
+ fprintf(stderr, "warning: no header name is provided, assuming it to be \"%s\"\n",
+ qPrintable(source.completeBaseName() + ".h"_L1));
+ } else {
+ // Both source and header generated: include "moc_headername.cpp"
+ QFileInfo header{headerName};
+
+ retval += "moc_"_L1;
+ retval += header.completeBaseName();
+ retval += ".cpp"_L1;
+ }
+ }
- retval.truncate(retval.length() - 1); // drop the h in .h
- retval += QLatin1String("moc");
return retval;
}
-static QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost)
+QTextStream &QDBusXmlToCpp::writeHeader(QTextStream &ts, bool changesWillBeLost)
{
- ts << "/*" << Qt::endl
- << " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION << Qt::endl
- << " * Command line was: " << commandLine << Qt::endl
- << " *" << Qt::endl
- << " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT << Qt::endl
- << " *" << Qt::endl
- << " * This is an auto-generated file." << Qt::endl;
+ ts << "/*\n"
+ " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION "\n"
+ " * Command line was: " << commandLine << "\n"
+ " *\n"
+ " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT "\n"
+ " *\n"
+ " * This is an auto-generated file.\n";
if (changesWillBeLost)
- ts << " * Do not edit! All changes made to it will be lost." << Qt::endl;
+ ts << " * Do not edit! All changes made to it will be lost.\n";
else
- ts << " * This file may have been hand-edited. Look for HAND-EDIT comments" << Qt::endl
- << " * before re-generating it." << Qt::endl;
+ ts << " * This file may have been hand-edited. Look for HAND-EDIT comments\n"
+ " * before re-generating it.\n";
- ts << " */" << Qt::endl
- << Qt::endl;
+ ts << " */\n\n";
return ts;
}
-enum ClassType { Proxy, Adaptor };
-static QString classNameForInterface(const QString &interface, ClassType classType)
+QString QDBusXmlToCpp::classNameForInterface(const QString &interface,
+ QDBusXmlToCpp::ClassType classType)
{
if (!globalClassName.isEmpty())
return globalClassName;
- const auto parts = QStringView{interface}.split(QLatin1Char('.'));
+ const auto parts = QStringView{interface}.split(u'.');
QString retval;
if (classType == Proxy) {
@@ -193,50 +312,46 @@ static QString classNameForInterface(const QString &interface, ClassType classTy
}
if (classType == Proxy)
- retval += QLatin1String("Interface");
+ retval += "Interface"_L1;
else
- retval += QLatin1String("Adaptor");
+ retval += "Adaptor"_L1;
return retval;
}
-// ### Qt6 Remove the two isSignal ifs
-// They are only here because before signal arguments where previously searched as "In" so to maintain compatibility
-// we first search for "Out" and if not found we search for "In"
-static QByteArray qtTypeName(const QString &signature, const QDBusIntrospection::Annotations &annotations, int paramId = -1, const char *direction = "Out", bool isSignal = false)
+QByteArray QDBusXmlToCpp::qtTypeName(const QDBusIntrospection::SourceLocation &location,
+ const QString &signature,
+ const QDBusIntrospection::Annotations &annotations,
+ qsizetype paramId, const char *direction)
{
int type = QDBusMetaType::signatureToMetaType(signature.toLatin1()).id();
if (type == QMetaType::UnknownType) {
- QString annotationName = QString::fromLatin1("org.qtproject.QtDBus.QtTypeName");
+ QString annotationName = u"org.qtproject.QtDBus.QtTypeName"_s;
if (paramId >= 0)
- annotationName += QString::fromLatin1(".%1%2").arg(QLatin1String(direction)).arg(paramId);
- QString qttype = annotations.value(annotationName);
+ annotationName += ".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);
+ auto annotation = annotations.value(annotationName);
+ QString qttype = annotation.value;
if (!qttype.isEmpty())
return std::move(qttype).toLatin1();
- QString oldAnnotationName = QString::fromLatin1("com.trolltech.QtDBus.QtTypeName");
+ QString oldAnnotationName = u"com.trolltech.QtDBus.QtTypeName"_s;
if (paramId >= 0)
- oldAnnotationName += QString::fromLatin1(".%1%2").arg(QLatin1String(direction)).arg(paramId);
- qttype = annotations.value(oldAnnotationName);
+ oldAnnotationName += ".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);
+ annotation = annotations.value(oldAnnotationName);
+ qttype = annotation.value;
if (qttype.isEmpty()) {
- if (!isSignal || qstrcmp(direction, "Out") == 0) {
- fprintf(stderr, "%s: Got unknown type `%s' processing '%s'\n",
- PROGRAMNAME, qPrintable(signature), qPrintable(inputFile));
- fprintf(stderr, "You should add <annotation name=\"%s\" value=\"<type>\"/> to the XML description\n",
- qPrintable(annotationName));
- }
-
- if (isSignal)
- return qtTypeName(signature, annotations, paramId, "In", isSignal);
+ reporter.error(location, "unknown type `%s'\n", qPrintable(signature));
+ reporter.note(location, "you should add <annotation name=\"%s\" value=\"<type>\"/>\n",
+ qPrintable(annotationName));
exit(1);
}
- fprintf(stderr, "%s: Warning: deprecated annotation '%s' found while processing '%s'; "
- "suggest updating to '%s'\n",
- PROGRAMNAME, qPrintable(oldAnnotationName), qPrintable(inputFile),
- qPrintable(annotationName));
+ reporter.warning(annotation.location, "deprecated annotation '%s' found\n",
+ qPrintable(oldAnnotationName));
+ reporter.note(annotation.location, "suggest updating to '%s'\n",
+ qPrintable(annotationName));
return std::move(qttype).toLatin1();
}
@@ -245,23 +360,23 @@ static QByteArray qtTypeName(const QString &signature, const QDBusIntrospection:
static QString nonConstRefArg(const QByteArray &arg)
{
- return QLatin1String(arg + " &");
+ return QLatin1StringView(arg) + " &"_L1;
}
static QString templateArg(const QByteArray &arg)
{
if (!arg.endsWith('>'))
- return QLatin1String(arg);
+ return QLatin1StringView(arg);
- return QLatin1String(arg + ' ');
+ return QLatin1StringView(arg) + " "_L1;
}
static QString constRefArg(const QByteArray &arg)
{
if (!arg.startsWith('Q'))
- return QLatin1String(arg + ' ');
+ return QLatin1StringView(arg) + " "_L1;
else
- return QString( QLatin1String("const %1 &") ).arg( QLatin1String(arg) );
+ return "const %1 &"_L1.arg(QLatin1StringView(arg));
}
static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs,
@@ -269,45 +384,45 @@ static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs,
QDBusIntrospection::Arguments())
{
QStringList retval;
- const int numInputArgs = inputArgs.count();
- const int numOutputArgs = outputArgs.count();
+ const qsizetype numInputArgs = inputArgs.size();
+ const qsizetype numOutputArgs = outputArgs.size();
retval.reserve(numInputArgs + numOutputArgs);
- for (int i = 0; i < numInputArgs; ++i) {
+ for (qsizetype i = 0; i < numInputArgs; ++i) {
const QDBusIntrospection::Argument &arg = inputArgs.at(i);
QString name = arg.name;
if (name.isEmpty())
- name = QString( QLatin1String("in%1") ).arg(i);
+ name = u"in%1"_s.arg(i);
else
- name.replace(QLatin1Char('-'), QLatin1Char('_'));
+ name.replace(u'-', u'_');
while (retval.contains(name))
- name += QLatin1String("_");
+ name += "_"_L1;
retval << name;
}
- for (int i = 0; i < numOutputArgs; ++i) {
+ for (qsizetype i = 0; i < numOutputArgs; ++i) {
const QDBusIntrospection::Argument &arg = outputArgs.at(i);
QString name = arg.name;
if (name.isEmpty())
- name = QString( QLatin1String("out%1") ).arg(i);
+ name = u"out%1"_s.arg(i);
else
- name.replace(QLatin1Char('-'), QLatin1Char('_'));
+ name.replace(u'-', u'_');
while (retval.contains(name))
- name += QLatin1String("_");
+ name += "_"_L1;
retval << name;
}
return retval;
}
-static void writeArgList(QTextStream &ts, const QStringList &argNames,
- const QDBusIntrospection::Annotations &annotations,
- const QDBusIntrospection::Arguments &inputArgs,
- const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments())
+void QDBusXmlToCpp::writeArgList(QTextStream &ts, const QStringList &argNames,
+ const QDBusIntrospection::Annotations &annotations,
+ const QDBusIntrospection::Arguments &inputArgs,
+ const QDBusIntrospection::Arguments &outputArgs)
{
// input args:
bool first = true;
- int argPos = 0;
- for (int i = 0; i < inputArgs.count(); ++i) {
+ qsizetype argPos = 0;
+ for (qsizetype i = 0; i < inputArgs.size(); ++i) {
const QDBusIntrospection::Argument &arg = inputArgs.at(i);
- QString type = constRefArg(qtTypeName(arg.type, annotations, i, "In"));
+ QString type = constRefArg(qtTypeName(arg.location, arg.type, annotations, i, "In"));
if (!first)
ts << ", ";
@@ -319,26 +434,26 @@ static void writeArgList(QTextStream &ts, const QStringList &argNames,
// output args
// yes, starting from 1
- for (int i = 1; i < outputArgs.count(); ++i) {
+ for (qsizetype i = 1; i < outputArgs.size(); ++i) {
const QDBusIntrospection::Argument &arg = outputArgs.at(i);
if (!first)
ts << ", ";
- ts << nonConstRefArg(qtTypeName(arg.type, annotations, i, "Out"))
+ ts << nonConstRefArg(qtTypeName(arg.location, arg.type, annotations, i, "Out"))
<< argNames.at(argPos++);
first = false;
}
}
-static void writeSignalArgList(QTextStream &ts, const QStringList &argNames,
- const QDBusIntrospection::Annotations &annotations,
- const QDBusIntrospection::Arguments &outputArgs)
+void QDBusXmlToCpp::writeSignalArgList(QTextStream &ts, const QStringList &argNames,
+ const QDBusIntrospection::Annotations &annotations,
+ const QDBusIntrospection::Arguments &outputArgs)
{
bool first = true;
- int argPos = 0;
- for (int i = 0; i < outputArgs.count(); ++i) {
+ qsizetype argPos = 0;
+ for (qsizetype i = 0; i < outputArgs.size(); ++i) {
const QDBusIntrospection::Argument &arg = outputArgs.at(i);
- QString type = constRefArg(qtTypeName(arg.type, annotations, i, "Out", true /* isSignal */));
+ QString type = constRefArg(qtTypeName(arg.location, arg.type, annotations, i, "Out"));
if (!first)
ts << ", ";
@@ -347,49 +462,49 @@ static void writeSignalArgList(QTextStream &ts, const QStringList &argNames,
}
}
-static QString propertyGetter(const QDBusIntrospection::Property &property)
+QString QDBusXmlToCpp::propertyGetter(const QDBusIntrospection::Property &property)
{
- QString getter = property.annotations.value(QLatin1String("org.qtproject.QtDBus.PropertyGetter"));
- if (!getter.isEmpty())
- return getter;
-
- getter = property.annotations.value(QLatin1String("com.trolltech.QtDBus.propertyGetter"));
- if (!getter.isEmpty()) {
- fprintf(stderr, "%s: Warning: deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found"
- " while processing '%s';"
- " suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n",
- PROGRAMNAME, qPrintable(inputFile));
- return getter;
+ auto annotation = property.annotations.value("org.qtproject.QtDBus.PropertyGetter"_L1);
+ if (!annotation.value.isEmpty())
+ return annotation.value;
+
+ annotation = property.annotations.value("com.trolltech.QtDBus.propertyGetter"_L1);
+ if (!annotation.value.isEmpty()) {
+ reporter.warning(annotation.location,
+ "deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found\n");
+ reporter.note(annotation.location,
+ "suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n");
+ return annotation.value;
}
- getter = property.name;
+ QString getter = property.name;
getter[0] = getter[0].toLower();
return getter;
}
-static QString propertySetter(const QDBusIntrospection::Property &property)
+QString QDBusXmlToCpp::propertySetter(const QDBusIntrospection::Property &property)
{
- QString setter = property.annotations.value(QLatin1String("org.qtproject.QtDBus.PropertySetter"));
- if (!setter.isEmpty())
- return setter;
-
- setter = property.annotations.value(QLatin1String("com.trolltech.QtDBus.propertySetter"));
- if (!setter.isEmpty()) {
- fprintf(stderr, "%s: Warning: deprecated annotation 'com.trolltech.QtDBus.propertySetter' found"
- " while processing '%s';"
- " suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n",
- PROGRAMNAME, qPrintable(inputFile));
- return setter;
+ auto annotation = property.annotations.value("org.qtproject.QtDBus.PropertySetter"_L1);
+ if (!annotation.value.isEmpty())
+ return annotation.value;
+
+ annotation = property.annotations.value("com.trolltech.QtDBus.propertySetter"_L1);
+ if (!annotation.value.isEmpty()) {
+ reporter.warning(annotation.location,
+ "deprecated annotation 'com.trolltech.QtDBus.propertySetter' found\n");
+ reporter.note(annotation.location,
+ "suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n");
+ return annotation.value;
}
- setter = QLatin1String("set") + property.name;
+ QString setter = "set"_L1 + property.name;
setter[3] = setter[3].toUpper();
return setter;
}
static QString methodName(const QDBusIntrospection::Method &method)
{
- QString name = method.annotations.value(QStringLiteral("org.qtproject.QtDBus.MethodName"));
+ QString name = method.annotations.value(u"org.qtproject.QtDBus.MethodName"_s).value;
if (!name.isEmpty())
return name;
@@ -399,17 +514,17 @@ static QString methodName(const QDBusIntrospection::Method &method)
static QString stringify(const QString &data)
{
QString retval;
- int i;
- for (i = 0; i < data.length(); ++i) {
- retval += QLatin1Char('\"');
- for ( ; i < data.length() && data[i] != QLatin1Char('\n') && data[i] != QLatin1Char('\r'); ++i)
- if (data[i] == QLatin1Char('\"'))
- retval += QLatin1String("\\\"");
+ qsizetype i;
+ for (i = 0; i < data.size(); ++i) {
+ retval += u'\"';
+ for ( ; i < data.size() && data[i] != u'\n' && data[i] != u'\r'; ++i)
+ if (data[i] == u'\"')
+ retval += "\\\""_L1;
else
retval += data[i];
- if (i+1 < data.length() && data[i] == QLatin1Char('\r') && data[i+1] == QLatin1Char('\n'))
+ if (i+1 < data.size() && data[i] == u'\r' && data[i+1] == u'\n')
i++;
- retval += QLatin1String("\\n\"\n");
+ retval += "\\n\"\n"_L1;
}
return retval;
}
@@ -420,7 +535,7 @@ static bool openFile(const QString &fileName, QFile &file)
return false;
bool isOk = false;
- if (fileName == QLatin1String("-")) {
+ if (fileName == "-"_L1) {
isOk = file.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
} else {
file.setFileName(fileName);
@@ -433,7 +548,8 @@ static bool openFile(const QString &fileName, QFile &file)
return isOk;
}
-static void writeProxy(const QString &filename, const QDBusIntrospection::Interfaces &interfaces)
+void QDBusXmlToCpp::writeProxy(const QString &filename,
+ const QDBusIntrospection::Interfaces &interfaces)
{
// open the file
QString headerName = header(filename);
@@ -451,85 +567,83 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
// include guards:
QString includeGuard;
- if (!headerName.isEmpty() && headerName != QLatin1String("-")) {
- includeGuard = headerName.toUpper().replace(QLatin1Char('.'), QLatin1Char('_'));
- int pos = includeGuard.lastIndexOf(QLatin1Char('/'));
+ if (!headerName.isEmpty() && headerName != "-"_L1) {
+ includeGuard = headerName.toUpper().replace(u'.', u'_');
+ qsizetype pos = includeGuard.lastIndexOf(u'/');
if (pos != -1)
includeGuard = includeGuard.mid(pos + 1);
} else {
- includeGuard = QLatin1String("QDBUSXML2CPP_PROXY");
+ includeGuard = u"QDBUSXML2CPP_PROXY"_s;
}
- includeGuard = QString(QLatin1String("%1"))
- .arg(includeGuard);
- hs << "#ifndef " << includeGuard << Qt::endl
- << "#define " << includeGuard << Qt::endl
- << Qt::endl;
+
+ hs << "#ifndef " << includeGuard << "\n"
+ "#define " << includeGuard << "\n\n";
// include our stuff:
- hs << "#include <QtCore/QObject>" << Qt::endl
+ hs << "#include <QtCore/QObject>\n"
<< includeList;
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
- hs << "#include <QtDBus/QtDBus>" << Qt::endl;
+ hs << "#include <QtDBus/QtDBus>\n";
#else
- hs << "#include <QtDBus/QDBusAbstractInterface>" << Qt::endl;
- hs << "#include <QtDBus/QDBusPendingReply>" << Qt::endl;
+ hs << "#include <QtDBus/QDBusAbstractInterface>\n"
+ "#include <QtDBus/QDBusPendingReply>\n";
#endif
- for (const QString &include : qAsConst(includes)) {
- hs << "#include \"" << include << "\"" << Qt::endl;
+ for (const QString &include : std::as_const(includes)) {
+ hs << "#include \"" << include << "\"\n";
+ if (headerName.isEmpty())
+ cs << "#include \"" << include << "\"\n";
+ }
+
+ for (const QString &include : std::as_const(globalIncludes)) {
+ hs << "#include <" << include << ">\n";
if (headerName.isEmpty())
- cs << "#include \"" << include << "\"" << Qt::endl;
+ cs << "#include <" << include << ">\n";
}
- hs << Qt::endl;
+ hs << "\n";
if (cppName != headerName) {
- if (!headerName.isEmpty() && headerName != QLatin1String("-"))
- cs << "#include \"" << headerName << "\"" << Qt::endl << Qt::endl;
+ if (!headerName.isEmpty() && headerName != "-"_L1)
+ cs << "#include \"" << headerName << "\"\n\n";
}
for (const QDBusIntrospection::Interface *interface : interfaces) {
QString className = classNameForInterface(interface->name, Proxy);
// comment:
- hs << "/*" << Qt::endl
- << " * Proxy class for interface " << interface->name << Qt::endl
- << " */" << Qt::endl;
- cs << "/*" << Qt::endl
- << " * Implementation of interface class " << className << Qt::endl
- << " */" << Qt::endl
- << Qt::endl;
+ hs << "/*\n"
+ " * Proxy class for interface " << interface->name << "\n"
+ " */\n";
+ cs << "/*\n"
+ " * Implementation of interface class " << className << "\n"
+ " */\n\n";
// class header:
- hs << "class " << className << ": public QDBusAbstractInterface" << Qt::endl
- << "{" << Qt::endl
- << " Q_OBJECT" << Qt::endl;
+ hs << "class " << className << ": public QDBusAbstractInterface\n"
+ "{\n"
+ " Q_OBJECT\n";
// the interface name
- hs << "public:" << Qt::endl
- << " static inline const char *staticInterfaceName()" << Qt::endl
- << " { return \"" << interface->name << "\"; }" << Qt::endl
- << Qt::endl;
+ hs << "public:\n"
+ " static inline const char *staticInterfaceName()\n"
+ " { return \"" << interface->name << "\"; }\n\n";
// constructors/destructors:
- hs << "public:" << Qt::endl
- << " " << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);" << Qt::endl
- << Qt::endl
- << " ~" << className << "();" << Qt::endl
- << Qt::endl;
- cs << className << "::" << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)" << Qt::endl
- << " : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)" << Qt::endl
- << "{" << Qt::endl
- << "}" << Qt::endl
- << Qt::endl
- << className << "::~" << className << "()" << Qt::endl
- << "{" << Qt::endl
- << "}" << Qt::endl
- << Qt::endl;
+ hs << "public:\n"
+ " " << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);\n\n"
+ " ~" << className << "();\n\n";
+ cs << className << "::" << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)\n"
+ " : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)\n"
+ "{\n"
+ "}\n\n"
+ << className << "::~" << className << "()\n"
+ "{\n"
+ "}\n\n";
// properties:
for (const QDBusIntrospection::Property &property : interface->properties) {
- QByteArray type = qtTypeName(property.type, property.annotations);
+ QByteArray type = qtTypeName(property.location, property.type, property.annotations);
QString getter = propertyGetter(property);
QString setter = propertySetter(property);
@@ -537,7 +651,7 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
// getter:
if (property.access != QDBusIntrospection::Property::Write)
- // it's readble
+ // it's readable
hs << " READ " << getter;
// setter
@@ -545,48 +659,53 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
// it's writeable
hs << " WRITE " << setter;
- hs << ")" << Qt::endl;
+ hs << ")\n";
// getter:
if (property.access != QDBusIntrospection::Property::Write) {
- hs << " inline " << type << " " << getter << "() const" << Qt::endl
- << " { return qvariant_cast< " << type << " >(property(\""
- << property.name << "\")); }" << Qt::endl;
+ hs << " inline " << type << " " << getter << "() const\n"
+ " { return qvariant_cast< " << type << " >(property(\""
+ << property.name << "\")); }\n";
}
// setter:
if (property.access != QDBusIntrospection::Property::Read) {
- hs << " inline void " << setter << "(" << constRefArg(type) << "value)" << Qt::endl
- << " { setProperty(\"" << property.name
- << "\", QVariant::fromValue(value)); }" << Qt::endl;
+ hs << " inline void " << setter << "(" << constRefArg(type) << "value)\n"
+ " { setProperty(\"" << property.name
+ << "\", QVariant::fromValue(value)); }\n";
}
- hs << Qt::endl;
+ hs << "\n";
}
// methods:
- hs << "public Q_SLOTS: // METHODS" << Qt::endl;
+ hs << "public Q_SLOTS: // METHODS\n";
for (const QDBusIntrospection::Method &method : interface->methods) {
- bool isDeprecated = method.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) == QLatin1String("true");
- bool isNoReply =
- method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true");
+ bool isDeprecated = method.annotations.value("org.freedesktop.DBus.Deprecated"_L1).value
+ == "true"_L1;
+ bool isNoReply = method.annotations.value(ANNOTATION_NO_WAIT ""_L1).value == "true"_L1;
if (isNoReply && !method.outputArgs.isEmpty()) {
- fprintf(stderr, "%s: warning while processing '%s': method %s in interface %s is marked 'no-reply' but has output arguments.\n",
- PROGRAMNAME, qPrintable(inputFile), qPrintable(method.name),
- qPrintable(interface->name));
+ reporter.warning(method.location,
+ "method %s in interface %s is marked 'no-reply' but has output "
+ "arguments.\n",
+ qPrintable(method.name), qPrintable(interface->name));
continue;
}
- hs << " inline "
- << (isDeprecated ? "Q_DECL_DEPRECATED " : "");
+ if (isDeprecated)
+ hs << " Q_DECL_DEPRECATED ";
+ else
+ hs << " ";
if (isNoReply) {
- hs << "Q_NOREPLY void ";
+ hs << "Q_NOREPLY inline void ";
} else {
- hs << "QDBusPendingReply<";
- for (int i = 0; i < method.outputArgs.count(); ++i)
+ hs << "inline QDBusPendingReply<";
+ for (qsizetype i = 0; i < method.outputArgs.size(); ++i)
hs << (i > 0 ? ", " : "")
- << templateArg(qtTypeName(method.outputArgs.at(i).type, method.annotations, i, "Out"));
+ << templateArg(qtTypeName(method.outputArgs.at(i).location,
+ method.outputArgs.at(i).type, method.annotations,
+ i, "Out"));
hs << "> ";
}
@@ -595,75 +714,77 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
QStringList argNames = makeArgNames(method.inputArgs);
writeArgList(hs, argNames, method.annotations, method.inputArgs);
- hs << ")" << Qt::endl
- << " {" << Qt::endl
- << " QList<QVariant> argumentList;" << Qt::endl;
+ hs << ")\n"
+ " {\n"
+ " QList<QVariant> argumentList;\n";
if (!method.inputArgs.isEmpty()) {
hs << " argumentList";
- for (int argPos = 0; argPos < method.inputArgs.count(); ++argPos)
+ for (qsizetype argPos = 0; argPos < method.inputArgs.size(); ++argPos)
hs << " << QVariant::fromValue(" << argNames.at(argPos) << ')';
- hs << ";" << Qt::endl;
+ hs << ";\n";
}
if (isNoReply)
hs << " callWithArgumentList(QDBus::NoBlock, "
- << "QStringLiteral(\"" << method.name << "\"), argumentList);" << Qt::endl;
+ "QStringLiteral(\"" << method.name << "\"), argumentList);\n";
else
hs << " return asyncCallWithArgumentList(QStringLiteral(\""
- << method.name << "\"), argumentList);" << Qt::endl;
+ << method.name << "\"), argumentList);\n";
// close the function:
- hs << " }" << Qt::endl;
+ hs << " }\n";
- if (method.outputArgs.count() > 1) {
+ if (method.outputArgs.size() > 1) {
// generate the old-form QDBusReply methods with multiple incoming parameters
- hs << " inline "
- << (isDeprecated ? "Q_DECL_DEPRECATED " : "")
- << "QDBusReply<"
- << templateArg(qtTypeName(method.outputArgs.first().type, method.annotations, 0, "Out")) << "> ";
+ hs << (isDeprecated ? " Q_DECL_DEPRECATED " : " ") << "inline QDBusReply<"
+ << templateArg(qtTypeName(method.outputArgs.first().location,
+ method.outputArgs.first().type, method.annotations, 0,
+ "Out"))
+ << "> ";
hs << method.name << "(";
QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
- hs << ")" << Qt::endl
- << " {" << Qt::endl
- << " QList<QVariant> argumentList;" << Qt::endl;
+ hs << ")\n"
+ " {\n"
+ " QList<QVariant> argumentList;\n";
- int argPos = 0;
+ qsizetype argPos = 0;
if (!method.inputArgs.isEmpty()) {
hs << " argumentList";
- for (argPos = 0; argPos < method.inputArgs.count(); ++argPos)
+ for (argPos = 0; argPos < method.inputArgs.size(); ++argPos)
hs << " << QVariant::fromValue(" << argNames.at(argPos) << ')';
- hs << ";" << Qt::endl;
+ hs << ";\n";
}
hs << " QDBusMessage reply = callWithArgumentList(QDBus::Block, "
- << "QStringLiteral(\"" << method.name << "\"), argumentList);" << Qt::endl;
+ "QStringLiteral(\"" << method.name << "\"), argumentList);\n";
argPos++;
- hs << " if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == "
- << method.outputArgs.count() << ") {" << Qt::endl;
+ hs << " if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().size() == "
+ << method.outputArgs.size() << ") {\n";
// yes, starting from 1
- for (int i = 1; i < method.outputArgs.count(); ++i)
+ for (qsizetype i = 1; i < method.outputArgs.size(); ++i)
hs << " " << argNames.at(argPos++) << " = qdbus_cast<"
- << templateArg(qtTypeName(method.outputArgs.at(i).type, method.annotations, i, "Out"))
- << ">(reply.arguments().at(" << i << "));" << Qt::endl;
- hs << " }" << Qt::endl
- << " return reply;" << Qt::endl
- << " }" << Qt::endl;
+ << templateArg(qtTypeName(method.outputArgs.at(i).location,
+ method.outputArgs.at(i).type, method.annotations,
+ i, "Out"))
+ << ">(reply.arguments().at(" << i << "));\n";
+ hs << " }\n"
+ " return reply;\n"
+ " }\n";
}
- hs << Qt::endl;
+ hs << "\n";
}
- hs << "Q_SIGNALS: // SIGNALS" << Qt::endl;
+ hs << "Q_SIGNALS: // SIGNALS\n";
for (const QDBusIntrospection::Signal &signal : interface->signals_) {
hs << " ";
- if (signal.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) ==
- QLatin1String("true"))
+ if (signal.annotations.value("org.freedesktop.DBus.Deprecated"_L1).value == "true"_L1)
hs << "Q_DECL_DEPRECATED ";
hs << "void " << signal.name << "(";
@@ -671,12 +792,11 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
QStringList argNames = makeArgNames(signal.outputArgs);
writeSignalArgList(hs, argNames, signal.annotations, signal.outputArgs);
- hs << ");" << Qt::endl; // finished for header
+ hs << ");\n"; // finished for header
}
// close the class:
- hs << "};" << Qt::endl
- << Qt::endl;
+ hs << "};\n\n";
}
if (!skipNamespaces) {
@@ -687,28 +807,28 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
QStringList current;
QString name;
if (it != interfaces.constEnd()) {
- current = it->constData()->name.split(QLatin1Char('.'));
+ current = it->constData()->name.split(u'.');
name = current.takeLast();
}
- int i = 0;
- while (i < current.count() && i < last.count() && current.at(i) == last.at(i))
+ qsizetype i = 0;
+ while (i < current.size() && i < last.size() && current.at(i) == last.at(i))
++i;
// i parts matched
- // close last.arguments().count() - i namespaces:
- for (int j = i; j < last.count(); ++j)
- hs << QString((last.count() - j - 1 + i) * 2, QLatin1Char(' ')) << "}" << Qt::endl;
+ // close last.arguments().size() - i namespaces:
+ for (qsizetype j = i; j < last.size(); ++j)
+ hs << QString((last.size() - j - 1 + i) * 2, u' ') << "}\n";
- // open current.arguments().count() - i namespaces
- for (int j = i; j < current.count(); ++j)
- hs << QString(j * 2, QLatin1Char(' ')) << "namespace " << current.at(j) << " {" << Qt::endl;
+ // open current.arguments().size() - i namespaces
+ for (qsizetype j = i; j < current.size(); ++j)
+ hs << QString(j * 2, u' ') << "namespace " << current.at(j) << " {\n";
// add this class:
if (!name.isEmpty()) {
- hs << QString(current.count() * 2, QLatin1Char(' '))
+ hs << QString(current.size() * 2, u' ')
<< "using " << name << " = ::" << classNameForInterface(it->constData()->name, Proxy)
- << ";" << Qt::endl;
+ << ";\n";
}
if (it == interfaces.constEnd())
@@ -719,12 +839,12 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
}
// close the include guard
- hs << "#endif" << Qt::endl;
+ hs << "#endif\n";
QString mocName = moc(filename);
if (includeMocs && !mocName.isEmpty())
- cs << Qt::endl
- << "#include \"" << mocName << "\"" << Qt::endl;
+ cs << "\n"
+ "#include \"" << mocName << "\"\n";
cs.flush();
hs.flush();
@@ -744,7 +864,8 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
}
}
-static void writeAdaptor(const QString &filename, const QDBusIntrospection::Interfaces &interfaces)
+void QDBusXmlToCpp::writeAdaptor(const QString &filename,
+ const QDBusIntrospection::Interfaces &interfaces)
{
// open the file
QString headerName = header(filename);
@@ -762,103 +883,102 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
// include guards:
QString includeGuard;
- if (!headerName.isEmpty() && headerName != QLatin1String("-")) {
- includeGuard = headerName.toUpper().replace(QLatin1Char('.'), QLatin1Char('_'));
- int pos = includeGuard.lastIndexOf(QLatin1Char('/'));
+ if (!headerName.isEmpty() && headerName != "-"_L1) {
+ includeGuard = headerName.toUpper().replace(u'.', u'_');
+ qsizetype pos = includeGuard.lastIndexOf(u'/');
if (pos != -1)
includeGuard = includeGuard.mid(pos + 1);
} else {
- includeGuard = QLatin1String("QDBUSXML2CPP_ADAPTOR");
+ includeGuard = u"QDBUSXML2CPP_ADAPTOR"_s;
}
- includeGuard = QString(QLatin1String("%1"))
- .arg(includeGuard);
- hs << "#ifndef " << includeGuard << Qt::endl
- << "#define " << includeGuard << Qt::endl
- << Qt::endl;
+
+ hs << "#ifndef " << includeGuard << "\n"
+ "#define " << includeGuard << "\n\n";
// include our stuff:
- hs << "#include <QtCore/QObject>" << Qt::endl;
+ hs << "#include <QtCore/QObject>\n";
if (cppName == headerName)
- hs << "#include <QtCore/QMetaObject>" << Qt::endl
- << "#include <QtCore/QVariant>" << Qt::endl;
+ hs << "#include <QtCore/QMetaObject>\n"
+ "#include <QtCore/QVariant>\n";
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
- hs << "#include <QtDBus/QtDBus>" << Qt::endl;
+ hs << "#include <QtDBus/QtDBus>\n";
#else
- hs << "#include <QtDBus/QDBusAbstractAdaptor>" << Qt::endl;
- hs << "#include <QtDBus/QDBusObjectPath>" << Qt::endl;
+ hs << "#include <QtDBus/QDBusAbstractAdaptor>\n"
+ "#include <QtDBus/QDBusObjectPath>\n";
#endif
- for (const QString &include : qAsConst(includes)) {
- hs << "#include \"" << include << "\"" << Qt::endl;
+ for (const QString &include : std::as_const(includes)) {
+ hs << "#include \"" << include << "\"\n";
+ if (headerName.isEmpty())
+ cs << "#include \"" << include << "\"\n";
+ }
+
+ for (const QString &include : std::as_const(globalIncludes)) {
+ hs << "#include <" << include << ">\n";
if (headerName.isEmpty())
- cs << "#include \"" << include << "\"" << Qt::endl;
+ cs << "#include <" << include << ">\n";
}
if (cppName != headerName) {
- if (!headerName.isEmpty() && headerName != QLatin1String("-"))
- cs << "#include \"" << headerName << "\"" << Qt::endl;
+ if (!headerName.isEmpty() && headerName != "-"_L1)
+ cs << "#include \"" << headerName << "\"\n";
- cs << "#include <QtCore/QMetaObject>" << Qt::endl
+ cs << "#include <QtCore/QMetaObject>\n"
<< includeList
- << Qt::endl;
+ << "\n";
hs << forwardDeclarations;
} else {
hs << includeList;
}
- hs << Qt::endl;
+ hs << "\n";
QString parent = parentClassName;
if (parentClassName.isEmpty())
- parent = QLatin1String("QObject");
+ parent = u"QObject"_s;
for (const QDBusIntrospection::Interface *interface : interfaces) {
QString className = classNameForInterface(interface->name, Adaptor);
// comment:
- hs << "/*" << Qt::endl
- << " * Adaptor class for interface " << interface->name << Qt::endl
- << " */" << Qt::endl;
- cs << "/*" << Qt::endl
- << " * Implementation of adaptor class " << className << Qt::endl
- << " */" << Qt::endl
- << Qt::endl;
+ hs << "/*\n"
+ " * Adaptor class for interface " << interface->name << "\n"
+ " */\n";
+ cs << "/*\n"
+ " * Implementation of adaptor class " << className << "\n"
+ " */\n\n";
// class header:
- hs << "class " << className << ": public QDBusAbstractAdaptor" << Qt::endl
- << "{" << Qt::endl
- << " Q_OBJECT" << Qt::endl
- << " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")" << Qt::endl
- << " Q_CLASSINFO(\"D-Bus Introspection\", \"\"" << Qt::endl
+ hs << "class " << className << ": public QDBusAbstractAdaptor\n"
+ "{\n"
+ " Q_OBJECT\n"
+ " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")\n"
+ " Q_CLASSINFO(\"D-Bus Introspection\", \"\"\n"
<< stringify(interface->introspection)
- << " \"\")" << Qt::endl
- << "public:" << Qt::endl
- << " " << className << "(" << parent << " *parent);" << Qt::endl
- << " virtual ~" << className << "();" << Qt::endl
- << Qt::endl;
+ << " \"\")\n"
+ "public:\n"
+ " " << className << "(" << parent << " *parent);\n"
+ " ~" << className << "() override;\n\n";
if (!parentClassName.isEmpty())
- hs << " inline " << parent << " *parent() const" << Qt::endl
- << " { return static_cast<" << parent << " *>(QObject::parent()); }" << Qt::endl
- << Qt::endl;
+ hs << " inline " << parent << " *parent() const\n"
+ " { return static_cast<" << parent << " *>(QObject::parent()); }\n\n";
// constructor/destructor
- cs << className << "::" << className << "(" << parent << " *parent)" << Qt::endl
- << " : QDBusAbstractAdaptor(parent)" << Qt::endl
- << "{" << Qt::endl
- << " // constructor" << Qt::endl
- << " setAutoRelaySignals(true);" << Qt::endl
- << "}" << Qt::endl
- << Qt::endl
- << className << "::~" << className << "()" << Qt::endl
- << "{" << Qt::endl
- << " // destructor" << Qt::endl
- << "}" << Qt::endl
- << Qt::endl;
-
- hs << "public: // PROPERTIES" << Qt::endl;
+ cs << className << "::" << className << "(" << parent << " *parent)\n"
+ " : QDBusAbstractAdaptor(parent)\n"
+ "{\n"
+ " // constructor\n"
+ " setAutoRelaySignals(true);\n"
+ "}\n\n"
+ << className << "::~" << className << "()\n"
+ "{\n"
+ " // destructor\n"
+ "}\n\n";
+
+ hs << "public: // PROPERTIES\n";
for (const QDBusIntrospection::Property &property : interface->properties) {
- QByteArray type = qtTypeName(property.type, property.annotations);
+ QByteArray type = qtTypeName(property.location, property.type, property.annotations);
QString constRefType = constRefArg(type);
QString getter = propertyGetter(property);
QString setter = propertySetter(property);
@@ -868,52 +988,47 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
hs << " READ " << getter;
if (property.access != QDBusIntrospection::Property::Read)
hs << " WRITE " << setter;
- hs << ")" << Qt::endl;
+ hs << ")\n";
// getter:
if (property.access != QDBusIntrospection::Property::Write) {
- hs << " " << type << " " << getter << "() const;" << Qt::endl;
+ hs << " " << type << " " << getter << "() const;\n";
cs << type << " "
- << className << "::" << getter << "() const" << Qt::endl
- << "{" << Qt::endl
- << " // get the value of property " << property.name << Qt::endl
- << " return qvariant_cast< " << type <<" >(parent()->property(\"" << property.name << "\"));" << Qt::endl
- << "}" << Qt::endl
- << Qt::endl;
+ << className << "::" << getter << "() const\n"
+ "{\n"
+ " // get the value of property " << property.name << "\n"
+ " return qvariant_cast< " << type <<" >(parent()->property(\"" << property.name << "\"));\n"
+ "}\n\n";
}
// setter
if (property.access != QDBusIntrospection::Property::Read) {
- hs << " void " << setter << "(" << constRefType << "value);" << Qt::endl;
- cs << "void " << className << "::" << setter << "(" << constRefType << "value)" << Qt::endl
- << "{" << Qt::endl
- << " // set the value of property " << property.name << Qt::endl
- << " parent()->setProperty(\"" << property.name << "\", QVariant::fromValue(value";
- if (constRefType.contains(QLatin1String("QDBusVariant")))
+ hs << " void " << setter << "(" << constRefType << "value);\n";
+ cs << "void " << className << "::" << setter << "(" << constRefType << "value)\n"
+ "{\n"
+ " // set the value of property " << property.name << "\n"
+ " parent()->setProperty(\"" << property.name << "\", QVariant::fromValue(value";
+ if (constRefType.contains("QDBusVariant"_L1))
cs << ".variant()";
- cs << "));" << Qt::endl
- << "}" << Qt::endl
- << Qt::endl;
+ cs << "));\n"
+ "}\n\n";
}
- hs << Qt::endl;
+ hs << "\n";
}
- hs << "public Q_SLOTS: // METHODS" << Qt::endl;
+ hs << "public Q_SLOTS: // METHODS\n";
for (const QDBusIntrospection::Method &method : interface->methods) {
- bool isNoReply =
- method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true");
+ bool isNoReply = method.annotations.value(ANNOTATION_NO_WAIT ""_L1).value == "true"_L1;
if (isNoReply && !method.outputArgs.isEmpty()) {
- fprintf(stderr, "%s: warning while processing '%s': method %s in interface %s is marked 'no-reply' but has output arguments.\n",
- PROGRAMNAME, qPrintable(inputFile), qPrintable(method.name), qPrintable(interface->name));
+ reporter.warning(method.location,
+ "method %s in interface %s is marked 'no-reply' but has output "
+ "arguments.\n",
+ qPrintable(method.name), qPrintable(interface->name));
continue;
}
hs << " ";
- if (method.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) ==
- QLatin1String("true"))
- hs << "Q_DECL_DEPRECATED ";
-
QByteArray returnType;
if (isNoReply) {
hs << "Q_NOREPLY void ";
@@ -922,7 +1037,9 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
hs << "void ";
cs << "void ";
} else {
- returnType = qtTypeName(method.outputArgs.first().type, method.annotations, 0, "Out");
+ returnType =
+ qtTypeName(method.outputArgs.first().location,
+ method.outputArgs.first().type, method.annotations, 0, "Out");
hs << returnType << " ";
cs << returnType << " ";
}
@@ -935,46 +1052,42 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
writeArgList(cs, argNames, method.annotations, method.inputArgs, method.outputArgs);
- hs << ");" << Qt::endl; // finished for header
- cs << ")" << Qt::endl
- << "{" << Qt::endl
- << " // handle method call " << interface->name << "." << methodName(method) << Qt::endl;
+ hs << ");\n"; // finished for header
+ cs << ")\n"
+ "{\n"
+ " // handle method call " << interface->name << "." << methodName(method) << "\n";
// make the call
bool usingInvokeMethod = false;
- if (parentClassName.isEmpty() && method.inputArgs.count() <= 10
- && method.outputArgs.count() <= 1)
+ if (parentClassName.isEmpty() && method.inputArgs.size() <= 10
+ && method.outputArgs.size() <= 1)
usingInvokeMethod = true;
if (usingInvokeMethod) {
// we are using QMetaObject::invokeMethod
if (!returnType.isEmpty())
- cs << " " << returnType << " " << argNames.at(method.inputArgs.count())
- << ";" << Qt::endl;
+ cs << " " << returnType << " " << argNames.at(method.inputArgs.size())
+ << ";\n";
static const char invoke[] = " QMetaObject::invokeMethod(parent(), \"";
cs << invoke << name << "\"";
if (!method.outputArgs.isEmpty())
cs << ", Q_RETURN_ARG("
- << qtTypeName(method.outputArgs.at(0).type, method.annotations,
- 0, "Out")
- << ", "
- << argNames.at(method.inputArgs.count())
- << ")";
+ << qtTypeName(method.outputArgs.at(0).location, method.outputArgs.at(0).type,
+ method.annotations, 0, "Out")
+ << ", " << argNames.at(method.inputArgs.size()) << ")";
- for (int i = 0; i < method.inputArgs.count(); ++i)
+ for (qsizetype i = 0; i < method.inputArgs.size(); ++i)
cs << ", Q_ARG("
- << qtTypeName(method.inputArgs.at(i).type, method.annotations,
- i, "In")
- << ", "
- << argNames.at(i)
- << ")";
+ << qtTypeName(method.inputArgs.at(i).location, method.inputArgs.at(i).type,
+ method.annotations, i, "In")
+ << ", " << argNames.at(i) << ")";
- cs << ");" << Qt::endl;
+ cs << ");\n";
if (!returnType.isEmpty())
- cs << " return " << argNames.at(method.inputArgs.count()) << ";" << Qt::endl;
+ cs << " return " << argNames.at(method.inputArgs.size()) << ";\n";
} else {
if (parentClassName.isEmpty())
cs << " //";
@@ -990,51 +1103,44 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
cs << "parent()->";
cs << name << "(";
- int argPos = 0;
+ qsizetype argPos = 0;
bool first = true;
- for (int i = 0; i < method.inputArgs.count(); ++i) {
+ for (qsizetype i = 0; i < method.inputArgs.size(); ++i) {
cs << (first ? "" : ", ") << argNames.at(argPos++);
first = false;
}
++argPos; // skip retval, if any
- for (int i = 1; i < method.outputArgs.count(); ++i) {
+ for (qsizetype i = 1; i < method.outputArgs.size(); ++i) {
cs << (first ? "" : ", ") << argNames.at(argPos++);
first = false;
}
- cs << ");" << Qt::endl;
+ cs << ");\n";
}
- cs << "}" << Qt::endl
- << Qt::endl;
+ cs << "}\n\n";
}
- hs << "Q_SIGNALS: // SIGNALS" << Qt::endl;
+ hs << "Q_SIGNALS: // SIGNALS\n";
for (const QDBusIntrospection::Signal &signal : interface->signals_) {
- hs << " ";
- if (signal.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) ==
- QLatin1String("true"))
- hs << "Q_DECL_DEPRECATED ";
-
- hs << "void " << signal.name << "(";
+ hs << " void " << signal.name << "(";
QStringList argNames = makeArgNames(signal.outputArgs);
writeSignalArgList(hs, argNames, signal.annotations, signal.outputArgs);
- hs << ");" << Qt::endl; // finished for header
+ hs << ");\n"; // finished for header
}
// close the class:
- hs << "};" << Qt::endl
- << Qt::endl;
+ hs << "};\n\n";
}
// close the include guard
- hs << "#endif" << Qt::endl;
+ hs << "#endif\n";
QString mocName = moc(filename);
if (includeMocs && !mocName.isEmpty())
- cs << Qt::endl
- << "#include \"" << mocName << "\"" << Qt::endl;
+ cs << "\n"
+ "#include \"" << mocName << "\"\n";
cs.flush();
hs.flush();
@@ -1054,70 +1160,73 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
}
}
-int main(int argc, char **argv)
+int QDBusXmlToCpp::run(const QCoreApplication &app)
{
- QCoreApplication app(argc, argv);
- QCoreApplication::setApplicationName(QStringLiteral(PROGRAMNAME));
- QCoreApplication::setApplicationVersion(QStringLiteral(PROGRAMVERSION));
-
QCommandLineParser parser;
- parser.setApplicationDescription(QLatin1String(
+ parser.setApplicationDescription(
"Produces the C++ code to implement the interfaces defined in the input file.\n\n"
"If the file name given to the options -a and -p does not end in .cpp or .h, the\n"
"program will automatically append the suffixes and produce both files.\n"
"You can also use a colon (:) to separate the header name from the source file\n"
"name, as in '-a filename_p.h:filename.cpp'.\n\n"
"If you pass a dash (-) as the argument to either -p or -a, the output is written\n"
- "to the standard output."));
+ "to the standard output."_L1);
parser.addHelpOption();
parser.addVersionOption();
- parser.addPositionalArgument(QStringLiteral("xml-or-xml-file"), QStringLiteral("XML file to use."));
- parser.addPositionalArgument(QStringLiteral("interfaces"), QStringLiteral("List of interfaces to use."),
- QStringLiteral("[interfaces ...]"));
+ parser.addPositionalArgument(u"xml-or-xml-file"_s, u"XML file to use."_s);
+ parser.addPositionalArgument(u"interfaces"_s, u"List of interfaces to use."_s,
+ u"[interfaces ...]"_s);
- QCommandLineOption adapterCodeOption(QStringList() << QStringLiteral("a") << QStringLiteral("adaptor"),
- QStringLiteral("Write the adaptor code to <filename>"), QStringLiteral("filename"));
+ QCommandLineOption adapterCodeOption(QStringList{u"a"_s, u"adaptor"_s},
+ u"Write the adaptor code to <filename>"_s, u"filename"_s);
parser.addOption(adapterCodeOption);
- QCommandLineOption classNameOption(QStringList() << QStringLiteral("c") << QStringLiteral("classname"),
- QStringLiteral("Use <classname> as the class name for the generated classes"), QStringLiteral("classname"));
+ QCommandLineOption classNameOption(QStringList{u"c"_s, u"classname"_s},
+ u"Use <classname> as the class name for the generated classes. "
+ u"This option can only be used when processing a single interface."_s,
+ u"classname"_s);
parser.addOption(classNameOption);
- QCommandLineOption addIncludeOption(QStringList() << QStringLiteral("i") << QStringLiteral("include"),
- QStringLiteral("Add #include to the output"), QStringLiteral("filename"));
+ QCommandLineOption addIncludeOption(QStringList{u"i"_s, u"include"_s},
+ u"Add #include \"filename\" to the output"_s, u"filename"_s);
parser.addOption(addIncludeOption);
- QCommandLineOption adapterParentOption(QStringLiteral("l"),
- QStringLiteral("When generating an adaptor, use <classname> as the parent class"), QStringLiteral("classname"));
+ QCommandLineOption addGlobalIncludeOption(QStringList{u"I"_s, u"global-include"_s},
+ u"Add #include <filename> to the output"_s, u"filename"_s);
+ parser.addOption(addGlobalIncludeOption);
+
+ QCommandLineOption adapterParentOption(u"l"_s,
+ u"When generating an adaptor, use <classname> as the parent class"_s, u"classname"_s);
parser.addOption(adapterParentOption);
- QCommandLineOption mocIncludeOption(QStringList() << QStringLiteral("m") << QStringLiteral("moc"),
- QStringLiteral("Generate #include \"filename.moc\" statements in the .cpp files"));
+ QCommandLineOption mocIncludeOption(QStringList{u"m"_s, u"moc"_s},
+ u"Generate #include \"filename.moc\" statements in the .cpp files"_s);
parser.addOption(mocIncludeOption);
- QCommandLineOption noNamespaceOption(QStringList() << QStringLiteral("N") << QStringLiteral("no-namespaces"),
- QStringLiteral("Don't use namespaces"));
+ QCommandLineOption noNamespaceOption(QStringList{u"N"_s, u"no-namespaces"_s},
+ u"Don't use namespaces"_s);
parser.addOption(noNamespaceOption);
- QCommandLineOption proxyCodeOption(QStringList() << QStringLiteral("p") << QStringLiteral("proxy"),
- QStringLiteral("Write the proxy code to <filename>"), QStringLiteral("filename"));
+ QCommandLineOption proxyCodeOption(QStringList{u"p"_s, u"proxy"_s},
+ u"Write the proxy code to <filename>"_s, u"filename"_s);
parser.addOption(proxyCodeOption);
- QCommandLineOption verboseOption(QStringList() << QStringLiteral("V") << QStringLiteral("verbose"),
- QStringLiteral("Be verbose."));
+ QCommandLineOption verboseOption(QStringList{u"V"_s, u"verbose"_s},
+ u"Be verbose."_s);
parser.addOption(verboseOption);
parser.process(app);
- adaptorFile = parser.value(adapterCodeOption);
+ QString adaptorFile = parser.value(adapterCodeOption);
globalClassName = parser.value(classNameOption);
includes = parser.values(addIncludeOption);
+ globalIncludes = parser.values(addGlobalIncludeOption);
parentClassName = parser.value(adapterParentOption);
includeMocs = parser.isSet(mocIncludeOption);
skipNamespaces = parser.isSet(noNamespaceOption);
- proxyFile = parser.value(proxyCodeOption);
- verbose = parser.isSet(verboseOption);
+ QString proxyFile = parser.value(proxyCodeOption);
+ bool verbose = parser.isSet(verboseOption);
wantedInterfaces = parser.positionalArguments();
if (!wantedInterfaces.isEmpty()) {
@@ -1131,15 +1240,19 @@ int main(int argc, char **argv)
}
if (verbose)
- QLoggingCategory::setFilterRules(QStringLiteral("dbus.parser.debug=true"));
+ QLoggingCategory::setFilterRules(u"dbus.parser.debug=true"_s);
QDBusIntrospection::Interfaces interfaces = readInput();
cleanInterfaces(interfaces);
+ if (!globalClassName.isEmpty() && interfaces.count() != 1) {
+ qCritical("Option -c/--classname can only be used with a single interface.\n");
+ return 1;
+ }
+
QStringList args = app.arguments();
args.removeFirst();
- commandLine = QLatin1String(PROGRAMNAME " ");
- commandLine += args.join(QLatin1Char(' '));
+ commandLine = PROGRAMNAME " "_L1 + args.join(u' ');
if (!proxyFile.isEmpty() || adaptorFile.isEmpty())
writeProxy(proxyFile, interfaces);
@@ -1150,3 +1263,11 @@ int main(int argc, char **argv)
return 0;
}
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral(PROGRAMNAME));
+ QCoreApplication::setApplicationVersion(QStringLiteral(PROGRAMVERSION));
+
+ return QDBusXmlToCpp().run(app);
+}
diff --git a/src/tools/qlalr/CMakeLists.txt b/src/tools/qlalr/CMakeLists.txt
index 1b28154695..da8b351889 100644
--- a/src/tools/qlalr/CMakeLists.txt
+++ b/src/tools/qlalr/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from qlalr.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qlalr Tool:
@@ -8,7 +9,7 @@ qt_get_tool_target_name(target_name qlalr)
qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "Qt Look Ahead LR Parser Generator"
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Core # special case
+ TOOLS_TARGET Core
SOURCES
compress.cpp compress.h
cppgenerator.cpp cppgenerator.h
@@ -20,11 +21,10 @@ qt_internal_add_tool(${target_name}
recognizer.cpp recognizer.h
DEFINES
QT_NO_FOREACH
- PUBLIC_LIBRARIES
- Qt::Core # special case
+ QT_NO_QPAIR
+ QT_USE_NODISCARD_FILE_OPEN
+ LIBRARIES
+ Qt::Core
+ Qt::CorePrivate
)
-
-#### Keys ignored in scope 1:.:.:qlalr.pro:<TRUE>:
-# OTHER_FILES = "lalr.g"
-# QMAKE_TARGET_DESCRIPTION = "Qt Look Ahead LR Parser Generator"
-# _OPTION = "host_build"
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/qlalr/compress.cpp b/src/tools/qlalr/compress.cpp
index 18f9fb4b58..6ee083f7e9 100644
--- a/src/tools/qlalr/compress.cpp
+++ b/src/tools/qlalr/compress.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compress.h"
@@ -164,7 +139,7 @@ void Compress::operator () (int *table, int row_count, int column_count)
#ifndef QLALR_NO_CHECK_SORTED_TABLE
int previous_zeros = INT_MAX;
- for (const UncompressedRow &row : qAsConst(sortedTable))
+ for (const UncompressedRow &row : std::as_const(sortedTable))
{
int zeros = row.count (0);
@@ -176,7 +151,7 @@ void Compress::operator () (int *table, int row_count, int column_count)
index.fill (-999999, row_count);
- for (const UncompressedRow &row : qAsConst(sortedTable))
+ for (const UncompressedRow &row : std::as_const(sortedTable))
{
int first_token = std::distance (row.begin (), row.beginNonZeros ());
QList<int>::iterator pos = info.begin();
@@ -186,7 +161,7 @@ void Compress::operator () (int *table, int row_count, int column_count)
if (pos == info.begin ())
{
// try to find a perfect match
- QList<int>::iterator pm = std::search(&*pos, &*info.end(), row.beginNonZeros(),
+ QList<int>::iterator pm = std::search(pos, info.end(), row.beginNonZeros(),
row.endNonZeros(), _PerfectMatch());
if (pm != info.end ())
@@ -196,7 +171,7 @@ void Compress::operator () (int *table, int row_count, int column_count)
}
}
- pos = std::search (&*pos, &*info.end (), row.beginNonZeros (), row.endNonZeros (), _Fit ());
+ pos = std::search (pos, info.end (), row.beginNonZeros (), row.endNonZeros (), _Fit ());
if (pos == info.end ())
break;
@@ -251,7 +226,7 @@ void Compress::operator () (int *table, int row_count, int column_count)
}
#if 0
- for (const UncompressedRow &row : qAsConst(sortedTable))
+ for (const UncompressedRow &row : std::as_const(sortedTable))
{
int i = row.index ();
Q_ASSERT (i < sortedTable.size ());
diff --git a/src/tools/qlalr/compress.h b/src/tools/qlalr/compress.h
index 84b804e234..99f790348a 100644
--- a/src/tools/qlalr/compress.h
+++ b/src/tools/qlalr/compress.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef COMPRESS_H
#define COMPRESS_H
diff --git a/src/tools/qlalr/cppgenerator.cpp b/src/tools/qlalr/cppgenerator.cpp
index 7efe94a5c2..fd56de106d 100644
--- a/src/tools/qlalr/cppgenerator.cpp
+++ b/src/tools/qlalr/cppgenerator.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppgenerator.h"
@@ -35,9 +10,12 @@
#include <QtCore/qtextstream.h>
#include <QtCore/qfile.h>
#include <QtCore/qmap.h>
+#include <QtCore/private/qconfig_p.h>
#include <iterator>
+using namespace Qt::StringLiterals;
+
namespace {
void generateSeparator(int i, QTextStream &out)
@@ -64,40 +42,15 @@ void generateList(const QList<int> &list, QTextStream &out)
QString CppGenerator::copyrightHeader() const
{
- return QLatin1String(
- "/****************************************************************************\n"
- "**\n"
- "** Copyright (C) 2016 The Qt Company Ltd.\n"
- "** Contact: https://www.qt.io/licensing/\n"
- "**\n"
- "** This file is part of the Qt Toolkit.\n"
- "**\n"
- "** $QT_BEGIN_LICENSE:GPL-EXCEPT$\n"
- "** Commercial License Usage\n"
- "** Licensees holding valid commercial Qt licenses may use this file in\n"
- "** accordance with the commercial license agreement provided with the\n"
- "** Software or, alternatively, in accordance with the terms contained in\n"
- "** a written agreement between you and The Qt Company. For licensing terms\n"
- "** and conditions see https://www.qt.io/terms-conditions. For further\n"
- "** information use the contact form at https://www.qt.io/contact-us.\n"
- "**\n"
- "** GNU General Public License Usage\n"
- "** Alternatively, this file may be used under the terms of the GNU\n"
- "** General Public License version 3 as published by the Free Software\n"
- "** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT\n"
- "** included in the packaging of this file. Please review the following\n"
- "** information to ensure the GNU General Public License requirements will\n"
- "** be met: https://www.gnu.org/licenses/gpl-3.0.html.\n"
- "**\n"
- "** $QT_END_LICENSE$\n"
- "**\n"
- "****************************************************************************/\n"
- "\n");
+ return
+ "// " QT_COPYRIGHT "\n"
+ "// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0\n"
+ "\n"_L1;
}
QString CppGenerator::privateCopyrightHeader() const
{
- return QLatin1String(
+ return
"//\n"
"// W A R N I N G\n"
"// -------------\n"
@@ -107,12 +60,12 @@ QString CppGenerator::privateCopyrightHeader() const
"// version without notice, or even be removed.\n"
"//\n"
"// We mean it.\n"
- "//\n");
+ "//\n"_L1;
}
QString CppGenerator::startIncludeGuard(const QString &fileName)
{
- const QString normalized(QString(fileName).replace(QLatin1Char('.'), QLatin1Char('_')).toUpper());
+ const QString normalized(QString(fileName).replace(u'.', u'_').toUpper());
return QString::fromLatin1("#ifndef %1\n"
"#define %2\n").arg(normalized, normalized);
@@ -120,7 +73,7 @@ QString CppGenerator::startIncludeGuard(const QString &fileName)
QString CppGenerator::endIncludeGuard(const QString &fileName)
{
- const QString normalized(QString(fileName).replace(QLatin1Char('.'), QLatin1Char('_')).toUpper());
+ const QString normalized(QString(fileName).replace(u'.', u'_').toUpper());
return QString::fromLatin1("#endif // %1\n").arg(normalized);
}
@@ -240,7 +193,15 @@ void CppGenerator::operator () ()
{
if (shift_reduce_conflict_count != grammar.expected_shift_reduce
|| reduce_reduce_conflict_count != grammar.expected_reduce_reduce)
- qerr() << "*** Conflicts: " << shift_reduce_conflict_count << " shift/reduce, " << reduce_reduce_conflict_count << " reduce/reduce" << Qt::endl;
+ {
+ qerr() << "*** Conflicts: " << shift_reduce_conflict_count << " shift/reduce, " << reduce_reduce_conflict_count << " reduce/reduce" << Qt::endl;
+ if (warnings_are_errors)
+ {
+ qerr() << "qlalr: error: warning occurred, treating as error due to "
+ "--exit-on-warn." << Qt::endl;
+ exit(2);
+ }
+ }
if (verbose)
qout() << Qt::endl << "*** Conflicts: " << shift_reduce_conflict_count << " shift/reduce, " << reduce_reduce_conflict_count << " reduce/reduce" << Qt::endl
@@ -262,12 +223,20 @@ void CppGenerator::operator () ()
}
auto rule = grammar.rules.begin();
- for (int i = 0; i < used_rules.count (); ++i, ++rule)
+ for (int i = 0; i < used_rules.size(); ++i, ++rule)
{
if (! used_rules.testBit (i))
{
if (rule != grammar.goal)
- qerr() << "*** Warning: Rule ``" << *rule << "'' is useless!" << Qt::endl;
+ {
+ qerr() << "*** Warning: Rule ``" << *rule << "'' is useless!" << Qt::endl;
+ if (warnings_are_errors)
+ {
+ qerr() << "qlalr: error: warning occurred, treating as error due to "
+ "--exit-on-warn." << Qt::endl;
+ exit(2);
+ }
+ }
}
}
@@ -374,12 +343,17 @@ void CppGenerator::operator () ()
}
// default behaviour
- QString declFileName = grammar.table_name.toLower () + QLatin1String("_p.h");
- QString bitsFileName = grammar.table_name.toLower () + QLatin1String(".cpp");
+ QString declFileName = grammar.table_name.toLower () + "_p.h"_L1;
+ QString bitsFileName = grammar.table_name.toLower () + ".cpp"_L1;
{ // decls...
QFile f (declFileName);
- f.open (QFile::WriteOnly);
+ if (! f.open (QFile::WriteOnly))
+ {
+ fprintf (stderr, "*** cannot create %s: %s\n",
+ qPrintable(declFileName), qPrintable(f.errorString()));
+ return;
+ }
QTextStream out (&f);
QString prot = declFileName.toUpper ().replace (QLatin1Char ('.'), QLatin1Char ('_'));
@@ -411,7 +385,12 @@ void CppGenerator::operator () ()
{ // bits...
QFile f (bitsFileName);
- f.open (QFile::WriteOnly);
+ if (! f.open (QFile::WriteOnly))
+ {
+ fprintf (stderr, "*** cannot create %s: %s\n",
+ qPrintable(bitsFileName), qPrintable(f.errorString()));
+ return;
+ }
QTextStream out (&f);
// copyright headers must come first, otherwise the headers tests will fail
@@ -432,7 +411,12 @@ void CppGenerator::operator () ()
if (! grammar.decl_file_name.isEmpty ())
{
QFile f (grammar.decl_file_name);
- f.open (QFile::WriteOnly);
+ if (! f.open (QFile::WriteOnly))
+ {
+ fprintf (stderr, "*** cannot create %s: %s\n",
+ qPrintable(grammar.decl_file_name), qPrintable(f.errorString()));
+ return;
+ }
QTextStream out (&f);
out << p.decls();
}
@@ -440,7 +424,12 @@ void CppGenerator::operator () ()
if (! grammar.impl_file_name.isEmpty ())
{
QFile f (grammar.impl_file_name);
- f.open (QFile::WriteOnly);
+ if (! f.open (QFile::WriteOnly))
+ {
+ fprintf (stderr, "*** cannot create %s: %s\n",
+ qPrintable(grammar.impl_file_name), qPrintable(f.errorString()));
+ return;
+ }
QTextStream out (&f);
out << p.impls();
}
@@ -448,9 +437,9 @@ void CppGenerator::operator () ()
QString CppGenerator::debugInfoProt() const
{
- QString prot = QLatin1String("QLALR_NO_");
+ QString prot = "QLALR_NO_"_L1;
prot += grammar.table_name.toUpper();
- prot += QLatin1String("_DEBUG_INFO");
+ prot += "_DEBUG_INFO"_L1;
return prot;
}
@@ -461,16 +450,16 @@ void CppGenerator::generateDecl (QTextStream &out)
<< "public:" << Qt::endl
<< " enum VariousConstants {" << Qt::endl;
- for (const Name &t : qAsConst(grammar.terminals))
+ for (const Name &t : std::as_const(grammar.terminals))
{
QString name = *t;
int value = std::distance (grammar.names.begin (), t);
- if (name == QLatin1String ("$end"))
- name = QLatin1String ("EOF_SYMBOL");
+ if (name == "$end"_L1)
+ name = "EOF_SYMBOL"_L1;
- else if (name == QLatin1String ("$accept"))
- name = QLatin1String ("ACCEPT_SYMBOL");
+ else if (name == "$accept"_L1)
+ name = "ACCEPT_SYMBOL"_L1;
else
name.prepend (grammar.token_prefix);
diff --git a/src/tools/qlalr/cppgenerator.h b/src/tools/qlalr/cppgenerator.h
index a83ec101fd..66ae781be4 100644
--- a/src/tools/qlalr/cppgenerator.h
+++ b/src/tools/qlalr/cppgenerator.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CPPGENERATOR_H
#define CPPGENERATOR_H
@@ -45,7 +20,8 @@ public:
aut (aut),
verbose (verbose),
debug_info (false),
- copyright (false) {}
+ copyright (false),
+ warnings_are_errors(false) {}
void operator () ();
@@ -54,6 +30,8 @@ public:
void setCopyright (bool t) { copyright = t; }
+ void setWarningsAreErrors (bool e) { warnings_are_errors = e; }
+
private:
void generateDecl (QTextStream &out);
void generateImpl (QTextStream &out);
@@ -76,6 +54,7 @@ private:
int non_terminal_count;
bool debug_info;
bool copyright;
+ bool warnings_are_errors;
Compress compressed_action;
Compress compressed_goto;
QList<int> count;
diff --git a/src/tools/qlalr/dotgraph.cpp b/src/tools/qlalr/dotgraph.cpp
index 1d479af2b2..75bd304367 100644
--- a/src/tools/qlalr/dotgraph.cpp
+++ b/src/tools/qlalr/dotgraph.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dotgraph.h"
diff --git a/src/tools/qlalr/dotgraph.h b/src/tools/qlalr/dotgraph.h
index 2d4f1cd2fc..42d4527c46 100644
--- a/src/tools/qlalr/dotgraph.h
+++ b/src/tools/qlalr/dotgraph.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DOTGRAPH_H
#define DOTGRAPH_H
diff --git a/src/tools/qlalr/examples/dummy-xml/ll/dummy-xml-ll.cpp b/src/tools/qlalr/examples/dummy-xml/ll/dummy-xml-ll.cpp
index 7bf1506b4a..24399d3786 100644
--- a/src/tools/qlalr/examples/dummy-xml/ll/dummy-xml-ll.cpp
+++ b/src/tools/qlalr/examples/dummy-xml/ll/dummy-xml-ll.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <cstdlib>
#include <cstdio>
diff --git a/src/tools/qlalr/examples/dummy-xml/xml.g b/src/tools/qlalr/examples/dummy-xml/xml.g
index 43c1fb169f..59472dc219 100644
--- a/src/tools/qlalr/examples/dummy-xml/xml.g
+++ b/src/tools/qlalr/examples/dummy-xml/xml.g
@@ -1,30 +1,5 @@
-----------------------------------------------------------------------------
---
-- Copyright (C) 2016 The Qt Company Ltd.
--- Contact: https://www.qt.io/licensing/
---
--- This file is part of the QtCore module of the Qt Toolkit.
---
--- $QT_BEGIN_LICENSE:GPL-EXCEPT$
--- Commercial License Usage
--- Licensees holding valid commercial Qt licenses may use this file in
--- accordance with the commercial license agreement provided with the
--- Software or, alternatively, in accordance with the terms contained in
--- a written agreement between you and The Qt Company. For licensing terms
--- and conditions see https://www.qt.io/terms-conditions. For further
--- information use the contact form at https://www.qt.io/contact-us.
---
--- GNU General Public License Usage
--- Alternatively, this file may be used under the terms of the GNU
--- General Public License version 3 as published by the Free Software
--- Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
--- included in the packaging of this file. Please review the following
--- information to ensure the GNU General Public License requirements will
--- be met: https://www.gnu.org/licenses/gpl-3.0.html.
---
--- $QT_END_LICENSE$
---
-----------------------------------------------------------------------------
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
%parser XMLTable
diff --git a/src/tools/qlalr/examples/glsl/build.sh b/src/tools/qlalr/examples/glsl/build.sh
index 5bd0faab07..d43889bf72 100644
--- a/src/tools/qlalr/examples/glsl/build.sh
+++ b/src/tools/qlalr/examples/glsl/build.sh
@@ -1,31 +1,6 @@
#!/bin/sh
-#############################################################################
-##
-## Copyright (C) 2016 The Qt Company Ltd.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is the build configuration utility of the Qt Toolkit.
-##
-## $QT_BEGIN_LICENSE:GPL-EXCEPT$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 3 as published by the Free Software
-## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
+# Copyright (C) 2016 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
${FLEX-flex} -oglsl-lex.incl glsl-lex.l
${QLALR-qlalr} glsl.g
diff --git a/src/tools/qlalr/examples/glsl/glsl-lex.l b/src/tools/qlalr/examples/glsl/glsl-lex.l
index c92aad296b..f3f9bb4f50 100644
--- a/src/tools/qlalr/examples/glsl/glsl-lex.l
+++ b/src/tools/qlalr/examples/glsl/glsl-lex.l
@@ -1,32 +1,5 @@
-
-%{
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR tool of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <cassert>
#define YY_DECL int GLSLParser::nextToken()
diff --git a/src/tools/qlalr/examples/glsl/glsl.g b/src/tools/qlalr/examples/glsl/glsl.g
index 66a2c73656..223e90284d 100644
--- a/src/tools/qlalr/examples/glsl/glsl.g
+++ b/src/tools/qlalr/examples/glsl/glsl.g
@@ -1,30 +1,5 @@
-----------------------------------------------------------------------------
---
-- Copyright (C) 2016 The Qt Company Ltd.
--- Contact: https://www.qt.io/licensing/
---
--- This file is part of the QtCore module of the Qt Toolkit.
---
--- $QT_BEGIN_LICENSE:GPL-EXCEPT$
--- Commercial License Usage
--- Licensees holding valid commercial Qt licenses may use this file in
--- accordance with the commercial license agreement provided with the
--- Software or, alternatively, in accordance with the terms contained in
--- a written agreement between you and The Qt Company. For licensing terms
--- and conditions see https://www.qt.io/terms-conditions. For further
--- information use the contact form at https://www.qt.io/contact-us.
---
--- GNU General Public License Usage
--- Alternatively, this file may be used under the terms of the GNU
--- General Public License version 3 as published by the Free Software
--- Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
--- included in the packaging of this file. Please review the following
--- information to ensure the GNU General Public License requirements will
--- be met: https://www.gnu.org/licenses/gpl-3.0.html.
---
--- $QT_END_LICENSE$
---
-----------------------------------------------------------------------------
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
%parser GLSLParserTable
%merged_output glsl.cpp
diff --git a/src/tools/qlalr/examples/lambda/lambda.g b/src/tools/qlalr/examples/lambda/lambda.g
index 2aa3c128e9..d4fd01ed4c 100644
--- a/src/tools/qlalr/examples/lambda/lambda.g
+++ b/src/tools/qlalr/examples/lambda/lambda.g
@@ -1,30 +1,5 @@
-----------------------------------------------------------------------------
---
-- Copyright (C) 2016 The Qt Company Ltd.
--- Contact: https://www.qt.io/licensing/
---
--- This file is part of the QtCore module of the Qt Toolkit.
---
--- $QT_BEGIN_LICENSE:GPL-EXCEPT$
--- Commercial License Usage
--- Licensees holding valid commercial Qt licenses may use this file in
--- accordance with the commercial license agreement provided with the
--- Software or, alternatively, in accordance with the terms contained in
--- a written agreement between you and The Qt Company. For licensing terms
--- and conditions see https://www.qt.io/terms-conditions. For further
--- information use the contact form at https://www.qt.io/contact-us.
---
--- GNU General Public License Usage
--- Alternatively, this file may be used under the terms of the GNU
--- General Public License version 3 as published by the Free Software
--- Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
--- included in the packaging of this file. Please review the following
--- information to ensure the GNU General Public License requirements will
--- be met: https://www.gnu.org/licenses/gpl-3.0.html.
---
--- $QT_END_LICENSE$
---
-----------------------------------------------------------------------------
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-- lambda calculus
diff --git a/src/tools/qlalr/examples/lambda/main.cpp b/src/tools/qlalr/examples/lambda/main.cpp
index 6198cc8538..c6a2695493 100644
--- a/src/tools/qlalr/examples/lambda/main.cpp
+++ b/src/tools/qlalr/examples/lambda/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "lambda.h"
diff --git a/src/tools/qlalr/examples/qparser/calc.g b/src/tools/qlalr/examples/qparser/calc.g
index 8524c1ca2c..2be9fd55c0 100644
--- a/src/tools/qlalr/examples/qparser/calc.g
+++ b/src/tools/qlalr/examples/qparser/calc.g
@@ -1,30 +1,5 @@
-----------------------------------------------------------------------------
---
-- Copyright (C) 2016 The Qt Company Ltd.
--- Contact: https://www.qt.io/licensing/
---
--- This file is part of the QtCore module of the Qt Toolkit.
---
--- $QT_BEGIN_LICENSE:GPL-EXCEPT$
--- Commercial License Usage
--- Licensees holding valid commercial Qt licenses may use this file in
--- accordance with the commercial license agreement provided with the
--- Software or, alternatively, in accordance with the terms contained in
--- a written agreement between you and The Qt Company. For licensing terms
--- and conditions see https://www.qt.io/terms-conditions. For further
--- information use the contact form at https://www.qt.io/contact-us.
---
--- GNU General Public License Usage
--- Alternatively, this file may be used under the terms of the GNU
--- General Public License version 3 as published by the Free Software
--- Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
--- included in the packaging of this file. Please review the following
--- information to ensure the GNU General Public License requirements will
--- be met: https://www.gnu.org/licenses/gpl-3.0.html.
---
--- $QT_END_LICENSE$
---
-----------------------------------------------------------------------------
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
%parser calc_grammar
%decl calc_parser.h
diff --git a/src/tools/qlalr/examples/qparser/calc.l b/src/tools/qlalr/examples/qparser/calc.l
index fe542680a8..0f42987758 100644
--- a/src/tools/qlalr/examples/qparser/calc.l
+++ b/src/tools/qlalr/examples/qparser/calc.l
@@ -1,34 +1,5 @@
-
-%option noyywrap
-
-%{
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR tool of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "calc_parser.h"
#include <cstdlib>
diff --git a/src/tools/qlalr/examples/qparser/qparser.cpp b/src/tools/qlalr/examples/qparser/qparser.cpp
index c173436a9c..354a778458 100644
--- a/src/tools/qlalr/examples/qparser/qparser.cpp
+++ b/src/tools/qlalr/examples/qparser/qparser.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "qparser.h"
diff --git a/src/tools/qlalr/examples/qparser/qparser.h b/src/tools/qlalr/examples/qparser/qparser.h
index 7700562d55..80643616c4 100644
--- a/src/tools/qlalr/examples/qparser/qparser.h
+++ b/src/tools/qlalr/examples/qparser/qparser.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef QPARSER_H
#define QPARSER_H
diff --git a/src/tools/qlalr/grammar.cpp b/src/tools/qlalr/grammar.cpp
index 2ab6504bf8..d4d8916c6d 100644
--- a/src/tools/qlalr/grammar.cpp
+++ b/src/tools/qlalr/grammar.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// This file was generated by qlalr - DO NOT EDIT!
#include "grammar_p.h"
diff --git a/src/tools/qlalr/grammar_p.h b/src/tools/qlalr/grammar_p.h
index 8482f9bfe1..b6fa6b6353 100644
--- a/src/tools/qlalr/grammar_p.h
+++ b/src/tools/qlalr/grammar_p.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
//
// W A R N I N G
diff --git a/src/tools/qlalr/lalr.cpp b/src/tools/qlalr/lalr.cpp
index 853465c272..51f1d94a40 100644
--- a/src/tools/qlalr/lalr.cpp
+++ b/src/tools/qlalr/lalr.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "lalr.h"
@@ -39,6 +14,8 @@
#define QLALR_NO_DEBUG_INCLUDES
#define QLALR_NO_DEBUG_LOOKAHEADS
+using namespace Qt::StringLiterals;
+
QT_BEGIN_NAMESPACE
QTextStream &qerr()
{
@@ -162,24 +139,24 @@ State::State (Grammar *g):
{
}
-QPair<ItemPointer, bool> State::insert (const Item &item)
+std::pair<ItemPointer, bool> State::insert(const Item &item)
{
ItemPointer it = std::find (kernel.begin (), kernel.end (), item);
if (it != kernel.end ())
- return qMakePair (it, false);
+ return {it, false};
- return qMakePair (kernel.insert (it, item), true);
+ return {kernel.insert(it, item), true};
}
-QPair<ItemPointer, bool> State::insertClosure (const Item &item)
+std::pair<ItemPointer, bool> State::insertClosure(const Item &item)
{
ItemPointer it = std::find (closure.begin (), closure.end (), item);
if (it != closure.end ())
- return qMakePair (it, false);
+ return {it, false};
- return qMakePair (closure.insert (it, item), true);
+ return {closure.insert (it, item), true};
}
@@ -194,11 +171,11 @@ Grammar::Grammar ():
current_prec = 0;
current_assoc = NonAssoc;
- table_name = QLatin1String ("parser_table");
+ table_name = "parser_table"_L1;
tk_end = intern ("$end");
terminals.insert (tk_end);
- spells.insert (tk_end, QLatin1String("end of file"));
+ spells.insert (tk_end, "end of file"_L1);
/*tk_error= terminals.insert (intern ("error"))*/;
}
@@ -319,14 +296,14 @@ void Automaton::buildNullables ()
#endif
}
-QPair<StatePointer, bool> Automaton::internState (const State &state)
+std::pair<StatePointer, bool> Automaton::internState(const State &state)
{
StatePointer it = std::find (states.begin (), states.end (), state);
if (it != states.end ())
- return qMakePair (it, false);
+ return {it, false};
- return qMakePair (states.insert (it, state), true);
+ return {states.insert (it, state), true};
}
struct _Bucket
@@ -374,7 +351,7 @@ void Automaton::closure (StatePointer state)
if (_M_grammar->isNonTerminal (*item->dot))
{
- const auto range = qAsConst(_M_grammar->rule_map).equal_range(*item->dot);
+ const auto range = std::as_const(_M_grammar->rule_map).equal_range(*item->dot);
for (auto it = range.first; it != range.second; ++it)
{
const RulePointer &rule = *it;
@@ -382,7 +359,7 @@ void Automaton::closure (StatePointer state)
ii.rule = rule;
ii.dot = rule->rhs.begin ();
- QPair<ItemPointer, bool> r = state->insertClosure (ii);
+ std::pair<ItemPointer, bool> r = state->insertClosure(ii);
if (r.second)
working_list.push (r.first);
@@ -394,7 +371,7 @@ void Automaton::closure (StatePointer state)
for (bucket_map_type::iterator bucket = buckets.begin (); bucket != buckets.end (); ++bucket)
{
- QPair<StatePointer, bool> r = internState (bucket->toState (this));
+ std::pair<StatePointer, bool> r = internState(bucket->toState(this));
StatePointer target = r.first;
@@ -422,7 +399,7 @@ void Automaton::buildLookbackSets ()
if (! _M_grammar->isNonTerminal (A))
continue;
- const auto range = qAsConst(_M_grammar->rule_map).equal_range(A);
+ const auto range = std::as_const(_M_grammar->rule_map).equal_range(A);
for (auto it = range.first; it != range.second; ++it)
{
const RulePointer &rule = *it;
@@ -617,7 +594,7 @@ void Automaton::buildIncludesDigraph ()
if (! _M_grammar->isNonTerminal (name))
continue;
- const auto range = qAsConst(_M_grammar->rule_map).equal_range(name);
+ const auto range = std::as_const(_M_grammar->rule_map).equal_range(name);
for (auto it = range.first; it != range.second; ++it)
{
const RulePointer &rule = *it;
@@ -719,7 +696,7 @@ void Automaton::buildLookaheads ()
{
for (ItemPointer item = p->closure.begin (); item != p->closure.end (); ++item)
{
- const auto range = qAsConst(lookbacks).equal_range(item);
+ const auto range = std::as_const(lookbacks).equal_range(item);
for (auto it = range.first; it != range.second; ++it)
{
const Lookback &lookback = *it;
diff --git a/src/tools/qlalr/lalr.g b/src/tools/qlalr/lalr.g
index 3c1d1d3ebd..71d0054feb 100644
--- a/src/tools/qlalr/lalr.g
+++ b/src/tools/qlalr/lalr.g
@@ -1,30 +1,5 @@
------------------------------------------------------------------------------
---
-- Copyright (C) 2016 The Qt Company Ltd.
--- Contact: https://www.qt.io/licensing/
---
--- This file is part of the QLALR project on Qt Labs.
---
--- $QT_BEGIN_LICENSE:GPL-EXCEPT$
--- Commercial License Usage
--- Licensees holding valid commercial Qt licenses may use this file in
--- accordance with the commercial license agreement provided with the
--- Software or, alternatively, in accordance with the terms contained in
--- a written agreement between you and The Qt Company. For licensing terms
--- and conditions see https://www.qt.io/terms-conditions. For further
--- information use the contact form at https://www.qt.io/contact-us.
---
--- GNU General Public License Usage
--- Alternatively, this file may be used under the terms of the GNU
--- General Public License version 3 as published by the Free Software
--- Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
--- included in the packaging of this file. Please review the following
--- information to ensure the GNU General Public License requirements will
--- be met: https://www.gnu.org/licenses/gpl-3.0.html.
---
--- $QT_END_LICENSE$
---
------------------------------------------------------------------------------
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
%parser grammar
@@ -61,33 +36,8 @@
%start Specification
-/:/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR project on Qt Labs.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+/:// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "$header"
@@ -126,7 +76,7 @@ protected: // scanner
{
ch = *_M_currentChar++;
- if (ch == QLatin1Char('\n'))
+ if (ch == u'\n')
++_M_line;
}
else
@@ -162,33 +112,8 @@ protected:
};
:/
-/./****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR project on Qt Labs.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+/.// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "recognizer.h"
@@ -198,6 +123,8 @@ protected:
#include <cstring>
#include <cctype>
+using namespace Qt::StringLiterals;
+
Recognizer::Recognizer (Grammar *grammar, bool no_lines):
tos(0),
stack_size(0),
@@ -247,9 +174,9 @@ int Recognizer::nextToken()
{
inp(); // skip "
text.clear ();
- while (! ch.isNull () && ch != QLatin1Char ('"'))
+ while (! ch.isNull () && ch != u'"')
{
- if (ch == QLatin1Char ('\\'))
+ if (ch == u'\\')
{
text += ch;
inp();
@@ -258,7 +185,7 @@ int Recognizer::nextToken()
inp ();
}
- if (ch == QLatin1Char ('"'))
+ if (ch == u'"')
inp ();
else
qerr() << _M_input_file << ":" << _M_line << ": Warning. Expected `\"'" << Qt::endl;
@@ -267,11 +194,11 @@ int Recognizer::nextToken()
return (token = STRING_LITERAL);
}
- else if (ch.isLetterOrNumber () || ch == QLatin1Char ('_'))
+ else if (ch.isLetterOrNumber () || ch == u'_')
{
text.clear ();
do { text += ch; inp (); }
- while (ch.isLetterOrNumber () || ch == QLatin1Char ('_') || ch == QLatin1Char ('.'));
+ while (ch.isLetterOrNumber () || ch == u'_' || ch == u'.');
_M_current_value = text;
return (token = ID);
}
@@ -284,33 +211,33 @@ int Recognizer::nextToken()
while (ch.isSpace ());
do { text += ch; inp (); }
- while (ch.isLetterOrNumber () || ch == QLatin1Char ('_') || ch == QLatin1Char ('-'));
+ while (ch.isLetterOrNumber () || ch == u'_' || ch == u'-');
- if (text == QLatin1String("token_prefix"))
+ if (text == "token_prefix"_L1)
return (token = TOKEN_PREFIX);
- else if (text == QLatin1String("merged_output"))
+ else if (text == "merged_output"_L1)
return (token = MERGED_OUTPUT);
- else if (text == QLatin1String("token"))
+ else if (text == "token"_L1)
return (token = TOKEN);
- else if (text == QLatin1String("start"))
+ else if (text == "start"_L1)
return (token = START);
- else if (text == QLatin1String("parser"))
+ else if (text == "parser"_L1)
return (token = PARSER);
- else if (text == QLatin1String("decl"))
+ else if (text == "decl"_L1)
return (token = DECL_FILE);
- else if (text == QLatin1String("impl"))
+ else if (text == "impl"_L1)
return (token = IMPL_FILE);
- else if (text == QLatin1String("expect"))
+ else if (text == "expect"_L1)
return (token = EXPECT);
- else if (text == QLatin1String("expect-rr"))
+ else if (text == "expect-rr"_L1)
return (token = EXPECT_RR);
- else if (text == QLatin1String("left"))
+ else if (text == "left"_L1)
return (token = LEFT);
- else if (text == QLatin1String("right"))
+ else if (text == "right"_L1)
return (token = RIGHT);
- else if (text == QLatin1String("nonassoc"))
+ else if (text == "nonassoc"_L1)
return (token = NONASSOC);
- else if (text == QLatin1String("prec"))
+ else if (text == "prec"_L1)
return (token = PREC);
else
{
@@ -322,30 +249,30 @@ int Recognizer::nextToken()
inp ();
- if (token == '-' && ch == QLatin1Char ('-'))
+ if (token == '-' && ch == u'-')
{
do { inp (); }
- while (! ch.isNull () && ch != QLatin1Char ('\n'));
+ while (! ch.isNull () && ch != u'\n');
goto Lagain;
}
- else if (token == ':' && ch == QLatin1Char (':'))
+ else if (token == ':' && ch == u':')
{
inp ();
- if (ch != QLatin1Char ('='))
+ if (ch != u'=')
return (token = ERROR);
inp ();
return (token = COLON);
}
- else if (token == '/' && ch == QLatin1Char (':'))
+ else if (token == '/' && ch == u':')
{
_M_action_line = _M_line;
text.clear ();
if (! _M_no_lines)
- text += QLatin1String("\n#line ") + QString::number(_M_action_line) +
- QLatin1String(" \"") + QDir::fromNativeSeparators(_M_input_file) + QLatin1String("\"\n");
+ text += "\n#line "_L1 + QString::number(_M_action_line) +
+ " \""_L1 + QDir::fromNativeSeparators(_M_input_file) + "\"\n"_L1;
inp (); // skip ':'
forever
@@ -355,13 +282,13 @@ int Recognizer::nextToken()
token = ch.unicode ();
inp ();
- if (token == ':' && ch == QLatin1Char ('/'))
+ if (token == ':' && ch == u'/')
break;
text += QLatin1Char (token);
}
- if (ch != QLatin1Char ('/'))
+ if (ch != u'/')
return (token = ERROR);
inp ();
@@ -372,18 +299,18 @@ int Recognizer::nextToken()
return (token = DECL);
}
else
- text += QLatin1String (":/");
+ text += ":/"_L1;
}
}
- else if (token == '/' && ch == QLatin1Char ('.'))
+ else if (token == '/' && ch == u'.')
{
_M_action_line = _M_line;
text.clear ();
if (! _M_no_lines)
- text += QLatin1String("\n#line ") + QString::number(_M_action_line) +
- QLatin1String(" \"") + QDir::fromNativeSeparators(_M_input_file) + QLatin1String("\"\n");
+ text += "\n#line "_L1 + QString::number(_M_action_line) +
+ " \""_L1 + QDir::fromNativeSeparators(_M_input_file) + "\"\n"_L1;
inp (); // skip ':'
@@ -394,13 +321,13 @@ int Recognizer::nextToken()
token = ch.unicode ();
inp ();
- if (token == '.' && ch == QLatin1Char ('/'))
+ if (token == '.' && ch == u'/')
break;
text += QLatin1Char (token);
}
- if (ch != QLatin1Char ('/'))
+ if (ch != u'/')
return (token = ERROR);
inp ();
@@ -411,7 +338,7 @@ int Recognizer::nextToken()
return (token = IMPL);
}
else
- text += QLatin1String ("./");
+ text += "./"_L1;
}
}
diff --git a/src/tools/qlalr/lalr.h b/src/tools/qlalr/lalr.h
index 5be44fea67..efa0a91a39 100644
--- a/src/tools/qlalr/lalr.h
+++ b/src/tools/qlalr/lalr.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef LALR_H
#define LALR_H
@@ -34,7 +9,6 @@
#include <QtCore/qmap.h>
#include <QtCore/qstring.h>
#include <QtCore/qtextstream.h>
-#include <QtCore/qpair.h>
#include <algorithm>
#include <functional>
@@ -142,8 +116,8 @@ public:
inline bool operator != (const State &other) const
{ return kernel != other.kernel; }
- QPair<ItemPointer, bool> insert (const Item &item);
- QPair<ItemPointer, bool> insertClosure (const Item &item);
+ std::pair<ItemPointer, bool> insert(const Item &item);
+ std::pair<ItemPointer, bool> insertClosure(const Item &item);
public: // attributes
ItemList kernel;
@@ -168,7 +142,7 @@ public:
public:
static iterator get (_Tp data);
- QPair<edge_iterator, bool> insertEdge (iterator other) const;
+ std::pair<edge_iterator, bool> insertEdge(iterator other) const;
inline edge_iterator begin () const
{ return outs.begin (); }
@@ -223,15 +197,15 @@ typename Node<_Tp>::iterator Node<_Tp>::get (_Tp data)
}
template <typename _Tp>
-QPair<typename std::list<typename Node<_Tp>::iterator>::iterator, bool> Node<_Tp>::insertEdge(typename Node<_Tp>::iterator other) const
+std::pair<typename std::list<typename Node<_Tp>::iterator>::iterator, bool> Node<_Tp>::insertEdge(typename Node<_Tp>::iterator other) const
{
edge_iterator it = std::find (outs.begin (), outs.end (), other);
if (it != outs.end ())
- return qMakePair (it, false);
+ return {it, false};
other->root = false;
- return qMakePair (outs.insert (outs.end (), other), true);
+ return {outs.insert (outs.end (), other), true};
}
/////////////////////////////////////////////////////////////
@@ -337,7 +311,7 @@ class Automaton
public:
Automaton (Grammar *g);
- QPair<StatePointer, bool> internState (const State &state);
+ std::pair<StatePointer, bool> internState (const State &state);
typedef Node<Read> ReadsGraph;
typedef ReadsGraph::iterator ReadNode;
diff --git a/src/tools/qlalr/main.cpp b/src/tools/qlalr/main.cpp
index 6a57c7aa7a..04ae54d986 100644
--- a/src/tools/qlalr/main.cpp
+++ b/src/tools/qlalr/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "lalr.h"
#include "dotgraph.h"
@@ -42,6 +17,8 @@
#define QLALR_NO_DEBUG_TABLE
#define QLALR_NO_DEBUG_DOT
+using namespace Qt::StringLiterals;
+
static void help_me ()
{
qerr() << "Usage: qlalr [options] [input file name]" << Qt::endl
@@ -51,7 +28,8 @@ static void help_me ()
<< " --no-debug\t\tno debug information" << Qt::endl
<< " --no-lines\t\tno #line directives" << Qt::endl
<< " --dot\t\t\tgenerate a graph" << Qt::endl
- << " --qt\t\tadd the Qt copyright header and Qt-specific types and macros" << Qt::endl
+ << " --qt\t\t\tadd the Qt copyright header and Qt-specific types and macros" << Qt::endl
+ << " --exit-on-warn\texit with status code 2 on warning" << Qt::endl
<< Qt::endl;
exit (0);
}
@@ -65,28 +43,32 @@ int main (int argc, char *argv[])
bool no_lines = false;
bool debug_info = true;
bool qt_copyright = false;
+ bool warnings_are_errors = false;
QString file_name;
const QStringList args = app.arguments().mid(1);
for (const QString &arg : args) {
- if (arg == QLatin1String ("-h") || arg == QLatin1String ("--help"))
+ if (arg == "-h"_L1 || arg == "--help"_L1)
help_me ();
- else if (arg == QLatin1String ("-v") || arg == QLatin1String ("--verbose"))
+ else if (arg == "-v"_L1 || arg == "--verbose"_L1)
generate_report = true;
- else if (arg == QLatin1String ("--dot"))
+ else if (arg == "--dot"_L1)
generate_dot = true;
- else if (arg == QLatin1String ("--no-lines"))
+ else if (arg == "--no-lines"_L1)
no_lines = true;
- else if (arg == QLatin1String ("--no-debug"))
+ else if (arg == "--no-debug"_L1)
debug_info = false;
- else if (arg == QLatin1String ("--qt"))
+ else if (arg == "--qt"_L1)
qt_copyright = true;
+ else if (arg == "--exit-on-warn"_L1)
+ warnings_are_errors = true;
+
else if (file_name.isEmpty ())
file_name = arg;
@@ -127,6 +109,7 @@ int main (int argc, char *argv[])
CppGenerator gen (p, grammar, aut, generate_report);
gen.setDebugInfo (debug_info);
gen.setCopyright (qt_copyright);
+ gen.setWarningsAreErrors (warnings_are_errors);
gen ();
if (generate_dot)
@@ -150,19 +133,19 @@ QString Recognizer::expand (const QString &text) const
if (_M_grammar->start != _M_grammar->names.end ())
{
- code = code.replace (QLatin1String("$start_id"), QString::number (std::distance (_M_grammar->names.begin (), _M_grammar->start)));
- code = code.replace (QLatin1String("$start"), *_M_grammar->start);
+ code = code.replace ("$start_id"_L1, QString::number (std::distance (_M_grammar->names.begin (), _M_grammar->start)));
+ code = code.replace ("$start"_L1, *_M_grammar->start);
}
- code = code.replace (QLatin1String("$header"), _M_grammar->table_name.toLower () + QLatin1String("_p.h"));
+ code = code.replace ("$header"_L1, _M_grammar->table_name.toLower () + "_p.h"_L1);
- code = code.replace (QLatin1String("$table"), _M_grammar->table_name);
- code = code.replace (QLatin1String("$parser"), _M_grammar->table_name);
+ code = code.replace ("$table"_L1, _M_grammar->table_name);
+ code = code.replace ("$parser"_L1, _M_grammar->table_name);
if (_M_current_rule != _M_grammar->rules.end ())
{
- code = code.replace (QLatin1String("$rule_number"), QString::number (std::distance (_M_grammar->rules.begin (), _M_current_rule)));
- code = code.replace (QLatin1String("$rule"), *_M_current_rule->lhs);
+ code = code.replace ("$rule_number"_L1, QString::number (std::distance (_M_grammar->rules.begin (), _M_current_rule)));
+ code = code.replace ("$rule"_L1, *_M_current_rule->lhs);
}
return code;
diff --git a/src/tools/qlalr/parsetable.cpp b/src/tools/qlalr/parsetable.cpp
index 9e71acebb4..c144b6f7a6 100644
--- a/src/tools/qlalr/parsetable.cpp
+++ b/src/tools/qlalr/parsetable.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "parsetable.h"
diff --git a/src/tools/qlalr/parsetable.h b/src/tools/qlalr/parsetable.h
index 2bf477011b..9d3a97026b 100644
--- a/src/tools/qlalr/parsetable.h
+++ b/src/tools/qlalr/parsetable.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the utils of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PARSETABLE_H
#define PARSETABLE_H
diff --git a/src/tools/qlalr/recognizer.cpp b/src/tools/qlalr/recognizer.cpp
index 760a094460..65862f7b02 100644
--- a/src/tools/qlalr/recognizer.cpp
+++ b/src/tools/qlalr/recognizer.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR project on Qt Labs.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "recognizer.h"
@@ -34,6 +9,8 @@
#include <cstring>
#include <cctype>
+using namespace Qt::StringLiterals;
+
Recognizer::Recognizer (Grammar *grammar, bool no_lines):
tos(0),
stack_size(0),
@@ -83,9 +60,9 @@ int Recognizer::nextToken()
{
inp(); // skip "
text.clear ();
- while (! ch.isNull () && ch != QLatin1Char ('"'))
+ while (!ch.isNull () && ch != u'"')
{
- if (ch == QLatin1Char ('\\'))
+ if (ch == u'\\')
{
text += ch;
inp();
@@ -94,7 +71,7 @@ int Recognizer::nextToken()
inp ();
}
- if (ch == QLatin1Char ('"'))
+ if (ch == u'"')
inp ();
else
qerr() << _M_input_file << ":" << _M_line << ": Warning. Expected `\"'" << Qt::endl;
@@ -103,11 +80,11 @@ int Recognizer::nextToken()
return (token = STRING_LITERAL);
}
- else if (ch.isLetterOrNumber () || ch == QLatin1Char ('_'))
+ else if (ch.isLetterOrNumber () || ch == u'_')
{
text.clear ();
do { text += ch; inp (); }
- while (ch.isLetterOrNumber () || ch == QLatin1Char ('_') || ch == QLatin1Char ('.'));
+ while (ch.isLetterOrNumber () || ch == u'_' || ch == u'.');
_M_current_value = text;
return (token = ID);
}
@@ -120,33 +97,33 @@ int Recognizer::nextToken()
while (ch.isSpace ());
do { text += ch; inp (); }
- while (ch.isLetterOrNumber () || ch == QLatin1Char ('_') || ch == QLatin1Char ('-'));
+ while (ch.isLetterOrNumber () || ch == u'_' || ch == u'-');
- if (text == QLatin1String("token_prefix"))
+ if (text == "token_prefix"_L1)
return (token = TOKEN_PREFIX);
- else if (text == QLatin1String("merged_output"))
+ else if (text == "merged_output"_L1)
return (token = MERGED_OUTPUT);
- else if (text == QLatin1String("token"))
+ else if (text == "token"_L1)
return (token = TOKEN);
- else if (text == QLatin1String("start"))
+ else if (text == "start"_L1)
return (token = START);
- else if (text == QLatin1String("parser"))
+ else if (text == "parser"_L1)
return (token = PARSER);
- else if (text == QLatin1String("decl"))
+ else if (text == "decl"_L1)
return (token = DECL_FILE);
- else if (text == QLatin1String("impl"))
+ else if (text == "impl"_L1)
return (token = IMPL_FILE);
- else if (text == QLatin1String("expect"))
+ else if (text == "expect"_L1)
return (token = EXPECT);
- else if (text == QLatin1String("expect-rr"))
+ else if (text == "expect-rr"_L1)
return (token = EXPECT_RR);
- else if (text == QLatin1String("left"))
+ else if (text == "left"_L1)
return (token = LEFT);
- else if (text == QLatin1String("right"))
+ else if (text == "right"_L1)
return (token = RIGHT);
- else if (text == QLatin1String("nonassoc"))
+ else if (text == "nonassoc"_L1)
return (token = NONASSOC);
- else if (text == QLatin1String("prec"))
+ else if (text == "prec"_L1)
return (token = PREC);
else
{
@@ -158,30 +135,30 @@ int Recognizer::nextToken()
inp ();
- if (token == '-' && ch == QLatin1Char ('-'))
+ if (token == '-' && ch == u'-')
{
do { inp (); }
- while (! ch.isNull () && ch != QLatin1Char ('\n'));
+ while (!ch.isNull () && ch != u'\n');
goto Lagain;
}
- else if (token == ':' && ch == QLatin1Char (':'))
+ else if (token == ':' && ch == u':')
{
inp ();
- if (ch != QLatin1Char ('='))
+ if (ch != u'=')
return (token = ERROR);
inp ();
return (token = COLON);
}
- else if (token == '/' && ch == QLatin1Char (':'))
+ else if (token == '/' && ch == u':')
{
_M_action_line = _M_line;
text.clear ();
if (! _M_no_lines)
- text += QLatin1String("\n#line ") + QString::number(_M_action_line) +
- QLatin1String(" \"") + QDir::fromNativeSeparators(_M_input_file) + QLatin1String("\"\n");
+ text += "\n#line "_L1 + QString::number(_M_action_line) +
+ " \""_L1 + QDir::fromNativeSeparators(_M_input_file) + "\"\n"_L1;
inp (); // skip ':'
forever
@@ -191,13 +168,13 @@ int Recognizer::nextToken()
token = ch.unicode ();
inp ();
- if (token == ':' && ch == QLatin1Char ('/'))
+ if (token == ':' && ch == u'/')
break;
text += QLatin1Char (token);
}
- if (ch != QLatin1Char ('/'))
+ if (ch != u'/')
return (token = ERROR);
inp ();
@@ -208,18 +185,18 @@ int Recognizer::nextToken()
return (token = DECL);
}
else
- text += QLatin1String (":/");
+ text += ":/"_L1;
}
}
- else if (token == '/' && ch == QLatin1Char ('.'))
+ else if (token == '/' && ch == u'.')
{
_M_action_line = _M_line;
text.clear ();
if (! _M_no_lines)
- text += QLatin1String("\n#line ") + QString::number(_M_action_line) +
- QLatin1String(" \"") + QDir::fromNativeSeparators(_M_input_file) + QLatin1String("\"\n");
+ text += "\n#line "_L1 + QString::number(_M_action_line) +
+ " \""_L1 + QDir::fromNativeSeparators(_M_input_file) + "\"\n"_L1;
inp (); // skip ':'
@@ -230,13 +207,13 @@ int Recognizer::nextToken()
token = ch.unicode ();
inp ();
- if (token == '.' && ch == QLatin1Char ('/'))
+ if (token == '.' && ch == u'/')
break;
text += QLatin1Char (token);
}
- if (ch != QLatin1Char ('/'))
+ if (ch != u'/')
return (token = ERROR);
inp ();
@@ -247,7 +224,7 @@ int Recognizer::nextToken()
return (token = IMPL);
}
else
- text += QLatin1String ("");
+ text += ""_L1;
}
}
diff --git a/src/tools/qlalr/recognizer.h b/src/tools/qlalr/recognizer.h
index 001de3bcf1..31d606e657 100644
--- a/src/tools/qlalr/recognizer.h
+++ b/src/tools/qlalr/recognizer.h
@@ -1,30 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QLALR project on Qt Labs.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef RECOGNIZER_H
+#define RECOGNIZER_H
#include "grammar_p.h"
@@ -63,7 +41,7 @@ protected: // scanner
{
ch = *_M_currentChar++;
- if (ch == QLatin1Char('\n'))
+ if (ch == u'\n')
++_M_line;
}
else
@@ -97,3 +75,5 @@ protected:
QString _M_current_value;
bool _M_no_lines;
};
+
+#endif // RECOGNIZER_H
diff --git a/src/tools/qtpaths/CMakeLists.txt b/src/tools/qtpaths/CMakeLists.txt
index 77b16f5f1e..d64caeb3c2 100644
--- a/src/tools/qtpaths/CMakeLists.txt
+++ b/src/tools/qtpaths/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from qtpaths.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qtpaths App:
@@ -6,15 +7,16 @@
qt_get_tool_target_name(target_name qtpaths)
qt_internal_add_tool(${target_name}
+ TRY_RUN
TARGET_DESCRIPTION "Qt tool that provides the standard paths of the Qt framework"
TOOLS_TARGET Core
INSTALL_VERSIONED_LINK
SOURCES
qtpaths.cpp
DEFINES
- QT_NO_FOREACH
QTPATHS_VERSION_STR="2.0"
)
+qt_internal_return_unless_building_tools()
## Scopes:
#####################################################################
@@ -22,7 +24,6 @@ qt_internal_add_tool(${target_name}
qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_settings
LIBRARIES
QtLibraryInfo
- Qt::CorePrivate
)
if(WIN32 AND TARGET ${target_name})
diff --git a/src/tools/qtpaths/qtpaths.cpp b/src/tools/qtpaths/qtpaths.cpp
index 667b594b67..71f9fe4349 100644
--- a/src/tools/qtpaths/qtpaths.cpp
+++ b/src/tools/qtpaths/qtpaths.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
- * *
- ** Copyright (C) 2016 Sune Vuorela <sune@kde.org>
- ** Contact: http://www.qt-project.org/
- **
- ** This file is part of the tools applications of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:BSD$
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and The Qt Company. For licensing terms
- ** and conditions see https://www.qt.io/terms-conditions. For further
- ** information use the contact form at https://www.qt.io/contact-us.
- **
- ** BSD License Usage
- ** Alternatively, you may use this file under the terms of the BSD license
- ** as follows:
- **
- ** "Redistribution and use in source and binary forms, with or without
- ** modification, are permitted provided that the following conditions are
- ** met:
- ** * Redistributions of source code must retain the above copyright
- ** notice, this list of conditions and the following disclaimer.
- ** * Redistributions in binary form must reproduce the above copyright
- ** notice, this list of conditions and the following disclaimer in
- ** the documentation and/or other materials provided with the
- ** distribution.
- ** * Neither the name of The Qt Company Ltd nor the names of its
- ** contributors may be used to endorse or promote products derived
- ** from this software without specific prior written permission.
- **
- **
- ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
- **
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
+// Copyright (C) 2016 Sune Vuorela <sune@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QCoreApplication>
#include <QCommandLineParser>
@@ -108,9 +61,6 @@ static const StringEnum lookupTableData[] = {
{ "ApplicationsLocation", QStandardPaths::ApplicationsLocation, false },
{ "CacheLocation", QStandardPaths::CacheLocation, true },
{ "ConfigLocation", QStandardPaths::ConfigLocation, false },
-#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
- { "DataLocation", QStandardPaths::DataLocation, true },
-#endif
{ "DesktopLocation", QStandardPaths::DesktopLocation, false },
{ "DocumentsLocation", QStandardPaths::DocumentsLocation, false },
{ "DownloadLocation", QStandardPaths::DownloadLocation, false },
@@ -118,11 +68,15 @@ static const StringEnum lookupTableData[] = {
{ "GenericCacheLocation", QStandardPaths::GenericCacheLocation, false },
{ "GenericConfigLocation", QStandardPaths::GenericConfigLocation, false },
{ "GenericDataLocation", QStandardPaths::GenericDataLocation, false },
+ { "GenericStateLocation", QStandardPaths::GenericStateLocation, false },
{ "HomeLocation", QStandardPaths::HomeLocation, false },
{ "MoviesLocation", QStandardPaths::MoviesLocation, false },
{ "MusicLocation", QStandardPaths::MusicLocation, false },
{ "PicturesLocation", QStandardPaths::PicturesLocation, false },
+ { "PublicShareLocation", QStandardPaths::PublicShareLocation, false },
{ "RuntimeLocation", QStandardPaths::RuntimeLocation, false },
+ { "StateLocation", QStandardPaths::StateLocation, true },
+ { "TemplatesLocation", QStandardPaths::TemplatesLocation, false },
{ "TempLocation", QStandardPaths::TempLocation, false }
};
@@ -145,7 +99,7 @@ static QStringList types()
static const StringEnum &parseLocationOrError(const QString &locationString)
{
for (const StringEnum &se : lookupTableData)
- if (locationString == QLatin1String(se.stringvalue))
+ if (locationString == QLatin1StringView(se.stringvalue))
return se;
QString message = QStringLiteral("Unknown location: %1");
@@ -168,6 +122,7 @@ static QString searchStringOrError(QCommandLineParser *parser)
int main(int argc, char **argv)
{
+ QString qtconfManualPath;
QCoreApplication app(argc, argv);
app.setApplicationVersion(QTPATHS_VERSION_STR);
@@ -265,13 +220,14 @@ int main(int argc, char **argv)
#if QT_CONFIG(settings)
if (parser.isSet(qtconf)) {
- QLibraryInfoPrivate::qtconfManualPath = parser.value(qtconf);
+ qtconfManualPath = parser.value(qtconf);
+ QLibraryInfoPrivate::setQtconfManualPath(&qtconfManualPath);
}
#endif
QStringList results;
if (parser.isSet(qtversion)) {
- QString qtversionstring = QString::fromLatin1(qVersion());
+ QString qtversionstring = QString::fromLatin1(QT_VERSION_STR);
results << qtversionstring;
}
@@ -295,6 +251,10 @@ int main(int argc, char **argv)
results << typesList.join('\n');
}
+ QT_WARNING_PUSH
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1300 && Q_CC_GNU < 1500
+ QT_WARNING_DISABLE_GCC("-Wdangling-reference")
+#endif
if (parser.isSet(display)) {
const StringEnum &location = parseLocationOrError(parser.value(display));
QString text = QStandardPaths::displayName(location.enumvalue);
@@ -346,6 +306,7 @@ int main(int argc, char **argv)
QStringList paths = QStandardPaths::locateAll(location.enumvalue, searchitem, QStandardPaths::LocateFile);
results << location.mapName(paths.join(pathsep));
}
+ QT_WARNING_POP
#if !QT_CONFIG(settings)
if (parser.isSet(query) || parser.isSet(qtconf) || parser.isSet(queryformat)) {
@@ -383,7 +344,7 @@ int main(int argc, char **argv)
if (results.isEmpty()) {
parser.showHelp();
} else if (results.size() == 1) {
- const QString &item = results.first();
+ const QString &item = results.constFirst();
message(item);
if (item.isEmpty())
return EXIT_FAILURE;
diff --git a/src/tools/qvkgen/CMakeLists.txt b/src/tools/qvkgen/CMakeLists.txt
index 75ea64fda6..0f68968fd3 100644
--- a/src/tools/qvkgen/CMakeLists.txt
+++ b/src/tools/qvkgen/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from qvkgen.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qvkgen Tool:
@@ -8,13 +9,10 @@ qt_get_tool_target_name(target_name qvkgen)
qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "Qt Vulkan Header Generator"
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Gui # special case
+ TOOLS_TARGET Gui
SOURCES
qvkgen.cpp
- PUBLIC_LIBRARIES
- Qt::Core # special case
+ LIBRARIES
+ Qt::Core
)
-
-#### Keys ignored in scope 1:.:.:qvkgen.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt Vulkan Header Generator"
-# _OPTION = "host_build"
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/qvkgen/qvkgen.cpp b/src/tools/qvkgen/qvkgen.cpp
index 798cbb7932..3ef7aa56f6 100644
--- a/src/tools/qvkgen/qvkgen.cpp
+++ b/src/tools/qvkgen/qvkgen.cpp
@@ -1,42 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
#include <QtCore/qxmlstream.h>
// generate wrappers for core functions from the following versions
static const QStringList VERSIONS = {
QStringLiteral("VK_VERSION_1_0"), // must be the first and always present
QStringLiteral("VK_VERSION_1_1"),
- QStringLiteral("VK_VERSION_1_2")
+ QStringLiteral("VK_VERSION_1_2"),
+ QStringLiteral("VK_VERSION_1_3")
};
class VkSpecParser
@@ -234,11 +211,11 @@ VkSpecParser::TypedName VkSpecParser::parseParamOrProto(const QString &tag)
} else {
auto text = m_reader.text().trimmed();
if (!text.isEmpty()) {
- if (text.startsWith(QLatin1Char('['))) {
+ if (text.startsWith(u'[')) {
t.typeSuffix += text;
} else {
if (!t.type.isEmpty())
- t.type += QLatin1Char(' ');
+ t.type += u' ';
t.type += text;
}
}
@@ -266,7 +243,7 @@ QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr)
(className ? className : ""), (className ? "::" : ""),
qPrintable(c.cmd.name)));
if (!c.args.isEmpty()) {
- s += QLatin1Char('(');
+ s += u'(';
bool first = true;
for (const VkSpecParser::TypedName &a : c.args) {
if (!first)
@@ -274,10 +251,10 @@ QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr)
else
first = false;
s += QString::asprintf("%s%s%s%s", qPrintable(a.type),
- (a.type.endsWith(QLatin1Char('*')) ? "" : " "),
+ (a.type.endsWith(u'*') ? "" : " "),
qPrintable(a.name), qPrintable(a.typeSuffix));
}
- s += QLatin1Char(')');
+ s += u')';
}
return s;
}
@@ -291,7 +268,7 @@ QString funcCall(const VkSpecParser::Command &c, int idx)
qPrintable(c.cmd.name),
idx);
if (!c.args.isEmpty()) {
- s += QLatin1Char('(');
+ s += u'(';
bool first = true;
for (const VkSpecParser::TypedName &a : c.args) {
if (!first)
@@ -300,7 +277,7 @@ QString funcCall(const VkSpecParser::Command &c, int idx)
first = false;
s += a.name;
}
- s += QLatin1Char(')');
+ s += u')';
}
return s;
}
@@ -348,9 +325,13 @@ bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands,
"#ifndef QVULKANFUNCTIONS_H\n"
"#define QVULKANFUNCTIONS_H\n"
"\n"
+"#if 0\n"
+"#pragma qt_no_master_include\n"
+"#endif\n"
+"\n"
"#include <QtGui/qtguiglobal.h>\n"
"\n"
-"#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)\n"
+"#if QT_CONFIG(vulkan) || defined(Q_QDOC)\n"
"\n"
"#ifndef VK_NO_PROTOTYPES\n"
"#define VK_NO_PROTOTYPES\n"
@@ -395,7 +376,7 @@ bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands,
"\n"
"QT_END_NAMESPACE\n"
"\n"
-"#endif // QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)\n"
+"#endif // QT_CONFIG(vulkan) || defined(Q_QDOC)\n"
"\n"
"#endif // QVULKANFUNCTIONS_H\n";
@@ -509,22 +490,25 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
return false;
}
- static const char *s =
+ static const char s[] =
"%s\n"
"#include \"qvulkanfunctions_p.h\"\n"
"#include \"qvulkaninstance.h\"\n"
"\n"
+"#include <QtCore/private/qoffsetstringarray_p.h>\n"
+"\n"
"QT_BEGIN_NAMESPACE\n"
"\n%s"
"QVulkanFunctionsPrivate::QVulkanFunctionsPrivate(QVulkanInstance *inst)\n"
"{\n"
-" static const char *funcNames[] = {\n"
+" static constexpr auto funcNames = qOffsetStringArray(\n"
"%s\n"
-" };\n"
-" for (int i = 0; i < %d; ++i) {\n"
-" m_funcs[i] = inst->getInstanceProcAddr(funcNames[i]);\n"
+" );\n"
+" static_assert(std::extent_v<decltype(m_funcs)> == size_t(funcNames.count()));\n"
+" for (int i = 0; i < funcNames.count(); ++i) {\n"
+" m_funcs[i] = inst->getInstanceProcAddr(funcNames.at(i));\n"
" if (i < %d && !m_funcs[i])\n"
-" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames[i]);\n"
+" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames.at(i));\n"
" }\n"
"}\n"
"\n%s"
@@ -532,13 +516,14 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
"{\n"
" QVulkanFunctions *f = inst->functions();\n"
" Q_ASSERT(f);\n\n"
-" static const char *funcNames[] = {\n"
+" static constexpr auto funcNames = qOffsetStringArray(\n"
"%s\n"
-" };\n"
-" for (int i = 0; i < %d; ++i) {\n"
-" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames[i]);\n"
+" );\n"
+" static_assert(std::extent_v<decltype(m_funcs)> == size_t(funcNames.count()));\n"
+" for (int i = 0; i < funcNames.count(); ++i) {\n"
+" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames.at(i));\n"
" if (i < %d && !m_funcs[i])\n"
-" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames[i]);\n"
+" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames.at(i));\n"
" }\n"
"}\n"
"\n"
@@ -547,9 +532,7 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
QString devCmdWrapperStr;
QString instCmdWrapperStr;
int devIdx = 0;
- int devCount = 0;
int instIdx = 0;
- int instCount = 0;
QString devCmdNamesStr;
QString instCmdNamesStr;
int vulkan10DevCount = 0;
@@ -575,11 +558,6 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
*dst += QStringLiteral(" \"");
*dst += c.cmd.name;
*dst += QStringLiteral("\",\n");
-
- if (c.deviceLevel)
- devCount += 1;
- else
- instCount += 1;
}
if (version == QStringLiteral("VK_VERSION_1_0")) {
vulkan10InstCount = instIdx;
@@ -589,17 +567,17 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
devCmdWrapperStr += "#endif\n\n";
}
- if (devCmdNamesStr.count() > 2)
- devCmdNamesStr = devCmdNamesStr.left(devCmdNamesStr.count() - 2);
- if (instCmdNamesStr.count() > 2)
- instCmdNamesStr = instCmdNamesStr.left(instCmdNamesStr.count() - 2);
+ if (devCmdNamesStr.size() > 2)
+ devCmdNamesStr.chop(2);
+ if (instCmdNamesStr.size() > 2)
+ instCmdNamesStr.chop(2);
const QString str =
QString::asprintf(s, preamble.get(licHeaderFn).constData(),
instCmdWrapperStr.toUtf8().constData(),
- instCmdNamesStr.toUtf8().constData(), instCount, vulkan10InstCount,
+ instCmdNamesStr.toUtf8().constData(), vulkan10InstCount,
devCmdWrapperStr.toUtf8().constData(),
- devCmdNamesStr.toUtf8().constData(), devCount, vulkan10DevCount);
+ devCmdNamesStr.toUtf8().constData(), vulkan10DevCount);
f.write(str.toUtf8());
@@ -633,7 +611,7 @@ int main(int argc, char **argv)
QStringLiteral("vkGetInstanceProcAddr"),
QStringLiteral("vkEnumerateInstanceVersion")
};
- for (int i = 0; i < commands.count(); ++i) {
+ for (int i = 0; i < commands.size(); ++i) {
if (ignoredFuncs.contains(commands[i].cmd.name))
commands.remove(i--);
}
diff --git a/src/tools/rcc/CMakeLists.txt b/src/tools/rcc/CMakeLists.txt
index 30fa8f4824..35a72c43fe 100644
--- a/src/tools/rcc/CMakeLists.txt
+++ b/src/tools/rcc/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from rcc.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## rcc Tool:
@@ -6,9 +7,10 @@
qt_get_tool_target_name(target_name rcc)
qt_internal_add_tool(${target_name}
+ TRY_RUN
TARGET_DESCRIPTION "Qt Resource Compiler"
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Core # special case
+ TOOLS_TARGET Core
SOURCES
main.cpp
rcc.cpp rcc.h
@@ -16,25 +18,16 @@ qt_internal_add_tool(${target_name}
QT_NO_CAST_FROM_ASCII
QT_NO_FOREACH
QT_RCC
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
)
-
-#### Keys ignored in scope 1:.:.:rcc.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt Resource Compiler"
-# _OPTION = "host_build"
+qt_internal_return_unless_building_tools()
## Scopes:
#####################################################################
-qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_zstd AND NOT CMAKE_CROSSCOMPILING
- DEFINES
- QT_FEATURE_zstd=1
+qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_zstd
LIBRARIES
- ZSTD::ZSTD
-)
-
-qt_internal_extend_target(${target_name} CONDITION CMAKE_CROSSCOMPILING OR NOT QT_FEATURE_zstd
- DEFINES
- QT_FEATURE_zstd=-1
+ WrapZSTD::WrapZSTD
)
diff --git a/src/tools/rcc/main.cpp b/src/tools/rcc/main.cpp
index eca7991280..03709ccbd4 100644
--- a/src/tools/rcc/main.cpp
+++ b/src/tools/rcc/main.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <rcc.h>
@@ -49,6 +24,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
void dumpRecursive(const QDir &dir, QTextStream &out)
{
const QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
@@ -57,9 +34,9 @@ void dumpRecursive(const QDir &dir, QTextStream &out)
if (entry.isDir()) {
dumpRecursive(entry.filePath(), out);
} else {
- out << QLatin1String("<file>")
+ out << "<file>"_L1
<< entry.filePath()
- << QLatin1String("</file>\n");
+ << "</file>\n"_L1;
}
}
}
@@ -69,7 +46,7 @@ int createProject(const QString &outFileName)
QDir currentDir = QDir::current();
QString currentDirName = currentDir.dirName();
if (currentDirName.isEmpty())
- currentDirName = QLatin1String("root");
+ currentDirName = "root"_L1;
QFile file;
bool isOk = false;
@@ -87,14 +64,14 @@ int createProject(const QString &outFileName)
}
QTextStream out(&file);
- out << QLatin1String("<!DOCTYPE RCC><RCC version=\"1.0\">\n"
- "<qresource>\n");
+ out << "<!DOCTYPE RCC><RCC version=\"1.0\">\n"
+ "<qresource>\n"_L1;
- // use "." as dir to get relative file pathes
- dumpRecursive(QDir(QLatin1String(".")), out);
+ // use "." as dir to get relative file paths
+ dumpRecursive(QDir("."_L1), out);
- out << QLatin1String("</qresource>\n"
- "</RCC>\n");
+ out << "</qresource>\n"
+ "</RCC>\n"_L1;
return 0;
}
@@ -105,11 +82,11 @@ QString makefileEscape(const QString &filepath)
// Always use forward slashes
QString result = QDir::cleanPath(filepath);
// Spaces are escaped with a backslash
- result.replace(QLatin1Char(' '), QLatin1String("\\ "));
+ result.replace(u' ', "\\ "_L1);
// Pipes are escaped with a backslash
- result.replace(QLatin1Char('|'), QLatin1String("\\|"));
+ result.replace(u'|', "\\|"_L1);
// Dollars are escaped with a dollar
- result.replace(QLatin1Char('$'), QLatin1String("$$"));
+ result.replace(u'$', "$$"_L1);
return result;
}
@@ -118,16 +95,16 @@ void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &
{
QTextStream out(&iodev);
out << qPrintable(makefileEscape(targetName));
- out << QLatin1Char(':');
+ out << QChar(u':');
// Write depfile
for (int i = 0; i < depsList.size(); ++i) {
- out << QLatin1Char(' ');
+ out << QChar(u' ');
out << qPrintable(makefileEscape(depsList.at(i)));
}
- out << QLatin1Char('\n');
+ out << QChar(u'\n');
}
int runRcc(int argc, char *argv[])
@@ -139,7 +116,7 @@ int runRcc(int argc, char *argv[])
// If you use this code as an example for a translated app, make sure to translate the strings.
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
- parser.setApplicationDescription(QLatin1String("Qt Resource Compiler version " QT_VERSION_STR));
+ parser.setApplicationDescription("Qt Resource Compiler version " QT_VERSION_STR ""_L1);
parser.addHelpOption();
parser.addVersionOption();
@@ -233,9 +210,9 @@ int runRcc(int argc, char *argv[])
bool ok = false;
formatVersion = parser.value(formatVersionOption).toUInt(&ok);
if (!ok) {
- errorMsg = QLatin1String("Invalid format version specified");
+ errorMsg = "Invalid format version specified"_L1;
} else if (formatVersion < 1 || formatVersion > 3) {
- errorMsg = QLatin1String("Unsupported format version specified");
+ errorMsg = "Unsupported format version specified"_L1;
}
}
@@ -244,19 +221,22 @@ int runRcc(int argc, char *argv[])
library.setInitName(parser.value(nameOption));
if (parser.isSet(rootOption)) {
library.setResourceRoot(QDir::cleanPath(parser.value(rootOption)));
- if (library.resourceRoot().isEmpty()
- || library.resourceRoot().at(0) != QLatin1Char('/'))
- errorMsg = QLatin1String("Root must start with a /");
+ if (library.resourceRoot().isEmpty() || library.resourceRoot().at(0) != u'/')
+ errorMsg = "Root must start with a /"_L1;
}
if (parser.isSet(compressionAlgoOption))
library.setCompressionAlgorithm(RCCResourceLibrary::parseCompressionAlgorithm(parser.value(compressionAlgoOption), &errorMsg));
- if (formatVersion < 3 && library.compressionAlgorithm() == RCCResourceLibrary::CompressionAlgorithm::Zstd)
- errorMsg = QLatin1String("Zstandard compression requires format version 3 or higher");
- if (parser.isSet(nocompressOption))
- library.setCompressionAlgorithm(RCCResourceLibrary::CompressionAlgorithm::None);
if (parser.isSet(noZstdOption))
library.setNoZstd(true);
+ if (library.compressionAlgorithm() == RCCResourceLibrary::CompressionAlgorithm::Zstd) {
+ if (formatVersion < 3)
+ errorMsg = "Zstandard compression requires format version 3 or higher"_L1;
+ if (library.noZstd())
+ errorMsg = "--compression-algo=zstd and --no-zstd both specified."_L1;
+ }
+ if (parser.isSet(nocompressOption))
+ library.setCompressionAlgorithm(RCCResourceLibrary::CompressionAlgorithm::None);
if (parser.isSet(compressOption) && errorMsg.isEmpty()) {
int level = library.parseCompressionLevel(library.compressionAlgorithm(), parser.value(compressOption), &errorMsg);
library.setCompressLevel(level);
@@ -267,25 +247,25 @@ int runRcc(int argc, char *argv[])
library.setFormat(RCCResourceLibrary::Binary);
if (parser.isSet(generatorOption)) {
auto value = parser.value(generatorOption);
- if (value == QLatin1String("cpp")) {
+ if (value == "cpp"_L1) {
library.setFormat(RCCResourceLibrary::C_Code);
- } else if (value == QLatin1String("python")) {
+ } else if (value == "python"_L1) {
library.setFormat(RCCResourceLibrary::Python_Code);
- } else if (value == QLatin1String("python2")) { // ### fixme Qt 7: remove
+ } else if (value == "python2"_L1) { // ### fixme Qt 7: remove
qWarning("Format python2 is no longer supported, defaulting to python.");
library.setFormat(RCCResourceLibrary::Python_Code);
} else {
- errorMsg = QLatin1String("Invalid generator: ") + value;
+ errorMsg = "Invalid generator: "_L1 + value;
}
}
if (parser.isSet(passOption)) {
- if (parser.value(passOption) == QLatin1String("1"))
+ if (parser.value(passOption) == "1"_L1)
library.setFormat(RCCResourceLibrary::Pass1);
- else if (parser.value(passOption) == QLatin1String("2"))
+ else if (parser.value(passOption) == "2"_L1)
library.setFormat(RCCResourceLibrary::Pass2);
else
- errorMsg = QLatin1String("Pass number must be 1 or 2");
+ errorMsg = "Pass number must be 1 or 2"_L1;
}
if (parser.isSet(namespaceOption))
library.setUseNameSpace(!library.useNameSpace());
@@ -298,7 +278,7 @@ int runRcc(int argc, char *argv[])
const QStringList filenamesIn = parser.positionalArguments();
for (const QString &file : filenamesIn) {
- if (file == QLatin1String("-"))
+ if (file == "-"_L1)
continue;
else if (!QFile::exists(file)) {
qWarning("%s: File does not exist '%s'", argv[0], qPrintable(file));
@@ -323,7 +303,8 @@ int runRcc(int argc, char *argv[])
return 1;
}
QFile errorDevice;
- errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text);
+ if (!errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text))
+ return 1;
if (library.verbose())
errorDevice.write("Qt resource compiler\n");
@@ -350,7 +331,7 @@ int runRcc(int argc, char *argv[])
}
- if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
+ if (outFilename.isEmpty() || outFilename == "-"_L1) {
#ifdef Q_OS_WIN
// Make sure fwrite to stdout doesn't do LF->CRLF
if (library.format() == RCCResourceLibrary::Binary)
@@ -361,7 +342,12 @@ int runRcc(int argc, char *argv[])
mode &= ~QIODevice::Text;
#endif // Q_OS_WIN
// using this overload close() only flushes.
- out.open(stdout, mode);
+ if (!out.open(stdout, mode)) {
+ const QString msg = QString::fromLatin1("Unable to open standard output for writing: %1\n")
+ .arg(out.errorString());
+ errorDevice.write(msg.toUtf8());
+ return 1;
+ }
} else {
out.setFileName(outFilename);
if (!out.open(mode)) {
@@ -398,7 +384,7 @@ int runRcc(int argc, char *argv[])
QFile depout;
depout.setFileName(depFilename);
- if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
+ if (outFilename.isEmpty() || outFilename == "-"_L1) {
const QString msg = QString::fromUtf8("Unable to write depfile when outputting to stdout!\n");
errorDevice.write(msg.toUtf8());
return 1;
diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp
index 1825c77b53..444e9c4ae5 100644
--- a/src/tools/rcc/rcc.cpp
+++ b/src/tools/rcc/rcc.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "rcc.h"
@@ -33,7 +8,7 @@
#include <qdatetime.h>
#include <qdebug.h>
#include <qdir.h>
-#include <qdiriterator.h>
+#include <qdirlisting.h>
#include <qfile.h>
#include <qiodevice.h>
#include <qlocale.h>
@@ -50,6 +25,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
enum {
CONSTANT_USENAMESPACE = 1,
CONSTANT_COMPRESSLEVEL_DEFAULT = -1,
@@ -58,14 +35,6 @@ enum {
CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70
};
-#if QT_CONFIG(zstd) && QT_VERSION >= QT_VERSION_CHECK(6,0,0)
-# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zstd
-#elif !defined(QT_NO_COMPRESS)
-# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zlib
-#else
-# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::None
-#endif
-
void RCCResourceLibrary::write(const char *str, int len)
{
int n = m_out.size();
@@ -106,15 +75,18 @@ public:
CompressedZstd = 0x04
};
- RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(),
- QLocale::Language language = QLocale::C,
- QLocale::Territory territory = QLocale::AnyTerritory,
- uint flags = NoFlags,
- RCCResourceLibrary::CompressionAlgorithm compressAlgo = CONSTANT_COMPRESSALGO_DEFAULT,
- int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT,
- int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT,
- bool noZstd = false);
+
+ RCCFileInfo() = default;
+ RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language,
+ QLocale::Territory territory, uint flags,
+ RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel,
+ int compressThreshold, bool noZstd, bool isEmpty);
+
~RCCFileInfo();
+ RCCFileInfo(const RCCFileInfo &) = delete;
+ RCCFileInfo &operator=(const RCCFileInfo &) = delete;
+ RCCFileInfo(RCCFileInfo &&) = default;
+ RCCFileInfo &operator=(RCCFileInfo &&other) = delete;
QString resourceName() const;
@@ -123,41 +95,40 @@ public:
qint64 writeDataName(RCCResourceLibrary &, qint64 offset);
void writeDataInfo(RCCResourceLibrary &lib);
- int m_flags;
+ int m_flags = NoFlags;
+ QLocale::Language m_language = QLocale::C;
+ QLocale::Territory m_territory = QLocale::AnyTerritory;
QString m_name;
- QLocale::Language m_language;
- QLocale::Territory m_territory;
QFileInfo m_fileInfo;
- RCCFileInfo *m_parent;
+ RCCFileInfo *m_parent = nullptr;
QMultiHash<QString, RCCFileInfo *> m_children;
- RCCResourceLibrary::CompressionAlgorithm m_compressAlgo;
- int m_compressLevel;
- int m_compressThreshold;
-
- qint64 m_nameOffset;
- qint64 m_dataOffset;
- qint64 m_childOffset;
- bool m_noZstd;
+
+ RCCResourceLibrary::CompressionAlgorithm m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Best;
+ int m_compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT;
+ int m_compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT;
+ bool m_noZstd = false;
+ bool m_isEmpty = false;
+
+ qint64 m_nameOffset = 0;
+ qint64 m_dataOffset = 0;
+ qint64 m_childOffset = 0;
};
-RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
- QLocale::Language language, QLocale::Territory territory, uint flags,
- RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel, int compressThreshold,
- bool noZstd)
+RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language,
+ QLocale::Territory territory, uint flags,
+ RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel,
+ int compressThreshold, bool noZstd, bool isEmpty)
+ : m_flags(flags),
+ m_language(language),
+ m_territory(territory),
+ m_name(name),
+ m_fileInfo(fileInfo),
+ m_compressAlgo(compressAlgo),
+ m_compressLevel(compressLevel),
+ m_compressThreshold(compressThreshold),
+ m_noZstd(noZstd),
+ m_isEmpty(isEmpty)
{
- m_name = name;
- m_fileInfo = fileInfo;
- m_language = language;
- m_territory = territory;
- m_flags = flags;
- m_parent = nullptr;
- m_nameOffset = 0;
- m_dataOffset = 0;
- m_childOffset = 0;
- m_compressAlgo = compressAlgo;
- m_compressLevel = compressLevel;
- m_compressThreshold = compressThreshold;
- m_noZstd = noZstd;
}
RCCFileInfo::~RCCFileInfo()
@@ -169,8 +140,9 @@ QString RCCFileInfo::resourceName() const
{
QString resource = m_name;
for (RCCFileInfo *p = m_parent; p; p = p->m_parent)
- resource = resource.prepend(p->m_name + QLatin1Char('/'));
- return QLatin1Char(':') + resource;
+ resource = resource.prepend(p->m_name + u'/');
+ resource.prepend(u':');
+ return resource;
}
void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
@@ -229,7 +201,7 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
if (lib.formatVersion() >= 2) {
// last modified time stamp
- const QDateTime lastModified = m_fileInfo.lastModified();
+ const QDateTime lastModified = m_fileInfo.lastModified(QTimeZone::UTC);
quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
if (sourceDate != 0)
@@ -256,14 +228,18 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
//capture the offset
m_dataOffset = offset;
+ QByteArray data;
- //find the data to be written
- QFile file(m_fileInfo.absoluteFilePath());
- if (!file.open(QFile::ReadOnly)) {
- *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
- return 0;
+ if (!m_isEmpty) {
+ //find the data to be written
+ QFile file(m_fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly)) {
+ *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
+ return 0;
+ }
+
+ data = file.readAll();
}
- QByteArray data = file.readAll();
// Check if compression is useful for this file
if (data.size() != 0) {
@@ -402,7 +378,7 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
}
// write the length
- lib.writeNumber2(m_name.length());
+ lib.writeNumber2(m_name.size());
if (text || pass1)
lib.writeString("\n ");
else if (python)
@@ -419,14 +395,14 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
// write the m_name
const QChar *unicode = m_name.unicode();
- for (int i = 0; i < m_name.length(); ++i) {
+ for (int i = 0; i < m_name.size(); ++i) {
lib.writeNumber2(unicode[i].unicode());
if ((text || pass1) && i % 16 == 0)
lib.writeString("\n ");
else if (python && i % 16 == 0)
lib.writeString("\\\n");
}
- offset += m_name.length()*2;
+ offset += m_name.size()*2;
// done
if (text || pass1)
@@ -445,14 +421,15 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
///////////////////////////////////////////////////////////
RCCResourceLibrary::Strings::Strings() :
- TAG_RCC(QLatin1String("RCC")),
- TAG_RESOURCE(QLatin1String("qresource")),
- TAG_FILE(QLatin1String("file")),
- ATTRIBUTE_LANG(QLatin1String("lang")),
- ATTRIBUTE_PREFIX(QLatin1String("prefix")),
- ATTRIBUTE_ALIAS(QLatin1String("alias")),
- ATTRIBUTE_THRESHOLD(QLatin1String("threshold")),
- ATTRIBUTE_COMPRESS(QLatin1String("compress")),
+ TAG_RCC("RCC"_L1),
+ TAG_RESOURCE("qresource"_L1),
+ TAG_FILE("file"_L1),
+ ATTRIBUTE_LANG("lang"_L1),
+ ATTRIBUTE_PREFIX("prefix"_L1),
+ ATTRIBUTE_ALIAS("alias"_L1),
+ ATTRIBUTE_EMPTY("empty"_L1),
+ ATTRIBUTE_THRESHOLD("threshold"_L1),
+ ATTRIBUTE_COMPRESS("compress"_L1),
ATTRIBUTE_COMPRESSALGO(QStringLiteral("compression-algorithm"))
{
}
@@ -461,7 +438,7 @@ RCCResourceLibrary::RCCResourceLibrary(quint8 formatVersion)
: m_root(nullptr),
m_format(C_Code),
m_verbose(false),
- m_compressionAlgo(CONSTANT_COMPRESSALGO_DEFAULT),
+ m_compressionAlgo(CompressionAlgorithm::Best),
m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
m_treeOffset(0),
@@ -495,11 +472,22 @@ enum RCCXmlTag {
};
Q_DECLARE_TYPEINFO(RCCXmlTag, Q_PRIMITIVE_TYPE);
+static bool parseBoolean(QStringView value, QString *errorMsg)
+{
+ if (value.compare("true"_L1, Qt::CaseInsensitive) == 0)
+ return true;
+ if (value.compare("false"_L1, Qt::CaseInsensitive) == 0)
+ return false;
+
+ *errorMsg = QString::fromLatin1("Invalid value for boolean attribute: '%1'").arg(value);
+ return false;
+}
+
bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
const QString &fname, QString currentPath, bool listMode)
{
Q_ASSERT(m_errorDevice);
- const QChar slash = QLatin1Char('/');
+ const QChar slash = u'/';
if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
currentPath += slash;
@@ -510,6 +498,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
QLocale::Language language = QLocale::c().language();
QLocale::Territory territory = QLocale::c().territory();
QString alias;
+ bool empty = false;
auto compressAlgo = m_compressionAlgo;
int compressLevel = m_compressLevel;
int compressThreshold = m_compressThreshold;
@@ -520,12 +509,12 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
case QXmlStreamReader::StartElement:
if (reader.name() == m_strings.TAG_RCC) {
if (!tokens.isEmpty())
- reader.raiseError(QLatin1String("expected <RCC> tag"));
+ reader.raiseError("expected <RCC> tag"_L1);
else
tokens.push(RccTag);
} else if (reader.name() == m_strings.TAG_RESOURCE) {
if (tokens.isEmpty() || tokens.top() != RccTag) {
- reader.raiseError(QLatin1String("unexpected <RESOURCE> tag"));
+ reader.raiseError("unexpected <RESOURCE> tag"_L1);
} else {
tokens.push(ResourceTag);
@@ -537,7 +526,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
QLocale lang = QLocale(attribute);
language = lang.language();
- if (2 == attribute.length()) {
+ if (2 == attribute.size()) {
// Language only
territory = QLocale::AnyTerritory;
} else {
@@ -555,7 +544,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
}
} else if (reader.name() == m_strings.TAG_FILE) {
if (tokens.isEmpty() || tokens.top() != ResourceTag) {
- reader.raiseError(QLatin1String("unexpected <FILE> tag"));
+ reader.raiseError("unexpected <FILE> tag"_L1);
} else {
tokens.push(FileTag);
@@ -569,6 +558,11 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
compressThreshold = m_compressThreshold;
QString errorString;
+ if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
+ empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
+ else
+ empty = false;
+
if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
@@ -587,7 +581,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
reader.raiseError(errorString);
}
} else {
- reader.raiseError(QString(QLatin1String("unexpected tag: %1")).arg(reader.name().toString()));
+ reader.raiseError("unexpected tag: %1"_L1.arg(reader.name().toString()));
}
break;
@@ -596,17 +590,17 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
if (!tokens.isEmpty() && tokens.top() == RccTag)
tokens.pop();
else
- reader.raiseError(QLatin1String("unexpected closing tag"));
+ reader.raiseError("unexpected closing tag"_L1);
} else if (reader.name() == m_strings.TAG_RESOURCE) {
if (!tokens.isEmpty() && tokens.top() == ResourceTag)
tokens.pop();
else
- reader.raiseError(QLatin1String("unexpected closing tag"));
+ reader.raiseError("unexpected closing tag"_L1);
} else if (reader.name() == m_strings.TAG_FILE) {
if (!tokens.isEmpty() && tokens.top() == FileTag)
tokens.pop();
else
- reader.raiseError(QLatin1String("unexpected closing tag"));
+ reader.raiseError("unexpected closing tag"_L1);
}
break;
@@ -614,7 +608,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
if (reader.isWhitespace())
break;
if (tokens.isEmpty() || tokens.top() != FileTag) {
- reader.raiseError(QLatin1String("unexpected text"));
+ reader.raiseError("unexpected text"_L1);
} else {
QString fileName = reader.text().toString();
if (fileName.isEmpty()) {
@@ -626,7 +620,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
alias = fileName;
alias = QDir::cleanPath(alias);
- while (alias.startsWith(QLatin1String("../")))
+ while (alias.startsWith("../"_L1))
alias.remove(0, 3);
alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
@@ -640,13 +634,12 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
alias += slash;
QStringList filePaths;
- QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
- while (it.hasNext()) {
- it.next();
- if (it.fileName() == QLatin1String(".")
- || it.fileName() == QLatin1String(".."))
+ using F = QDirListing::IteratorFlag;
+ for (const auto &entry : QDirListing(dir, F::FollowSymlinks | F::Recursive)) {
+ const QString &fileName = entry.fileName();
+ if (fileName == "."_L1 || fileName == ".."_L1)
continue;
- filePaths.append(it.filePath());
+ filePaths.emplace_back(entry.filePath());
}
// make rcc output deterministic
@@ -660,7 +653,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
child.isDir() ? RCCFileInfo::Directory
: RCCFileInfo::NoFlags,
compressAlgo, compressLevel, compressThreshold,
- m_noZstd));
+ m_noZstd, empty));
if (!arc)
m_failedResources.push_back(child.fileName());
}
@@ -675,7 +668,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
compressAlgo,
compressLevel,
compressThreshold,
- m_noZstd)
+ m_noZstd, empty)
);
if (!arc)
m_failedResources.push_back(absFileName);
@@ -714,15 +707,15 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
m_errorDevice->write(msg.toUtf8());
if (!listMode && m_format == Binary) {
// create dummy entry, otherwise loading with QResource will crash
- m_root = new RCCFileInfo(QString(), QFileInfo(),
- QLocale::C, QLocale::AnyTerritory, RCCFileInfo::Directory);
+ m_root = new RCCFileInfo{};
+ m_root->m_flags = RCCFileInfo::Directory;
}
}
return true;
}
-bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
+bool RCCResourceLibrary::addFile(const QString &alias, RCCFileInfo file)
{
Q_ASSERT(m_errorDevice);
if (file.m_fileInfo.size() > 0xffffffff) {
@@ -730,17 +723,21 @@ bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
m_errorDevice->write(msg.toUtf8());
return false;
}
- if (!m_root)
- m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyTerritory, RCCFileInfo::Directory);
+ if (!m_root) {
+ m_root = new RCCFileInfo{};
+ m_root->m_flags = RCCFileInfo::Directory;
+ }
RCCFileInfo *parent = m_root;
- const QStringList nodes = alias.split(QLatin1Char('/'));
+ const QStringList nodes = alias.split(u'/');
for (int i = 1; i < nodes.size()-1; ++i) {
const QString node = nodes.at(i);
if (node.isEmpty())
continue;
if (!parent->m_children.contains(node)) {
- RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyTerritory, RCCFileInfo::Directory);
+ RCCFileInfo *s = new RCCFileInfo{};
+ s->m_name = node;
+ s->m_flags = RCCFileInfo::Directory;
s->m_parent = parent;
parent->m_children.insert(node, s);
parent = s;
@@ -750,14 +747,14 @@ bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
}
const QString filename = nodes.at(nodes.size()-1);
- RCCFileInfo *s = new RCCFileInfo(file);
+ RCCFileInfo *s = new RCCFileInfo(std::move(file));
s->m_parent = parent;
auto cbegin = parent->m_children.constFind(filename);
auto cend = parent->m_children.constEnd();
for (auto it = cbegin; it != cend; ++it) {
if (it.key() == filename && it.value()->m_language == s->m_language &&
it.value()->m_territory == s->m_territory) {
- for (const QString &name : qAsConst(m_fileNames)) {
+ for (const QString &name : std::as_const(m_fileNames)) {
qWarning("%s: Warning: potential duplicate alias detected: '%s'",
qPrintable(name), qPrintable(filename));
}
@@ -793,8 +790,8 @@ bool RCCResourceLibrary::readFiles(bool listMode, QIODevice &errorDevice)
QFile fileIn;
QString fname = m_fileNames.at(i);
QString pwd;
- if (fname == QLatin1String("-")) {
- fname = QLatin1String("(stdin)");
+ if (fname == "-"_L1) {
+ fname = "(stdin)"_L1;
pwd = QDir::currentPath();
fileIn.setFileName(fname);
if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
@@ -845,7 +842,7 @@ QStringList RCCResourceLibrary::dataFiles() const
// Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
{
- const QChar slash = QLatin1Char('/');
+ const QChar slash = u'/';
const auto cend = m_root->m_children.constEnd();
for (auto it = m_root->m_children.constBegin(); it != cend; ++it) {
const RCCFileInfo *child = it.value();
@@ -862,27 +859,27 @@ RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap(
{
ResourceDataFileMap rc;
if (m_root)
- resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')), rc);
+ resourceDataFileMapRecursion(m_root, QString(u':'), rc);
return rc;
}
RCCResourceLibrary::CompressionAlgorithm RCCResourceLibrary::parseCompressionAlgorithm(QStringView value, QString *errorMsg)
{
- if (value == QLatin1String("best"))
+ if (value == "best"_L1)
return CompressionAlgorithm::Best;
- if (value == QLatin1String("zlib")) {
+ if (value == "zlib"_L1) {
#ifdef QT_NO_COMPRESS
- *errorMsg = QLatin1String("zlib support not compiled in");
+ *errorMsg = "zlib support not compiled in"_L1;
#else
return CompressionAlgorithm::Zlib;
#endif
- } else if (value == QLatin1String("zstd")) {
+ } else if (value == "zstd"_L1) {
#if QT_CONFIG(zstd)
return CompressionAlgorithm::Zstd;
#else
- *errorMsg = QLatin1String("Zstandard support not compiled in");
+ *errorMsg = "Zstandard support not compiled in"_L1;
#endif
- } else if (value != QLatin1String("none")) {
+ } else if (value != "none"_L1) {
*errorMsg = QString::fromLatin1("Unknown compression algorithm '%1'").arg(value);
}
@@ -932,13 +929,17 @@ bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &tempDevice, QIO
m_errorDevice->write("No data signature found\n");
return false;
}
+
+ if (c != pattern[i]) {
+ for (int k = 0; k < i; ++k)
+ outDevice.putChar(pattern[k]);
+ i = 0;
+ }
+
if (c == pattern[i]) {
++i;
} else {
- for (int k = 0; k < i; ++k)
- outDevice.putChar(pattern[k]);
outDevice.putChar(c);
- i = 0;
}
}
@@ -1097,6 +1098,10 @@ bool RCCResourceLibrary::writeHeader()
writeString("\n**\n");
writeString("** WARNING! All changes made in this file will be lost!\n");
writeString( "*****************************************************************************/\n\n");
+ writeString("#ifdef _MSC_VER\n"
+ "// disable informational message \"function ... selected for automatic inline expansion\"\n"
+ "#pragma warning (disable: 4711)\n"
+ "#endif\n\n");
break;
case Python_Code:
writeString("# Resource object code (Python 3)\n");
@@ -1348,7 +1353,7 @@ bool RCCResourceLibrary::writeInitializer()
//write("\nQT_BEGIN_NAMESPACE\n");
QString initNameStr = m_initName;
if (!initNameStr.isEmpty()) {
- initNameStr.prepend(QLatin1Char('_'));
+ initNameStr.prepend(u'_');
auto isAsciiLetterOrNumber = [] (QChar c) -> bool {
ushort ch = c.unicode();
return (ch >= '0' && ch <= '9') ||
@@ -1358,7 +1363,7 @@ bool RCCResourceLibrary::writeInitializer()
};
for (QChar &c : initNameStr) {
if (!isAsciiLetterOrNumber(c))
- c = QLatin1Char('_');
+ c = u'_';
}
}
QByteArray initName = initNameStr.toLatin1();
@@ -1377,7 +1382,9 @@ bool RCCResourceLibrary::writeInitializer()
"# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
"#endif\n\n");
- writeString("#ifdef QT_NAMESPACE\n"
+ writeString("#if defined(QT_INLINE_NAMESPACE)\n"
+ "inline namespace QT_NAMESPACE {\n"
+ "#elif defined(QT_NAMESPACE)\n"
"namespace QT_NAMESPACE {\n"
"#endif\n\n");
}
@@ -1478,6 +1485,11 @@ bool RCCResourceLibrary::writeInitializer()
writeString(" return 1;\n");
writeString("}\n\n");
+ // -Wexit-time-destructors was added to clang 3.0.0 in 2011.
+ writeString("#ifdef __clang__\n"
+ "# pragma clang diagnostic push\n"
+ "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
+ "#endif\n\n");
writeString("namespace {\n"
" struct initializer {\n");
@@ -1490,7 +1502,12 @@ bool RCCResourceLibrary::writeInitializer()
" ~initializer() { " + cleanResources + "(); }\n");
}
writeString(" } dummy;\n"
- "}\n");
+ "}\n\n");
+
+ writeString("#ifdef __clang__\n"
+ "# pragma clang diagnostic pop\n"
+ "#endif\n");
+
} else if (m_format == Binary) {
int i = 4;
diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h
index b6fcb21f5f..60af1c67cf 100644
--- a/src/tools/rcc/rcc.h
+++ b/src/tools/rcc/rcc.h
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// Note: A copy of this file is used in Qt Designer (qttools/src/designer/src/lib/shared/rcc_p.h)
@@ -121,13 +96,14 @@ private:
const QString ATTRIBUTE_LANG;
const QString ATTRIBUTE_PREFIX;
const QString ATTRIBUTE_ALIAS;
+ const QString ATTRIBUTE_EMPTY;
const QString ATTRIBUTE_THRESHOLD;
const QString ATTRIBUTE_COMPRESS;
const QString ATTRIBUTE_COMPRESSALGO;
};
friend class RCCFileInfo;
void reset();
- bool addFile(const QString &alias, const RCCFileInfo &file);
+ bool addFile(const QString &alias, RCCFileInfo file);
bool interpretResourceFile(QIODevice *inputDevice, const QString &file,
QString currentPath = QString(), bool listMode = false);
bool writeHeader();
diff --git a/src/tools/shared/depfile_shared.h b/src/tools/shared/depfile_shared.h
index 9fb9bbb863..411a7a1110 100644
--- a/src/tools/shared/depfile_shared.h
+++ b/src/tools/shared/depfile_shared.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QTBASE_DEPFILE_SHARED_H
#define QTBASE_DEPFILE_SHARED_H
diff --git a/src/tools/shared/shellquote_shared.h b/src/tools/shared/shellquote_shared.h
new file mode 100644
index 0000000000..7a9ab691da
--- /dev/null
+++ b/src/tools/shared/shellquote_shared.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef SHELLQUOTE_SHARED_H
+#define SHELLQUOTE_SHARED_H
+
+#include <QDir>
+#include <QRegularExpression>
+#include <QString>
+
+// Copy-pasted from qmake/library/ioutil.cpp
+inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
+{
+ for (int x = arg.size() - 1; x >= 0; --x) {
+ ushort c = arg.unicode()[x].unicode();
+ if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
+ return true;
+ }
+ return false;
+}
+
+static QString shellQuoteUnix(const QString &arg)
+{
+ // Chars that should be quoted (TM). This includes:
+ static const uchar iqm[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
+ 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
+ }; // 0-32 \'"$`<>|;&(){}*?#!~[]
+
+ if (!arg.size())
+ return QLatin1String("\"\"");
+
+ QString ret(arg);
+ if (hasSpecialChars(ret, iqm)) {
+ ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
+ ret.prepend(QLatin1Char('\''));
+ ret.append(QLatin1Char('\''));
+ }
+ return ret;
+}
+
+static QString shellQuoteWin(const QString &arg)
+{
+ // Chars that should be quoted (TM). This includes:
+ // - control chars & space
+ // - the shell meta chars "&()<>^|
+ // - the potential separators ,;=
+ static const uchar iqm[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
+ };
+
+ if (!arg.size())
+ return QLatin1String("\"\"");
+
+ QString ret(arg);
+ if (hasSpecialChars(ret, iqm)) {
+ // Quotes are escaped and their preceding backslashes are doubled.
+ // It's impossible to escape anything inside a quoted string on cmd
+ // level, so the outer quoting must be "suspended".
+ ret.replace(QRegularExpression(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ int i = ret.size();
+ while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
+ --i;
+ ret.insert(i, QLatin1Char('"'));
+ ret.prepend(QLatin1Char('"'));
+ }
+ return ret;
+}
+
+static QString shellQuote(const QString &arg)
+{
+ if (QDir::separator() == QLatin1Char('\\'))
+ return shellQuoteWin(arg);
+ else
+ return shellQuoteUnix(arg);
+}
+
+#endif // SHELLQUOTE_SHARED_H
diff --git a/src/tools/syncqt/CMakeLists.txt b/src/tools/syncqt/CMakeLists.txt
new file mode 100644
index 0000000000..b3ab091aa4
--- /dev/null
+++ b/src/tools/syncqt/CMakeLists.txt
@@ -0,0 +1,80 @@
+if(NOT QT_INTERNAL_AVOID_OVERRIDING_SYNCQT_CONFIG)
+ qt_internal_get_configs_for_flag_manipulation(configs)
+ qt_internal_remove_known_optimization_flags(LANGUAGES CXX CONFIGS ${configs})
+ # The /RTC1 flag is present in the default DEBUG flags list and contradicts -O2 but is not
+ # removed by qt_internal_remove_known_optimization_flags
+ qt_internal_remove_compiler_flags("/RTC1" LANGUAGES CXX CONFIGS ${configs})
+ qt_internal_get_optimize_full_flags(optimize_full_flags)
+ qt_internal_add_compiler_flags(LANGUAGES CXX CONFIGS ${configs} FLAGS "${optimize_full_flags}")
+
+ if(MSVC)
+ qt_internal_add_compiler_flags(LANGUAGES CXX CONFIGS ${configs} FLAGS "/EHsc")
+ else()
+ qt_internal_add_compiler_flags(LANGUAGES CXX CONFIGS ${configs} FLAGS "-fexceptions")
+ endif()
+
+ # Replace all linker flags with those we use in the RelWithDebInfo configuration
+ list(REMOVE_ITEM configs RELWITHDEBINFO)
+ foreach(config IN LISTS configs)
+ set(CMAKE_EXE_LINKER_FLAGS_${config} "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
+ endforeach()
+ qt_internal_skip_sanitizer()
+endif()
+
+set(compile_definitions
+ QT_VERSION_STR="${PROJECT_VERSION}"
+ QT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
+ QT_VERSION_MINOR=${PROJECT_VERSION_MINOR}
+ QT_VERSION_PATCH=${PROJECT_VERSION_PATCH}
+ QT_NAMESPACE="${QT_NAMESPACE}"
+)
+
+qt_get_tool_target_name(target_name syncqt)
+if(NOT QT_SYNC_HEADERS_AT_CONFIGURE_TIME)
+ qt_internal_add_tool(${target_name}
+ DEFINES ${compile_definitions}
+ EXCEPTIONS
+ TOOLS_TARGET Core
+ CORE_LIBRARY None
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
+ SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
+ )
+else()
+ set(config_type "")
+ if(NOT QT_INTERNAL_AVOID_OVERRIDING_SYNCQT_CONFIG)
+ set(config_type CONFIG RelWithDebInfo)
+ endif()
+
+ if(CMAKE_OSX_ARCHITECTURES)
+ set(osx_architectures "-DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES}")
+ endif()
+
+ if(CMAKE_OSX_SYSROOT)
+ set(osx_sysroot "-DCMAKE_OSX_SYSROOT:STRING=${CMAKE_OSX_SYSROOT}")
+ endif()
+ # Note: configure-time tools reserve the original tool name for the imported executable.
+ # To re-build syncqt use 'syncqt_build' target.
+ qt_internal_add_configure_time_tool(${target_name}
+ DEFINES ${compile_definitions}
+ TOOLS_TARGET Core
+ INSTALL_DIRECTORY "${INSTALL_LIBEXECDIR}"
+ CMAKE_FLAGS
+ -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=TRUE
+ -DCMAKE_CXX_STANDARD:STRING=17
+ # std::filesystem API is only available in macOS 10.15+
+ -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.15
+ "${osx_architectures}"
+ "${osx_sysroot}"
+ SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
+ ${config_type}
+ )
+endif()
+
+# Needs to be called at the end after all relevant target have created
+# when using qt_internal_add_tool.
+# Doesn't work if QT_SYNC_HEADERS_AT_CONFIGURE_TIME is TRUE.
+if(NOT QT_INTERNAL_AVOID_OVERRIDING_SYNCQT_CONFIG)
+ qt_internal_skip_linking_sanitizer()
+endif()
diff --git a/src/tools/syncqt/main.cpp b/src/tools/syncqt/main.cpp
new file mode 100644
index 0000000000..5df7b03fd5
--- /dev/null
+++ b/src/tools/syncqt/main.cpp
@@ -0,0 +1,1844 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*
+ * The tool generates deployment artifacts for the Qt builds such as:
+ * - CaMeL case header files named by public C++ symbols located in public module header files
+ * - Header file that contains the module version information, and named as <module>Vesion
+ * - LD version script if applicable
+ * - Aliases or copies of the header files sorted by the generic Qt-types: public/private/qpa
+ * and stored in the corresponding directories.
+ * Also the tool executes conformity checks on each header file if applicable, to make sure they
+ * follow rules that are relevant for their header type.
+ * The tool can be run in two modes: with either '-all' or '-headers' options specified. Depending
+ * on the selected mode, the tool either scans the filesystem to find header files or use the
+ * pre-defined list of header files.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <string_view>
+#include <cstring>
+#include <sstream>
+#include <filesystem>
+#include <unordered_map>
+#include <vector>
+#include <regex>
+#include <map>
+#include <set>
+#include <stdexcept>
+#include <array>
+
+enum ErrorCodes {
+ NoError = 0,
+ InvalidArguments,
+ SyncFailed,
+};
+
+// Enum contains the list of checks that can be executed on header files.
+enum HeaderChecks {
+ NoChecks = 0,
+ NamespaceChecks = 1, /* Checks if header file is wrapped with QT_<BEGIN|END>_NAMESPACE macros */
+ PrivateHeaderChecks = 2, /* Checks if the public header includes a private header */
+ IncludeChecks = 4, /* Checks if the real header file but not an alias is included */
+ WeMeantItChecks = 8, /* Checks if private header files contains 'We meant it' disclaimer */
+ PragmaOnceChecks = 16,
+ /* Checks that lead to the fatal error of the sync process: */
+ CriticalChecks = PrivateHeaderChecks | PragmaOnceChecks,
+ AllChecks = NamespaceChecks | CriticalChecks | IncludeChecks | WeMeantItChecks,
+};
+
+constexpr int LinkerScriptCommentAlignment = 55;
+
+static const std::regex GlobalHeaderRegex("^q(.*)global\\.h$");
+
+constexpr std::string_view ErrorMessagePreamble = "ERROR: ";
+constexpr std::string_view WarningMessagePreamble = "WARNING: ";
+
+// This comparator is used to sort include records in master header.
+// It's used to put q.*global.h file to the top of the list and sort all other files alphabetically.
+bool MasterHeaderIncludeComparator(const std::string &a, const std::string &b)
+{
+ std::smatch amatch;
+ std::smatch bmatch;
+
+ if (std::regex_match(a, amatch, GlobalHeaderRegex)) {
+ if (std::regex_match(b, bmatch, GlobalHeaderRegex)) {
+ return amatch[1].str().empty()
+ || (!bmatch[1].str().empty() && amatch[1].str() < bmatch[1].str());
+ }
+ return true;
+ } else if (std::regex_match(b, bmatch, GlobalHeaderRegex)) {
+ return false;
+ }
+
+ return a < b;
+};
+
+namespace utils {
+std::string asciiToLower(std::string s)
+{
+ std::transform(s.begin(), s.end(), s.begin(),
+ [](unsigned char c) { return (c >= 'A' && c <= 'Z') ? c | 0x20 : c; });
+ return s;
+}
+
+std::string asciiToUpper(std::string s)
+{
+ std::transform(s.begin(), s.end(), s.begin(),
+ [](unsigned char c) { return (c >= 'a' && c <= 'z') ? c & 0xdf : c; });
+ return s;
+}
+
+bool parseVersion(const std::string &version, int &major, int &minor)
+{
+ const size_t separatorPos = version.find('.');
+ if (separatorPos == std::string::npos || separatorPos == (version.size() - 1)
+ || separatorPos == 0)
+ return false;
+
+ try {
+ size_t pos = 0;
+ major = std::stoi(version.substr(0, separatorPos), &pos);
+ if (pos != separatorPos)
+ return false;
+
+ const size_t nextPart = separatorPos + 1;
+ pos = 0;
+ minor = std::stoi(version.substr(nextPart), &pos);
+ if (pos != (version.size() - nextPart))
+ return false;
+ } catch (const std::invalid_argument &) {
+ return false;
+ } catch (const std::out_of_range &) {
+ return false;
+ }
+
+ return true;
+}
+
+class DummyOutputStream : public std::ostream
+{
+ struct : public std::streambuf
+ {
+ int overflow(int c) override { return c; }
+ } buff;
+
+public:
+ DummyOutputStream() : std::ostream(&buff) { }
+} DummyOutput;
+
+void printInternalError()
+{
+ std::cerr << "Internal error. Please create bugreport at https://bugreports.qt.io "
+ "using 'Build tools: Other component.'"
+ << std::endl;
+}
+
+void printFilesystemError(const std::filesystem::filesystem_error &fserr, std::string_view errorMsg)
+{
+ std::cerr << errorMsg << ": " << fserr.path1() << ".\n"
+ << fserr.what() << "(" << fserr.code().value() << ")" << std::endl;
+}
+
+std::filesystem::path normilizedPath(const std::string &path)
+{
+ try {
+ auto result = std::filesystem::path(std::filesystem::weakly_canonical(path).generic_string());
+ return result;
+ } catch (const std::filesystem::filesystem_error &fserr) {
+ printFilesystemError(fserr, "Unable to normalize path");
+ throw;
+ }
+}
+
+bool createDirectories(const std::string &path, std::string_view errorMsg, bool *exists = nullptr)
+{
+ bool result = true;
+ try {
+ if (!std::filesystem::exists(path)) {
+ if (exists)
+ *exists = false;
+ std::filesystem::create_directories(path);
+ } else {
+ if (exists)
+ *exists = true;
+ }
+ } catch (const std::filesystem::filesystem_error &fserr) {
+ result = false;
+ std::cerr << errorMsg << ": " << path << ".\n"
+ << fserr.code().message() << "(" << fserr.code().value() << "):" << fserr.what()
+ << std::endl;
+ }
+ return result;
+}
+
+} // namespace utils
+
+using FileStamp = std::filesystem::file_time_type;
+
+class CommandLineOptions
+{
+ template<typename T>
+ struct CommandLineOption
+ {
+ CommandLineOption(T *_value, bool _isOptional = false)
+ : value(_value), isOptional(_isOptional)
+ {
+ }
+
+ T *value;
+ bool isOptional;
+ };
+
+public:
+ CommandLineOptions(int argc, char *argv[]) : m_isValid(parseArguments(argc, argv)) { }
+
+ bool isValid() const { return m_isValid; }
+
+ const std::string &moduleName() const { return m_moduleName; }
+
+ const std::string &sourceDir() const { return m_sourceDir; }
+
+ const std::string &binaryDir() const { return m_binaryDir; }
+
+ const std::string &includeDir() const { return m_includeDir; }
+
+ const std::string &privateIncludeDir() const { return m_privateIncludeDir; }
+
+ const std::string &qpaIncludeDir() const { return m_qpaIncludeDir; }
+
+ const std::string &rhiIncludeDir() const { return m_rhiIncludeDir; }
+
+ const std::string &ssgIncludeDir() const { return m_ssgIncludeDir; }
+
+ const std::string &stagingDir() const { return m_stagingDir; }
+
+ const std::string &versionScriptFile() const { return m_versionScriptFile; }
+
+ const std::set<std::string> &knownModules() const { return m_knownModules; }
+
+ const std::regex &qpaHeadersRegex() const { return m_qpaHeadersRegex; }
+
+ const std::regex &rhiHeadersRegex() const { return m_rhiHeadersRegex; }
+
+ const std::regex &ssgHeadersRegex() const { return m_ssgHeadersRegex; }
+
+ const std::regex &privateHeadersRegex() const { return m_privateHeadersRegex; }
+
+ const std::regex &publicNamespaceRegex() const { return m_publicNamespaceRegex; }
+
+ const std::set<std::string> &headers() const { return m_headers; }
+
+ const std::set<std::string> &generatedHeaders() const { return m_generatedHeaders; }
+
+ bool scanAllMode() const { return m_scanAllMode; }
+
+ bool isInternal() const { return m_isInternal; }
+
+ bool isNonQtModule() const { return m_isNonQtModule; }
+
+ bool printHelpOnly() const { return m_printHelpOnly; }
+
+ bool debug() const { return m_debug; }
+
+ bool copy() const { return m_copy; }
+
+ bool minimal() const { return m_minimal; }
+
+ bool showOnly() const { return m_showOnly; }
+
+ bool warningsAreErrors() const { return m_warningsAreErrors; }
+
+ void printHelp() const
+ {
+ std::cout << "Usage: syncqt -sourceDir <dir> -binaryDir <dir> -module <module name>"
+ " -includeDir <dir> -privateIncludeDir <dir> -qpaIncludeDir <dir> -rhiIncludeDir <dir> -ssgIncludeDir <dir>"
+ " -stagingDir <dir> <-headers <header list>|-all> [-debug]"
+ " [-versionScript <path>] [-qpaHeadersFilter <regex>] [-rhiHeadersFilter <regex>]"
+ " [-knownModules <module1> <module2>... <moduleN>]"
+ " [-nonQt] [-internal] [-copy]\n"
+ ""
+ "Mandatory arguments:\n"
+ " -module Module name.\n"
+ " -headers List of header files.\n"
+ " -all In 'all' mode syncqt scans source\n"
+ " directory for public qt headers and\n"
+ " artifacts not considering CMake source\n"
+ " tree. The main use cases are the \n"
+ " generating of documentation and creating\n"
+ " API review changes.\n"
+ " -sourceDir Module source directory.\n"
+ " -binaryDir Module build directory.\n"
+ " -includeDir Module include directory where the\n"
+ " generated header files will be located.\n"
+ " -privateIncludeDir Module include directory for the\n"
+ " generated private header files.\n"
+ " -qpaIncludeDir Module include directory for the \n"
+ " generated QPA header files.\n"
+ " -rhiIncludeDir Module include directory for the \n"
+ " generated RHI header files.\n"
+ " -ssgIncludeDir Module include directory for the \n"
+ " generated SSG header files.\n"
+ " -stagingDir Temporary staging directory to collect\n"
+ " artifacts that need to be installed.\n"
+ " -knownModules list of known modules. syncqt uses the\n"
+ " list to check the #include macros\n"
+ " consistency.\n"
+ "Optional arguments:\n"
+ " -internal Indicates that the module is internal.\n"
+ " -nonQt Indicates that the module is not a Qt\n"
+ " module.\n"
+ " -privateHeadersFilter Regex that filters private header files\n"
+ " from the list of 'headers'.\n"
+ " -qpaHeadersFilter Regex that filters qpa header files from.\n"
+ " the list of 'headers'.\n"
+ " -rhiHeadersFilter Regex that filters rhi header files from.\n"
+ " the list of 'headers'.\n"
+ " -ssgHeadersFilter Regex that filters ssg files from.\n"
+ " the list of 'headers'.\n"
+ " -publicNamespaceFilter Symbols that are in the specified\n"
+ " namespace.\n"
+ " are treated as public symbols.\n"
+ " -versionScript Generate linker version script by\n"
+ " provided path.\n"
+ " -debug Enable debug output.\n"
+ " -copy Copy header files instead of creating\n"
+ " aliases.\n"
+ " -minimal Do not create CaMeL case headers for the\n"
+ " public C++ symbols.\n"
+ " -showonly Show actions, but not perform them.\n"
+ " -warningsAreErrors Treat all warnings as errors.\n"
+ " -help Print this help.\n";
+ }
+
+private:
+ template<typename T>
+ [[nodiscard]] bool checkRequiredArguments(const std::unordered_map<std::string, T> &arguments)
+ {
+ bool ret = true;
+ for (const auto &argument : arguments) {
+ if (!argument.second.isOptional
+ && (!argument.second.value || argument.second.value->size()) == 0) {
+ std::cerr << "Missing argument: " << argument.first << std::endl;
+ ret = false;
+ }
+ }
+ return ret;
+ }
+
+ [[nodiscard]] bool parseArguments(int argc, char *argv[])
+ {
+ std::string qpaHeadersFilter;
+ std::string rhiHeadersFilter;
+ std::string ssgHeadersFilter;
+ std::string privateHeadersFilter;
+ std::string publicNamespaceFilter;
+ static std::unordered_map<std::string, CommandLineOption<std::string>> stringArgumentMap = {
+ { "-module", { &m_moduleName } },
+ { "-sourceDir", { &m_sourceDir } },
+ { "-binaryDir", { &m_binaryDir } },
+ { "-privateHeadersFilter", { &privateHeadersFilter, true } },
+ { "-qpaHeadersFilter", { &qpaHeadersFilter, true } },
+ { "-rhiHeadersFilter", { &rhiHeadersFilter, true } },
+ { "-ssgHeadersFilter", { &ssgHeadersFilter, true } },
+ { "-includeDir", { &m_includeDir } },
+ { "-privateIncludeDir", { &m_privateIncludeDir } },
+ { "-qpaIncludeDir", { &m_qpaIncludeDir } },
+ { "-rhiIncludeDir", { &m_rhiIncludeDir } },
+ { "-ssgIncludeDir", { &m_ssgIncludeDir } },
+ { "-stagingDir", { &m_stagingDir, true } },
+ { "-versionScript", { &m_versionScriptFile, true } },
+ { "-publicNamespaceFilter", { &publicNamespaceFilter, true } },
+ };
+
+ static const std::unordered_map<std::string, CommandLineOption<std::set<std::string>>>
+ listArgumentMap = {
+ { "-headers", { &m_headers, true } },
+ { "-generatedHeaders", { &m_generatedHeaders, true } },
+ { "-knownModules", { &m_knownModules, true } },
+ };
+
+ static const std::unordered_map<std::string, CommandLineOption<bool>> boolArgumentMap = {
+ { "-nonQt", { &m_isNonQtModule, true } }, { "-debug", { &m_debug, true } },
+ { "-help", { &m_printHelpOnly, true } },
+ { "-internal", { &m_isInternal, true } }, { "-all", { &m_scanAllMode, true } },
+ { "-copy", { &m_copy, true } }, { "-minimal", { &m_minimal, true } },
+ { "-showonly", { &m_showOnly, true } }, { "-showOnly", { &m_showOnly, true } },
+ { "-warningsAreErrors", { &m_warningsAreErrors, true } }
+ };
+
+ std::string *currentValue = nullptr;
+ std::set<std::string> *currentListValue = nullptr;
+
+ auto parseArgument = [&currentValue, &currentListValue](const std::string &arg) -> bool {
+ if (arg[0] == '-') {
+ currentValue = nullptr;
+ currentListValue = nullptr;
+ {
+ auto it = stringArgumentMap.find(arg);
+ if (it != stringArgumentMap.end()) {
+ if (it->second.value == nullptr) {
+ utils::printInternalError();
+ return false;
+ }
+ currentValue = it->second.value;
+ return true;
+ }
+ }
+
+ {
+ auto it = boolArgumentMap.find(arg);
+ if (it != boolArgumentMap.end()) {
+ if (it->second.value == nullptr) {
+ utils::printInternalError();
+ return false;
+ }
+ *(it->second.value) = true;
+ return true;
+ }
+ }
+
+ {
+ auto it = listArgumentMap.find(arg);
+ if (it != listArgumentMap.end()) {
+ if (it->second.value == nullptr) {
+ utils::printInternalError();
+ return false;
+ }
+ currentListValue = it->second.value;
+ currentListValue->insert(""); // Indicate that argument is provided
+ return true;
+ }
+ }
+
+ std::cerr << "Unknown argument: " << arg << std::endl;
+ return false;
+ }
+
+ if (currentValue != nullptr) {
+ *currentValue = arg;
+ currentValue = nullptr;
+ } else if (currentListValue != nullptr) {
+ currentListValue->insert(arg);
+ } else {
+ std::cerr << "Unknown argument: " << arg << std::endl;
+ return false;
+ }
+ return true;
+ };
+
+ for (int i = 1; i < argc; ++i) {
+ std::string arg(argv[i]);
+ if (arg.empty())
+ continue;
+
+ if (arg[0] == '@') {
+ std::ifstream ifs(arg.substr(1), std::ifstream::in);
+ if (!ifs.is_open()) {
+ std::cerr << "Unable to open rsp file: " << arg[0] << std::endl;
+ return false;
+ }
+ std::string argFromFile;
+ while (std::getline(ifs, argFromFile)) {
+ if (argFromFile.empty())
+ continue;
+ if (!parseArgument(argFromFile))
+ return false;
+ }
+ ifs.close();
+ continue;
+ }
+
+ if (!parseArgument(arg))
+ return false;
+ }
+
+ if (m_printHelpOnly)
+ return true;
+
+ if (!qpaHeadersFilter.empty())
+ m_qpaHeadersRegex = std::regex(qpaHeadersFilter);
+
+ if (!rhiHeadersFilter.empty())
+ m_rhiHeadersRegex = std::regex(rhiHeadersFilter);
+
+ if (!ssgHeadersFilter.empty())
+ m_ssgHeadersRegex = std::regex(ssgHeadersFilter);
+
+ if (!privateHeadersFilter.empty())
+ m_privateHeadersRegex = std::regex(privateHeadersFilter);
+
+ if (!publicNamespaceFilter.empty())
+ m_publicNamespaceRegex = std::regex(publicNamespaceFilter);
+
+ if (m_headers.empty() && !m_scanAllMode) {
+ std::cerr << "You need to specify either -headers or -all option." << std::endl;
+ return false;
+ }
+
+ if (!m_headers.empty() && m_scanAllMode) {
+ std::cerr << "Both -headers and -all are specified. Need to choose only one"
+ "operational mode." << std::endl;
+ return false;
+ }
+
+ for (const auto &argument : listArgumentMap)
+ argument.second.value->erase("");
+
+ bool ret = true;
+ ret &= checkRequiredArguments(stringArgumentMap);
+ ret &= checkRequiredArguments(listArgumentMap);
+
+ normilizePaths();
+
+ return ret;
+ }
+
+ // Convert all paths from command line to a generic one.
+ void normilizePaths()
+ {
+ static std::array<std::string *, 8> paths = {
+ &m_sourceDir, &m_binaryDir, &m_includeDir, &m_privateIncludeDir,
+ &m_qpaIncludeDir, &m_rhiIncludeDir, &m_stagingDir,
+ &m_versionScriptFile,
+ };
+ for (auto path : paths) {
+ if (!path->empty())
+ *path = utils::normilizedPath(*path).generic_string();
+ }
+ }
+
+ std::string m_moduleName;
+ std::string m_sourceDir;
+ std::string m_binaryDir;
+ std::string m_includeDir;
+ std::string m_privateIncludeDir;
+ std::string m_qpaIncludeDir;
+ std::string m_rhiIncludeDir;
+ std::string m_ssgIncludeDir;
+ std::string m_stagingDir;
+ std::string m_versionScriptFile;
+ std::set<std::string> m_knownModules;
+ std::set<std::string> m_headers;
+ std::set<std::string> m_generatedHeaders;
+ bool m_scanAllMode = false;
+ bool m_copy = false;
+ bool m_isNonQtModule = false;
+ bool m_isInternal = false;
+ bool m_printHelpOnly = false;
+ bool m_debug = false;
+ bool m_minimal = false;
+ bool m_showOnly = false;
+ bool m_warningsAreErrors = false;
+ std::regex m_qpaHeadersRegex;
+ std::regex m_rhiHeadersRegex;
+ std::regex m_ssgHeadersRegex;
+ std::regex m_privateHeadersRegex;
+ std::regex m_publicNamespaceRegex;
+
+ bool m_isValid;
+};
+
+class SyncScanner
+{
+ class SymbolDescriptor
+ {
+ public:
+ // Where the symbol comes from
+ enum SourceType {
+ Pragma = 0, // pragma qt_class is mentioned a header file
+ Declaration, // The symbol declaration inside a header file
+ MaxSourceType
+ };
+
+ void update(const std::string &file, SourceType type)
+ {
+ if (type < m_type) {
+ m_file = file;
+ m_type = type;
+ }
+ }
+
+ // The file that contains a symbol.
+ const std::string &file() const { return m_file; }
+
+ private:
+ SourceType m_type = MaxSourceType;
+ std::string m_file;
+ };
+ using SymbolContainer = std::unordered_map<std::string, SymbolDescriptor>;
+
+ struct ParsingResult
+ {
+ std::vector<std::string> versionScriptContent;
+ std::string requireConfig;
+ bool masterInclude = true;
+ };
+
+ CommandLineOptions *m_commandLineArgs = nullptr;
+
+ std::map<std::string /* header file name */, std::string /* header feature guard name */,
+ decltype(MasterHeaderIncludeComparator) *>
+ m_masterHeaderContents;
+
+ std::unordered_map<std::string /* the deprecated header name*/,
+ std::string /* the replacement */>
+ m_deprecatedHeaders;
+ std::vector<std::string> m_versionScriptContents;
+ std::set<std::string> m_producedHeaders;
+ std::vector<std::string> m_headerCheckExceptions;
+ SymbolContainer m_symbols;
+ std::ostream &scannerDebug() const
+ {
+ if (m_commandLineArgs->debug())
+ return std::cout;
+ return utils::DummyOutput;
+ }
+
+ enum { Active, Stopped, IgnoreNext, Ignore } m_versionScriptGeneratorState = Active;
+
+ std::filesystem::path m_outputRootName;
+ std::filesystem::path m_currentFile;
+ std::string m_currentFilename;
+ std::string m_currentFileString;
+ size_t m_currentFileLineNumber = 0;
+ bool m_currentFileInSourceDir = false;
+
+ enum FileType { PublicHeader = 0, PrivateHeader = 1, QpaHeader = 2, ExportHeader = 4, RhiHeader = 8, SsgHeader = 16 };
+ unsigned int m_currentFileType = PublicHeader;
+
+ int m_criticalChecks = CriticalChecks;
+ std::string_view m_warningMessagePreamble;
+
+public:
+ SyncScanner(CommandLineOptions *commandLineArgs)
+ : m_commandLineArgs(commandLineArgs),
+ m_masterHeaderContents(MasterHeaderIncludeComparator),
+ m_outputRootName(
+ std::filesystem::weakly_canonical(m_commandLineArgs->includeDir()).root_name()),
+ m_warningMessagePreamble(WarningMessagePreamble)
+ {
+ }
+
+ // The function converts the relative path to a header files to the absolute. It also makes the
+ // path canonical(removes '..' and '.' parts of the path). The source directory passed in
+ // '-sourceDir' command line argument is used as base path for relative paths to create the
+ // absolute path.
+ [[nodiscard]] std::filesystem::path makeHeaderAbsolute(const std::string &filename) const;
+
+ ErrorCodes sync()
+ {
+ if (m_commandLineArgs->warningsAreErrors()) {
+ m_criticalChecks = AllChecks;
+ m_warningMessagePreamble = ErrorMessagePreamble;
+ }
+
+ m_versionScriptGeneratorState =
+ m_commandLineArgs->versionScriptFile().empty() ? Stopped : Active;
+ auto error = NoError;
+
+ // In the scan all mode we ingore the list of header files that is specified in the
+ // '-headers' argument, and collect header files from the source directory tree.
+ if (m_commandLineArgs->scanAllMode()) {
+ for (auto const &entry :
+ std::filesystem::recursive_directory_iterator(m_commandLineArgs->sourceDir())) {
+
+ const bool isRegularFile = entry.is_regular_file();
+ const bool isHeaderFlag = isHeader(entry);
+ const bool isDocFileHeuristicFlag =
+ isDocFileHeuristic(entry.path().generic_string());
+ const bool shouldProcessHeader =
+ isRegularFile && isHeaderFlag && !isDocFileHeuristicFlag;
+ const std::string filePath = entry.path().generic_string();
+
+ if (shouldProcessHeader) {
+ scannerDebug() << "Processing header: " << filePath << std::endl;
+ if (!processHeader(makeHeaderAbsolute(filePath)))
+ error = SyncFailed;
+ } else {
+ scannerDebug()
+ << "Skipping processing header: " << filePath
+ << " isRegularFile: " << isRegularFile
+ << " isHeaderFlag: " << isHeaderFlag
+ << " isDocFileHeuristicFlag: " << isDocFileHeuristicFlag
+ << std::endl;
+ }
+ }
+ } else {
+ // Since the list of header file is quite big syncqt supports response files to avoid
+ // the issues with long command lines.
+ std::set<std::string> rspHeaders;
+ const auto &headers = m_commandLineArgs->headers();
+ for (auto it = headers.begin(); it != headers.end(); ++it) {
+ const auto &header = *it;
+ scannerDebug() << "Processing header: " << header << std::endl;
+ if (!processHeader(makeHeaderAbsolute(header))) {
+ error = SyncFailed;
+ }
+ }
+ for (const auto &header : rspHeaders) {
+ scannerDebug() << "Processing header: " << header << std::endl;
+ if (!processHeader(makeHeaderAbsolute(header)))
+ error = SyncFailed;
+ }
+ }
+
+ // No further processing in minimal mode.
+ if (m_commandLineArgs->minimal())
+ return error;
+
+ // Generate aliases for all unique symbols collected during the header files parsing.
+ for (auto it = m_symbols.begin(); it != m_symbols.end(); ++it) {
+ const std::string &filename = it->second.file();
+ if (!filename.empty()) {
+ if (generateQtCamelCaseFileIfContentChanged(
+ m_commandLineArgs->includeDir() + '/' + it->first, filename)) {
+ m_producedHeaders.insert(it->first);
+ } else {
+ error = SyncFailed;
+ }
+ }
+ }
+
+ // Generate the header file containing version information.
+ if (!m_commandLineArgs->isNonQtModule()) {
+ std::string moduleNameLower = utils::asciiToLower(m_commandLineArgs->moduleName());
+ std::string versionHeaderFilename(moduleNameLower + "version.h");
+ std::string versionHeaderCamel(m_commandLineArgs->moduleName() + "Version");
+ std::string versionFile = m_commandLineArgs->includeDir() + '/' + versionHeaderFilename;
+
+ std::error_code ec;
+ FileStamp originalStamp = std::filesystem::last_write_time(versionFile, ec);
+ if (ec)
+ originalStamp = FileStamp::clock::now();
+
+ if (generateVersionHeader(versionFile)) {
+ if (!generateAliasedHeaderFileIfTimestampChanged(
+ m_commandLineArgs->includeDir() + '/' + versionHeaderCamel,
+ versionHeaderFilename, originalStamp)) {
+ error = SyncFailed;
+ }
+ m_masterHeaderContents[versionHeaderFilename] = {};
+ m_producedHeaders.insert(versionHeaderFilename);
+ m_producedHeaders.insert(versionHeaderCamel);
+ } else {
+ error = SyncFailed;
+ }
+ }
+
+ if (!m_commandLineArgs->scanAllMode()) {
+ if (!m_commandLineArgs->isNonQtModule()) {
+ if (!generateDeprecatedHeaders())
+ error = SyncFailed;
+
+ if (!generateHeaderCheckExceptions())
+ error = SyncFailed;
+ }
+
+ if (!m_commandLineArgs->versionScriptFile().empty()) {
+ if (!generateLinkerVersionScript())
+ error = SyncFailed;
+ }
+ }
+
+ if (!m_commandLineArgs->isNonQtModule()) {
+ if (!generateMasterHeader())
+ error = SyncFailed;
+ }
+
+ if (!m_commandLineArgs->scanAllMode() && !m_commandLineArgs->stagingDir().empty()) {
+ // Copy the generated files to a spearate staging directory to make the installation
+ // process eaiser.
+ if (!copyGeneratedHeadersToStagingDirectory(m_commandLineArgs->stagingDir()))
+ error = SyncFailed;
+ }
+ return error;
+ }
+
+ // The function copies files, that were generated while the sync procedure to a staging
+ // directory. This is necessary to simplify the installation of the generated files.
+ [[nodiscard]] bool copyGeneratedHeadersToStagingDirectory(const std::string &outputDirectory,
+ bool skipCleanup = false)
+ {
+ bool result = true;
+ bool outDirExists = false;
+ if (!utils::createDirectories(outputDirectory, "Unable to create staging directory",
+ &outDirExists))
+ return false;
+
+ if (outDirExists && !skipCleanup) {
+ try {
+ for (const auto &entry :
+ std::filesystem::recursive_directory_iterator(outputDirectory)) {
+ if (m_producedHeaders.find(entry.path().filename().generic_string())
+ == m_producedHeaders.end()) {
+ // Check if header file came from another module as result of the
+ // cross-module deprecation before removing it.
+ std::string firstLine;
+ {
+ std::ifstream input(entry.path(), std::ifstream::in);
+ if (input.is_open()) {
+ std::getline(input, firstLine);
+ input.close();
+ }
+ }
+ if (firstLine.find("#ifndef DEPRECATED_HEADER_"
+ + m_commandLineArgs->moduleName())
+ == 0
+ || firstLine.find("#ifndef DEPRECATED_HEADER_") != 0)
+ std::filesystem::remove(entry.path());
+ }
+ }
+ } catch (const std::filesystem::filesystem_error &fserr) {
+ utils::printFilesystemError(fserr, "Unable to clean the staging directory");
+ return false;
+ }
+ }
+
+ for (const auto &header : m_producedHeaders) {
+ std::filesystem::path src(m_commandLineArgs->includeDir() + '/' + header);
+ std::filesystem::path dst(outputDirectory + '/' + header);
+ if (!m_commandLineArgs->showOnly())
+ result &= updateOrCopy(src, dst);
+ }
+ return result;
+ }
+
+ void resetCurrentFileInfoData(const std::filesystem::path &headerFile)
+ {
+ // This regex filters the generated '*exports.h' and '*exports_p.h' header files.
+ static const std::regex ExportsHeaderRegex("^q(.*)exports(_p)?\\.h$");
+
+ m_currentFile = headerFile;
+ m_currentFileLineNumber = 0;
+ m_currentFilename = m_currentFile.filename().generic_string();
+ m_currentFileType = PublicHeader;
+ m_currentFileString = m_currentFile.generic_string();
+ m_currentFileInSourceDir = m_currentFileString.find(m_commandLineArgs->sourceDir()) == 0;
+
+ if (isHeaderPrivate(m_currentFilename))
+ m_currentFileType = PrivateHeader;
+
+ if (isHeaderQpa(m_currentFilename))
+ m_currentFileType = QpaHeader | PrivateHeader;
+
+ if (isHeaderRhi(m_currentFilename))
+ m_currentFileType = RhiHeader | PrivateHeader;
+
+ if (isHeaderSsg(m_currentFilename))
+ m_currentFileType = SsgHeader | PrivateHeader;
+
+ if (std::regex_match(m_currentFilename, ExportsHeaderRegex))
+ m_currentFileType |= ExportHeader;
+ }
+
+ [[nodiscard]] bool processHeader(const std::filesystem::path &headerFile)
+ {
+ // This regex filters any paths that contain the '3rdparty' directory.
+ static const std::regex ThirdPartyFolderRegex("(^|.+/)3rdparty/.+");
+
+ // This regex filters '-config.h' and '-config_p.h' header files.
+ static const std::regex ConfigHeaderRegex("^(q|.+-)config(_p)?\\.h");
+
+ resetCurrentFileInfoData(headerFile);
+ // We assume that header files ouside of the module source or build directories do not
+ // belong to the module. Skip any processing.
+ if (!m_currentFileInSourceDir
+ && m_currentFileString.find(m_commandLineArgs->binaryDir()) != 0) {
+ scannerDebug() << "Header file: " << headerFile
+ << " is outside the sync directories. Skipping." << std::endl;
+ m_headerCheckExceptions.push_back(m_currentFileString);
+ return true;
+ }
+
+ // Check if a directory is passed as argument. That shouldn't happen, print error and exit.
+ if (m_currentFilename.empty()) {
+ std::cerr << "Header file name of " << m_currentFileString << "is empty" << std::endl;
+ return false;
+ }
+
+ std::error_code ec;
+ FileStamp originalStamp = std::filesystem::last_write_time(headerFile, ec);
+ if (ec)
+ originalStamp = FileStamp::clock::now();
+ ec.clear();
+
+ bool isPrivate = m_currentFileType & PrivateHeader;
+ bool isQpa = m_currentFileType & QpaHeader;
+ bool isRhi = m_currentFileType & RhiHeader;
+ bool isSsg = m_currentFileType & SsgHeader;
+ bool isExport = m_currentFileType & ExportHeader;
+ scannerDebug()
+ << "processHeader:start: " << headerFile
+ << " m_currentFilename: " << m_currentFilename
+ << " isPrivate: " << isPrivate
+ << " isQpa: " << isQpa
+ << " isRhi: " << isRhi
+ << " isSsg: " << isSsg
+ << std::endl;
+
+ // Chose the directory where to generate the header aliases or to copy header file if
+ // the '-copy' argument is passed.
+ std::string outputDir = m_commandLineArgs->includeDir();
+ if (isQpa)
+ outputDir = m_commandLineArgs->qpaIncludeDir();
+ else if (isRhi)
+ outputDir = m_commandLineArgs->rhiIncludeDir();
+ else if (isSsg)
+ outputDir = m_commandLineArgs->ssgIncludeDir();
+ else if (isPrivate)
+ outputDir = m_commandLineArgs->privateIncludeDir();
+
+ if (!utils::createDirectories(outputDir, "Unable to create output directory"))
+ return false;
+
+ bool headerFileExists = std::filesystem::exists(headerFile);
+
+ std::string aliasedFilepath = headerFile.generic_string();
+
+ std::string aliasPath = outputDir + '/' + m_currentFilename;
+
+ // If the '-copy' argument is passed, we copy the original file to a corresponding output
+ // directory otherwise we only create a header file alias that contains relative path to
+ // the original header file in the module source or build tree.
+ if (m_commandLineArgs->copy() && headerFileExists) {
+ if (!updateOrCopy(headerFile, aliasPath))
+ return false;
+ } else {
+ if (!generateAliasedHeaderFileIfTimestampChanged(aliasPath, aliasedFilepath,
+ originalStamp))
+ return false;
+ }
+
+ // No further processing in minimal mode.
+ if (m_commandLineArgs->minimal())
+ return true;
+
+ // Stop processing if header files doesn't exist. This happens at configure time, since
+ // either header files are generated later than syncqt is running or header files only
+ // generated at build time. These files will be processed at build time, if CMake files
+ // contain the correct dependencies between the missing header files and the module
+ // 'sync_headers' targets.
+ if (!headerFileExists) {
+ scannerDebug() << "Header file: " << headerFile
+ << " doesn't exist, but is added to syncqt scanning. Skipping.";
+ return true;
+ }
+
+ bool isGenerated = isHeaderGenerated(m_currentFileString);
+
+ // Make sure that we detect the '3rdparty' directory inside the source directory only,
+ // since full path to the Qt sources might contain '/3rdparty/' too.
+ bool is3rdParty = std::regex_match(
+ std::filesystem::relative(headerFile, m_commandLineArgs->sourceDir())
+ .generic_string(),
+ ThirdPartyFolderRegex);
+
+ // No processing of generated Qt config header files.
+ if (!std::regex_match(m_currentFilename, ConfigHeaderRegex)) {
+ unsigned int skipChecks = m_commandLineArgs->scanAllMode() ? AllChecks : NoChecks;
+
+ // Collect checks that should skipped for the header file.
+ if (m_commandLineArgs->isNonQtModule() || is3rdParty || isQpa || isRhi || isSsg
+ || !m_currentFileInSourceDir || isGenerated) {
+ skipChecks = AllChecks;
+ } else {
+ if (std::regex_match(m_currentFilename, GlobalHeaderRegex) || isExport)
+ skipChecks |= NamespaceChecks;
+
+ if (isHeaderPCH(m_currentFilename))
+ skipChecks |= WeMeantItChecks;
+
+ if (isPrivate) {
+ skipChecks |= NamespaceChecks;
+ skipChecks |= PrivateHeaderChecks;
+ skipChecks |= IncludeChecks;
+ } else {
+ skipChecks |= WeMeantItChecks;
+ }
+ }
+
+ ParsingResult parsingResult;
+ parsingResult.masterInclude = m_currentFileInSourceDir && !isExport && !is3rdParty
+ && !isQpa && !isRhi && !isSsg && !isPrivate && !isGenerated;
+ if (!parseHeader(headerFile, parsingResult, skipChecks)) {
+ scannerDebug() << "parseHeader failed: " << headerFile << std::endl;
+ return false;
+ }
+
+ // Record the private header file inside the version script content.
+ if (isPrivate && !m_commandLineArgs->versionScriptFile().empty()
+ && !parsingResult.versionScriptContent.empty()) {
+ m_versionScriptContents.insert(m_versionScriptContents.end(),
+ parsingResult.versionScriptContent.begin(),
+ parsingResult.versionScriptContent.end());
+ }
+
+ // Add the '#if QT_CONFIG(<feature>)' check for header files that supposed to be
+ // included into the module master header only if corresponding feature is enabled.
+ bool willBeInModuleMasterHeader = false;
+ if (!isQpa && !isRhi && !isSsg && !isPrivate) {
+ if (m_currentFilename.find('_') == std::string::npos
+ && parsingResult.masterInclude) {
+ m_masterHeaderContents[m_currentFilename] = parsingResult.requireConfig;
+ willBeInModuleMasterHeader = true;
+ }
+ }
+
+ scannerDebug()
+ << "processHeader:end: " << headerFile
+ << " is3rdParty: " << is3rdParty
+ << " isGenerated: " << isGenerated
+ << " m_currentFileInSourceDir: " << m_currentFileInSourceDir
+ << " willBeInModuleMasterHeader: " << willBeInModuleMasterHeader
+ << std::endl;
+ } else if (m_currentFilename == "qconfig.h") {
+ // Hardcode generating of QtConfig alias
+ updateSymbolDescriptor("QtConfig", "qconfig.h", SyncScanner::SymbolDescriptor::Pragma);
+ }
+
+ return true;
+ }
+
+ void parseVersionScriptContent(const std::string buffer, ParsingResult &result)
+ {
+ // This regex looks for the symbols that needs to be placed into linker version script.
+ static const std::regex VersionScriptSymbolRegex(
+ "^(?:struct|class)(?:\\s+Q_\\w*_EXPORT)?\\s+([\\w:]+)[^;]*(;$)?");
+
+ // This regex looks for the namespaces that needs to be placed into linker version script.
+ static const std::regex VersionScriptNamespaceRegex(
+ "^namespace\\s+Q_\\w+_EXPORT\\s+([\\w:]+).*");
+
+ // This regex filters the tailing colon from the symbol name.
+ static const std::regex TrailingColonRegex("([\\w]+):$");
+
+ switch (m_versionScriptGeneratorState) {
+ case Ignore:
+ scannerDebug() << "line ignored: " << buffer << std::endl;
+ m_versionScriptGeneratorState = Active;
+ return;
+ case Stopped:
+ return;
+ case IgnoreNext:
+ m_versionScriptGeneratorState = Ignore;
+ break;
+ case Active:
+ break;
+ }
+
+ if (buffer.empty())
+ return;
+
+ std::smatch match;
+ std::string symbol;
+ if (std::regex_match(buffer, match, VersionScriptSymbolRegex) && match[2].str().empty())
+ symbol = match[1].str();
+ else if (std::regex_match(buffer, match, VersionScriptNamespaceRegex))
+ symbol = match[1].str();
+
+ if (std::regex_match(symbol, match, TrailingColonRegex))
+ symbol = match[1].str();
+
+ // checkLineForSymbols(buffer, symbol);
+ if (!symbol.empty() && symbol[symbol.size() - 1] != ';') {
+ std::string relPath = m_currentFileInSourceDir
+ ? std::filesystem::relative(m_currentFile, m_commandLineArgs->sourceDir())
+ .string()
+ : std::filesystem::relative(m_currentFile, m_commandLineArgs->binaryDir())
+ .string();
+
+ std::string versionStringRecord = " *";
+ size_t startPos = 0;
+ size_t endPos = 0;
+ while (endPos != std::string::npos) {
+ endPos = symbol.find("::", startPos);
+ size_t length = endPos != std::string::npos ? (endPos - startPos)
+ : (symbol.size() - startPos);
+ if (length > 0) {
+ std::string symbolPart = symbol.substr(startPos, length);
+ versionStringRecord += std::to_string(symbolPart.size());
+ versionStringRecord += symbolPart;
+ }
+ startPos = endPos + 2;
+ }
+ versionStringRecord += "*;";
+ if (versionStringRecord.size() < LinkerScriptCommentAlignment)
+ versionStringRecord +=
+ std::string(LinkerScriptCommentAlignment - versionStringRecord.size(), ' ');
+ versionStringRecord += " # ";
+ versionStringRecord += relPath;
+ versionStringRecord += ":";
+ versionStringRecord += std::to_string(m_currentFileLineNumber);
+ versionStringRecord += "\n";
+ result.versionScriptContent.push_back(versionStringRecord);
+ }
+ }
+
+ // The function parses 'headerFile' and collect artifacts that are used at generating step.
+ // 'timeStamp' is saved in internal structures to compare it when generating files.
+ // 'result' the function output value that stores the result of parsing.
+ // 'skipChecks' checks that are not applicable for the header file.
+ [[nodiscard]] bool parseHeader(const std::filesystem::path &headerFile,
+ ParsingResult &result,
+ unsigned int skipChecks)
+ {
+ if (m_commandLineArgs->showOnly())
+ std::cout << headerFile << " [" << m_commandLineArgs->moduleName() << "]" << std::endl;
+ // This regex checks if line contains a macro.
+ static const std::regex MacroRegex("^\\s*#.*");
+
+ // The regex's bellow check line for known pragmas:
+ //
+ // - 'once' is not allowed in installed headers, so error out.
+ //
+ // - 'qt_sync_skip_header_check' avoid any header checks.
+ //
+ // - 'qt_sync_stop_processing' stops the header proccesing from a moment when pragma is
+ // found. Important note: All the parsing artifacts were found before this point are
+ // stored for further processing.
+ //
+ // - 'qt_sync_suspend_processing' pauses processing and skip lines inside a header until
+ // 'qt_sync_resume_processing' is found. 'qt_sync_stop_processing' stops processing if
+ // it's found before the 'qt_sync_resume_processing'.
+ //
+ // - 'qt_sync_resume_processing' resumes processing after 'qt_sync_suspend_processing'.
+ //
+ // - 'qt_class(<symbol>)' manually declares the 'symbol' that should be used to generate
+ // the CaMeL case header alias.
+ //
+ // - 'qt_deprecates([module/]<deprecated header file>[,<major.minor>])' indicates that
+ // this header file replaces the 'deprecated header file'. syncqt will create the
+ // deprecated header file' with the special deprecation content. Pragma optionally
+ // accepts the Qt version where file should be removed. If the current Qt version is
+ // higher than the deprecation version, syncqt displays deprecation warning and skips
+ // generating the deprecated header. If the module is specified and is different from
+ // the one this header file belongs to, syncqt attempts to generate header files
+ // for the specified module. Cross-module deprecation only works within the same repo.
+ // See the 'generateDeprecatedHeaders' function for details.
+ //
+ // - 'qt_no_master_include' indicates that syncqt should avoid including this header
+ // files into the module master header file.
+ static const std::regex OnceRegex(R"(^#\s*pragma\s+once$)");
+ static const std::regex SkipHeaderCheckRegex("^#\\s*pragma qt_sync_skip_header_check$");
+ static const std::regex StopProcessingRegex("^#\\s*pragma qt_sync_stop_processing$");
+ static const std::regex SuspendProcessingRegex("^#\\s*pragma qt_sync_suspend_processing$");
+ static const std::regex ResumeProcessingRegex("^#\\s*pragma qt_sync_resume_processing$");
+ static const std::regex ExplixitClassPragmaRegex("^#\\s*pragma qt_class\\(([^\\)]+)\\)$");
+ static const std::regex DeprecatesPragmaRegex("^#\\s*pragma qt_deprecates\\(([^\\)]+)\\)$");
+ static const std::regex NoMasterIncludePragmaRegex("^#\\s*pragma qt_no_master_include$");
+
+ // This regex checks if header contains 'We mean it' disclaimer. All private headers should
+ // contain them.
+ static const std::string_view WeMeantItString("We mean it.");
+
+ // The regex's check if the content of header files is wrapped with the Qt namespace macros.
+ static const std::regex BeginNamespaceRegex("^QT_BEGIN_NAMESPACE(_[A-Z_]+)?$");
+ static const std::regex EndNamespaceRegex("^QT_END_NAMESPACE(_[A-Z_]+)?$");
+
+ // This regex checks if line contains the include macro of the following formats:
+ // - #include <file>
+ // - #include "file"
+ // - # include <file>
+ static const std::regex IncludeRegex("^#\\s*include\\s*[<\"](.+)[>\"]");
+
+ // This regex checks if line contains namespace definition.
+ static const std::regex NamespaceRegex("\\s*namespace ([^ ]*)\\s+");
+
+ // This regex checks if line contains the Qt iterator declaration, that need to have
+ // CaMel case header alias.
+ static const std::regex DeclareIteratorRegex("^ *Q_DECLARE_\\w*ITERATOR\\((\\w+)\\);?$");
+
+ // This regex checks if header file contains the QT_REQUIRE_CONFIG call.
+ // The macro argument is used to wrap an include of the header file inside the module master
+ // header file with the '#if QT_CONFIG(<feature>)' guard.
+ static const std::regex RequireConfigRegex("^ *QT_REQUIRE_CONFIG\\((\\w+)\\);?$");
+
+ // This regex looks for the ELFVERSION tag this is control key-word for the version script
+ // content processing.
+ // ELFVERSION tag accepts the following values:
+ // - stop - stops the symbols lookup for a version script starting from this line.
+ // - ignore-next - ignores the line followed by the current one.
+ // - ignore - ignores the current line.
+ static const std::regex ElfVersionTagRegex(".*ELFVERSION:(stop|ignore-next|ignore).*");
+
+ std::ifstream input(headerFile, std::ifstream::in);
+ if (!input.is_open()) {
+ std::cerr << "Unable to open " << headerFile << std::endl;
+ return false;
+ }
+
+ bool hasQtBeginNamespace = false;
+ std::string qtBeginNamespace;
+ std::string qtEndNamespace;
+ bool hasWeMeantIt = false;
+ bool isSuspended = false;
+ bool isMultiLineComment = false;
+ std::size_t bracesDepth = 0;
+ std::size_t namespaceCount = 0;
+ std::string namespaceString;
+
+ std::smatch match;
+
+ std::string buffer;
+ std::string line;
+ std::string tmpLine;
+ std::size_t linesProcessed = 0;
+ int faults = NoChecks;
+
+ const auto error = [&] () -> decltype(auto) {
+ return std::cerr << ErrorMessagePreamble << m_currentFileString
+ << ":" << m_currentFileLineNumber << " ";
+ };
+
+ // Read file line by line
+ while (std::getline(input, tmpLine)) {
+ ++m_currentFileLineNumber;
+ line.append(tmpLine);
+ if (line.empty() || line.at(line.size() - 1) == '\\') {
+ continue;
+ }
+ buffer.clear();
+ buffer.reserve(line.size());
+ // Optimize processing by looking for a special sequences such as:
+ // - start-end of comments
+ // - start-end of class/structures
+ // And avoid processing of the the data inside these blocks.
+ for (std::size_t i = 0; i < line.size(); ++i) {
+ if (line[i] == '\r')
+ continue;
+ if (bracesDepth == namespaceCount) {
+ if (line[i] == '/') {
+ if ((i + 1) < line.size()) {
+ if (line[i + 1] == '*') {
+ isMultiLineComment = true;
+ continue;
+ } else if (line[i + 1] == '/') { // Single line comment
+ if (!(skipChecks & WeMeantItChecks)
+ && line.find(WeMeantItString) != std::string::npos) {
+ hasWeMeantIt = true;
+ continue;
+ }
+ if (m_versionScriptGeneratorState != Stopped
+ && std::regex_match(line, match, ElfVersionTagRegex)) {
+ if (match[1].str() == "ignore")
+ m_versionScriptGeneratorState = Ignore;
+ else if (match[1].str() == "ignore-next")
+ m_versionScriptGeneratorState = IgnoreNext;
+ else if (match[1].str() == "stop")
+ m_versionScriptGeneratorState = Stopped;
+ }
+ break;
+ }
+ }
+ } else if (line[i] == '*' && (i + 1) < line.size() && line[i + 1] == '/') {
+ ++i;
+ isMultiLineComment = false;
+ continue;
+ }
+ }
+
+ if (isMultiLineComment) {
+ if (!(skipChecks & WeMeantItChecks) &&
+ line.find(WeMeantItString) != std::string::npos) {
+ hasWeMeantIt = true;
+ continue;
+ }
+ continue;
+ }
+
+ if (line[i] == '{') {
+ if (std::regex_match(buffer, match, NamespaceRegex)) {
+ ++namespaceCount;
+ namespaceString += "::";
+ namespaceString += match[1].str();
+ }
+ ++bracesDepth;
+ continue;
+ } else if (line[i] == '}') {
+ if (namespaceCount > 0 && bracesDepth == namespaceCount) {
+ namespaceString.resize(namespaceString.rfind("::"));
+ --namespaceCount;
+ }
+ --bracesDepth;
+ } else if (bracesDepth == namespaceCount) {
+ buffer += line[i];
+ }
+ }
+ line.clear();
+
+ scannerDebug() << m_currentFilename << ": " << buffer << std::endl;
+
+ if (m_currentFileType & PrivateHeader) {
+ parseVersionScriptContent(buffer, result);
+ }
+
+ if (buffer.empty())
+ continue;
+
+ ++linesProcessed;
+
+ bool skipSymbols =
+ (m_currentFileType & PrivateHeader) || (m_currentFileType & QpaHeader) || (m_currentFileType & RhiHeader)
+ || (m_currentFileType & SsgHeader);
+
+ // Parse pragmas
+ if (std::regex_match(buffer, MacroRegex)) {
+ if (std::regex_match(buffer, SkipHeaderCheckRegex)) {
+ skipChecks = AllChecks;
+ faults = NoChecks;
+ } else if (std::regex_match(buffer, StopProcessingRegex)) {
+ if (skipChecks == AllChecks)
+ m_headerCheckExceptions.push_back(m_currentFileString);
+ return true;
+ } else if (std::regex_match(buffer, SuspendProcessingRegex)) {
+ isSuspended = true;
+ } else if (std::regex_match(buffer, ResumeProcessingRegex)) {
+ isSuspended = false;
+ } else if (std::regex_match(buffer, match, ExplixitClassPragmaRegex)) {
+ if (!skipSymbols) {
+ updateSymbolDescriptor(match[1].str(), m_currentFilename,
+ SymbolDescriptor::Pragma);
+ } else {
+ // TODO: warn about skipping symbols that are defined explicitly
+ }
+ } else if (std::regex_match(buffer, NoMasterIncludePragmaRegex)) {
+ result.masterInclude = false;
+ } else if (std::regex_match(buffer, match, DeprecatesPragmaRegex)) {
+ m_deprecatedHeaders[match[1].str()] =
+ m_commandLineArgs->moduleName() + '/' + m_currentFilename;
+ } else if (std::regex_match(buffer, OnceRegex)) {
+ if (!(skipChecks & PragmaOnceChecks)) {
+ faults |= PragmaOnceChecks;
+ error() << "\"#pragma once\" is not allowed in installed header files: "
+ "https://lists.qt-project.org/pipermail/development/2022-October/043121.html"
+ << std::endl;
+ }
+ } else if (std::regex_match(buffer, match, IncludeRegex) && !isSuspended) {
+ if (!(skipChecks & IncludeChecks)) {
+ std::string includedHeader = match[1].str();
+ if (!(skipChecks & PrivateHeaderChecks)
+ && isHeaderPrivate(std::filesystem::path(includedHeader)
+ .filename()
+ .generic_string())) {
+ faults |= PrivateHeaderChecks;
+ error() << "includes private header " << includedHeader << std::endl;
+ }
+ for (const auto &module : m_commandLineArgs->knownModules()) {
+ std::string suggestedHeader = "Qt" + module + '/' + includedHeader;
+ if (std::filesystem::exists(m_commandLineArgs->includeDir() + "/../"
+ + suggestedHeader)) {
+ faults |= IncludeChecks;
+ std::cerr << m_warningMessagePreamble << m_currentFileString
+ << ":" << m_currentFileLineNumber
+ << " includes " << includedHeader
+ << " when it should include "
+ << suggestedHeader << std::endl;
+ }
+ }
+ }
+ }
+ continue;
+ }
+
+ // Logic below this line is affected by the 'qt_sync_suspend_processing' and
+ // 'qt_sync_resume_processing' pragmas.
+ if (isSuspended)
+ continue;
+
+ // Look for the symbols in header file.
+ if (!skipSymbols) {
+ std::string symbol;
+ if (checkLineForSymbols(buffer, symbol)) {
+ if (namespaceCount == 0
+ || std::regex_match(namespaceString,
+ m_commandLineArgs->publicNamespaceRegex())) {
+ updateSymbolDescriptor(symbol, m_currentFilename,
+ SymbolDescriptor::Declaration);
+ }
+ continue;
+ } else if (std::regex_match(buffer, match, DeclareIteratorRegex)) {
+ std::string iteratorSymbol = match[1].str() + "Iterator";
+ updateSymbolDescriptor(std::string("Q") + iteratorSymbol, m_currentFilename,
+ SymbolDescriptor::Declaration);
+ updateSymbolDescriptor(std::string("QMutable") + iteratorSymbol,
+ m_currentFilename, SymbolDescriptor::Declaration);
+ continue;
+ } else if (std::regex_match(buffer, match, RequireConfigRegex)) {
+ result.requireConfig = match[1].str();
+ continue;
+ }
+ }
+
+ // Check for both QT_BEGIN_NAMESPACE and QT_END_NAMESPACE macros are present in the
+ // header file.
+ if (!(skipChecks & NamespaceChecks)) {
+ if (std::regex_match(buffer, match, BeginNamespaceRegex)) {
+ qtBeginNamespace = match[1].str();
+ hasQtBeginNamespace = true;
+ } else if (std::regex_match(buffer, match, EndNamespaceRegex)) {
+ qtEndNamespace = match[1].str();
+ }
+ }
+ }
+ input.close();
+
+ // Error out if namespace checks are failed.
+ if (!(skipChecks & NamespaceChecks)) {
+ if (hasQtBeginNamespace) {
+ if (qtBeginNamespace != qtEndNamespace) {
+ faults |= NamespaceChecks;
+ std::cerr << m_warningMessagePreamble << m_currentFileString
+ << " the begin namespace macro QT_BEGIN_NAMESPACE" << qtBeginNamespace
+ << " doesn't match the end namespace macro QT_END_NAMESPACE"
+ << qtEndNamespace << std::endl;
+ }
+ } else {
+ faults |= NamespaceChecks;
+ std::cerr << m_warningMessagePreamble << m_currentFileString
+ << " does not include QT_BEGIN_NAMESPACE" << std::endl;
+ }
+ }
+
+ if (!(skipChecks & WeMeantItChecks) && !hasWeMeantIt) {
+ faults |= WeMeantItChecks;
+ std::cerr << m_warningMessagePreamble << m_currentFileString
+ << " does not have the \"We mean it.\" warning"
+ << std::endl;
+ }
+
+ scannerDebug() << "linesTotal: " << m_currentFileLineNumber
+ << " linesProcessed: " << linesProcessed << std::endl;
+
+ if (skipChecks == AllChecks)
+ m_headerCheckExceptions.push_back(m_currentFileString);
+
+ // Exit with an error if any of critical checks are present.
+ return !(faults & m_criticalChecks);
+ }
+
+ // The function checks if line contains the symbol that needs to have a CaMeL-style alias.
+ [[nodiscard]] bool checkLineForSymbols(const std::string &line, std::string &symbol)
+ {
+ scannerDebug() << "checkLineForSymbols: " << line << std::endl;
+
+ // This regex checks if line contains class or structure declaration like:
+ // - <class|stuct> StructName
+ // - template <> class ClassName
+ // - class ClassName : [public|protected|private] BaseClassName
+ // - class ClassName [final|Q_DECL_FINAL|sealed]
+ // And possible combinations of the above variants.
+ static const std::regex ClassRegex(
+ "^ *(template *<.*> *)?(class|struct) +([^ <>]* "
+ "+)?((?!Q_DECL_FINAL|final|sealed)[^<\\s\\:]+) ?(<[^>\\:]*> "
+ "?)?\\s*(?:Q_DECL_FINAL|final|sealed)?\\s*((,|:)\\s*(public|protected|private)? "
+ "*.*)? *$");
+
+ // This regex checks if line contains function pointer typedef declaration like:
+ // - typedef void (* QFunctionPointerType)(int, char);
+ static const std::regex FunctionPointerRegex(
+ "^ *typedef *.*\\(\\*(Q[^\\)]+)\\)\\(.*\\); *");
+
+ // This regex checks if line contains class or structure typedef declaration like:
+ // - typedef AnySymbol<char> QAnySymbolType;
+ static const std::regex TypedefRegex("^ *typedef\\s+(.*)\\s+(Q\\w+); *$");
+
+ // This regex checks if symbols is the Qt public symbol. Assume that Qt public symbols start
+ // with the capital 'Q'.
+ static const std::regex QtClassRegex("^Q\\w+$");
+
+ std::smatch match;
+ if (std::regex_match(line, match, FunctionPointerRegex)) {
+ symbol = match[1].str();
+ } else if (std::regex_match(line, match, TypedefRegex)) {
+ symbol = match[2].str();
+ } else if (std::regex_match(line, match, ClassRegex)) {
+ symbol = match[4].str();
+ if (!std::regex_match(symbol, QtClassRegex))
+ symbol.clear();
+ } else {
+ return false;
+ }
+ return !symbol.empty();
+ }
+
+ [[nodiscard]] bool isHeaderQpa(const std::string &headerFileName)
+ {
+ return std::regex_match(headerFileName, m_commandLineArgs->qpaHeadersRegex());
+ }
+
+ [[nodiscard]] bool isHeaderRhi(const std::string &headerFileName)
+ {
+ return std::regex_match(headerFileName, m_commandLineArgs->rhiHeadersRegex());
+ }
+
+ [[nodiscard]] bool isHeaderSsg(const std::string &headerFileName)
+ {
+ return std::regex_match(headerFileName, m_commandLineArgs->ssgHeadersRegex());
+ }
+
+ [[nodiscard]] bool isHeaderPrivate(const std::string &headerFile)
+ {
+ return std::regex_match(headerFile, m_commandLineArgs->privateHeadersRegex());
+ }
+
+ [[nodiscard]] bool isHeaderPCH(const std::string &headerFilename)
+ {
+ static const std::string pchSuffix("_pch.h");
+ return headerFilename.find(pchSuffix, headerFilename.size() - pchSuffix.size())
+ != std::string::npos;
+ }
+
+ [[nodiscard]] bool isHeader(const std::filesystem::path &path)
+ {
+ return path.extension().string() == ".h";
+ }
+
+ [[nodiscard]] bool isDocFileHeuristic(const std::string &headerFilePath)
+ {
+ return headerFilePath.find("/doc/") != std::string::npos;
+ }
+
+ [[nodiscard]] bool isHeaderGenerated(const std::string &header)
+ {
+ return m_commandLineArgs->generatedHeaders().find(header)
+ != m_commandLineArgs->generatedHeaders().end();
+ }
+
+ [[nodiscard]] bool generateQtCamelCaseFileIfContentChanged(const std::string &outputFilePath,
+ const std::string &aliasedFilePath);
+
+ [[nodiscard]] bool generateAliasedHeaderFileIfTimestampChanged(
+ const std::string &outputFilePath, const std::string &aliasedFilePath,
+ const FileStamp &originalStamp = FileStamp::clock::now());
+
+ bool writeIfDifferent(const std::string &outputFile, const std::string &buffer);
+
+ [[nodiscard]] bool generateMasterHeader()
+ {
+ if (m_masterHeaderContents.empty())
+ return true;
+
+ std::string outputFile =
+ m_commandLineArgs->includeDir() + '/' + m_commandLineArgs->moduleName();
+
+ std::string moduleUpper = utils::asciiToUpper(m_commandLineArgs->moduleName());
+ std::stringstream buffer;
+ buffer << "#ifndef QT_" << moduleUpper << "_MODULE_H\n"
+ << "#define QT_" << moduleUpper << "_MODULE_H\n"
+ << "#include <" << m_commandLineArgs->moduleName() << "/"
+ << m_commandLineArgs->moduleName() << "Depends>\n";
+ for (const auto &headerContents : m_masterHeaderContents) {
+ if (!headerContents.second.empty()) {
+ buffer << "#if QT_CONFIG(" << headerContents.second << ")\n"
+ << "#include \"" << headerContents.first << "\"\n"
+ << "#endif\n";
+ } else {
+ buffer << "#include \"" << headerContents.first << "\"\n";
+ }
+ }
+ buffer << "#endif\n";
+
+ m_producedHeaders.insert(m_commandLineArgs->moduleName());
+ return writeIfDifferent(outputFile, buffer.str());
+ }
+
+ [[nodiscard]] bool generateVersionHeader(const std::string &outputFile)
+ {
+ std::string moduleNameUpper = utils::asciiToUpper( m_commandLineArgs->moduleName());
+
+ std::stringstream buffer;
+ buffer << "/* This file was generated by syncqt. */\n"
+ << "#ifndef QT_" << moduleNameUpper << "_VERSION_H\n"
+ << "#define QT_" << moduleNameUpper << "_VERSION_H\n\n"
+ << "#define " << moduleNameUpper << "_VERSION_STR \"" << QT_VERSION_STR << "\"\n\n"
+ << "#define " << moduleNameUpper << "_VERSION "
+ << "0x0" << QT_VERSION_MAJOR << "0" << QT_VERSION_MINOR << "0" << QT_VERSION_PATCH
+ << "\n\n"
+ << "#endif // QT_" << moduleNameUpper << "_VERSION_H\n";
+
+ return writeIfDifferent(outputFile, buffer.str());
+ }
+
+ [[nodiscard]] bool generateDeprecatedHeaders()
+ {
+ static std::regex cIdentifierSymbolsRegex("[^a-zA-Z0-9_]");
+ static std::string guard_base = "DEPRECATED_HEADER_" + m_commandLineArgs->moduleName();
+ bool result = true;
+ for (auto it = m_deprecatedHeaders.begin(); it != m_deprecatedHeaders.end(); ++it) {
+ const std::string &descriptor = it->first;
+ const std::string &replacement = it->second;
+
+ const auto separatorPos = descriptor.find(',');
+ std::string headerPath = descriptor.substr(0, separatorPos);
+ std::string versionDisclaimer;
+ if (separatorPos != std::string::npos) {
+ std::string version = descriptor.substr(separatorPos + 1);
+ versionDisclaimer = " and will be removed in Qt " + version;
+ int minor = 0;
+ int major = 0;
+ if (!utils::parseVersion(version, major, minor)) {
+ std::cerr << ErrorMessagePreamble
+ << "Invalid version format specified for the deprecated header file "
+ << headerPath << ": '" << version
+ << "'. Expected format: 'major.minor'.\n";
+ result = false;
+ continue;
+ }
+
+ if (QT_VERSION_MAJOR > major
+ || (QT_VERSION_MAJOR == major && QT_VERSION_MINOR >= minor)) {
+ std::cerr << WarningMessagePreamble << headerPath
+ << " is marked as deprecated and will not be generated in Qt "
+ << QT_VERSION_STR
+ << ". The respective qt_deprecates pragma needs to be removed.\n";
+ continue;
+ }
+ }
+
+ const auto moduleSeparatorPos = headerPath.find('/');
+ std::string headerName = moduleSeparatorPos != std::string::npos
+ ? headerPath.substr(moduleSeparatorPos + 1)
+ : headerPath;
+ const std::string moduleName = moduleSeparatorPos != std::string::npos
+ ? headerPath.substr(0, moduleSeparatorPos)
+ : m_commandLineArgs->moduleName();
+
+ bool isCrossModuleDeprecation = moduleName != m_commandLineArgs->moduleName();
+
+ std::string qualifiedHeaderName =
+ std::regex_replace(headerName, cIdentifierSymbolsRegex, "_");
+ std::string guard = guard_base + "_" + qualifiedHeaderName;
+ std::string warningText = "Header <" + moduleName + "/" + headerName + "> is deprecated"
+ + versionDisclaimer + ". Please include <" + replacement + "> instead.";
+ std::stringstream buffer;
+ buffer << "#ifndef " << guard << "\n"
+ << "#define " << guard << "\n"
+ << "#if defined(__GNUC__)\n"
+ << "# warning " << warningText << "\n"
+ << "#elif defined(_MSC_VER)\n"
+ << "# pragma message (\"" << warningText << "\")\n"
+ << "#endif\n"
+ << "#include <" << replacement << ">\n"
+ << "#endif\n";
+
+ const std::string outputDir = isCrossModuleDeprecation
+ ? m_commandLineArgs->includeDir() + "/../" + moduleName
+ : m_commandLineArgs->includeDir();
+ writeIfDifferent(outputDir + '/' + headerName, buffer.str());
+
+ // Add header file to staging installation directory for cross-module deprecation case.
+ if (isCrossModuleDeprecation) {
+ const std::string stagingDir = outputDir + "/.syncqt_staging/";
+ writeIfDifferent(stagingDir + headerName, buffer.str());
+ }
+ m_producedHeaders.insert(headerName);
+ }
+ return result;
+ }
+
+ [[nodiscard]] bool generateHeaderCheckExceptions()
+ {
+ std::stringstream buffer;
+ for (const auto &header : m_headerCheckExceptions)
+ buffer << header << ";";
+ return writeIfDifferent(m_commandLineArgs->binaryDir() + '/'
+ + m_commandLineArgs->moduleName()
+ + "_header_check_exceptions",
+ buffer.str());
+ }
+
+ [[nodiscard]] bool generateLinkerVersionScript()
+ {
+ std::stringstream buffer;
+ for (const auto &content : m_versionScriptContents)
+ buffer << content;
+ return writeIfDifferent(m_commandLineArgs->versionScriptFile(), buffer.str());
+ }
+
+ bool updateOrCopy(const std::filesystem::path &src, const std::filesystem::path &dst) noexcept;
+ void updateSymbolDescriptor(const std::string &symbol, const std::string &file,
+ SymbolDescriptor::SourceType type);
+};
+
+// The function updates information about the symbol:
+// - The path and modification time of the file where the symbol was found.
+// - The source of finding
+// Also displays a short info about a symbol in show only mode.
+void SyncScanner::updateSymbolDescriptor(const std::string &symbol, const std::string &file,
+ SymbolDescriptor::SourceType type)
+{
+ if (m_commandLineArgs->showOnly())
+ std::cout << " SYMBOL: " << symbol << std::endl;
+ m_symbols[symbol].update(file, type);
+}
+
+[[nodiscard]] std::filesystem::path
+SyncScanner::makeHeaderAbsolute(const std::string &filename) const
+{
+ if (std::filesystem::path(filename).is_relative())
+ return utils::normilizedPath(m_commandLineArgs->sourceDir() + '/' + filename);
+
+ return utils::normilizedPath(filename);
+}
+
+bool SyncScanner::updateOrCopy(const std::filesystem::path &src,
+ const std::filesystem::path &dst) noexcept
+{
+ if (m_commandLineArgs->showOnly())
+ return true;
+
+ if (src == dst) {
+ std::cout << "Source and destination paths are same when copying " << src.string()
+ << ". Skipping." << std::endl;
+ return true;
+ }
+
+ std::error_code ec;
+ std::filesystem::copy(src, dst, std::filesystem::copy_options::update_existing, ec);
+ if (ec) {
+ ec.clear();
+ std::filesystem::remove(dst, ec);
+ if (ec) {
+ // On some file systems(e.g. vboxfs) the std::filesystem::copy doesn't support
+ // std::filesystem::copy_options::overwrite_existing remove file first and then copy.
+ std::cerr << "Unable to remove file: " << src << " to " << dst << " error: ("
+ << ec.value() << ")" << ec.message() << std::endl;
+ return false;
+ }
+
+ std::filesystem::copy(src, dst, std::filesystem::copy_options::overwrite_existing, ec);
+ if (ec) {
+ std::cerr << "Unable to copy file: " << src << " to " << dst << " error: ("
+ << ec.value() << ")" << ec.message() << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+// The function generates Qt CaMeL-case files.
+// CaMeL-case files can have timestamp that is the same as or newer than timestamp of file that
+// supposed to included there. It's not an issue when we generate regular aliases because the
+// content of aliases is always the same, but we only want to "touch" them when content of original
+// is changed.
+bool SyncScanner::generateQtCamelCaseFileIfContentChanged(const std::string &outputFilePath,
+ const std::string &aliasedFilePath)
+{
+ if (m_commandLineArgs->showOnly())
+ return true;
+
+ std::string buffer = "#include \"";
+ buffer += aliasedFilePath;
+ buffer += "\" // IWYU pragma: export\n";
+
+ return writeIfDifferent(outputFilePath, buffer);
+}
+
+// The function generates aliases for files in source tree. Since the content of these aliases is
+// always same, it's ok to check only timestamp and touch files in case if stamp of original is
+// newer than the timestamp of an alias.
+bool SyncScanner::generateAliasedHeaderFileIfTimestampChanged(const std::string &outputFilePath,
+ const std::string &aliasedFilePath,
+ const FileStamp &originalStamp)
+{
+ if (m_commandLineArgs->showOnly())
+ return true;
+
+ if (std::filesystem::exists({ outputFilePath })
+ && std::filesystem::last_write_time({ outputFilePath }) >= originalStamp) {
+ return true;
+ }
+ scannerDebug() << "Rewrite " << outputFilePath << std::endl;
+
+ std::ofstream ofs;
+ ofs.open(outputFilePath, std::ofstream::out | std::ofstream::trunc);
+ if (!ofs.is_open()) {
+ std::cerr << "Unable to write header file alias: " << outputFilePath << std::endl;
+ return false;
+ }
+ ofs << "#include \"" << aliasedFilePath << "\" // IWYU pragma: export\n";
+ ofs.close();
+ return true;
+}
+
+bool SyncScanner::writeIfDifferent(const std::string &outputFile, const std::string &buffer)
+{
+ if (m_commandLineArgs->showOnly())
+ return true;
+
+ static const std::streamsize bufferSize = 1025;
+ bool differs = false;
+ std::filesystem::path outputFilePath(outputFile);
+
+ std::string outputDirectory = outputFilePath.parent_path().string();
+
+ if (!utils::createDirectories(outputDirectory, "Unable to create output directory"))
+ return false;
+
+ auto expectedSize = buffer.size();
+#ifdef _WINDOWS
+ // File on disk has \r\n instead of just \n
+ expectedSize += std::count(buffer.begin(), buffer.end(), '\n');
+#endif
+
+ if (std::filesystem::exists(outputFilePath)
+ && expectedSize == std::filesystem::file_size(outputFilePath)) {
+ char rdBuffer[bufferSize];
+ memset(rdBuffer, 0, bufferSize);
+
+ std::ifstream ifs(outputFile, std::fstream::in);
+ if (!ifs.is_open()) {
+ std::cerr << "Unable to open " << outputFile << " for comparison." << std::endl;
+ return false;
+ }
+ std::streamsize currentPos = 0;
+
+ std::size_t bytesRead = 0;
+ do {
+ ifs.read(rdBuffer, bufferSize - 1); // Read by 1K
+ bytesRead = ifs.gcount();
+ if (buffer.compare(currentPos, bytesRead, rdBuffer) != 0) {
+ differs = true;
+ break;
+ }
+ currentPos += bytesRead;
+ memset(rdBuffer, 0, bufferSize);
+ } while (bytesRead > 0);
+
+ ifs.close();
+ } else {
+ differs = true;
+ }
+
+ scannerDebug() << "Update: " << outputFile << " " << differs << std::endl;
+ if (differs) {
+ std::ofstream ofs;
+ ofs.open(outputFilePath, std::fstream::out | std::ofstream::trunc);
+ if (!ofs.is_open()) {
+ std::cerr << "Unable to write header content to " << outputFilePath << std::endl;
+ return false;
+ }
+ ofs << buffer;
+
+ ofs.close();
+ }
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ CommandLineOptions options(argc, argv);
+ if (!options.isValid())
+ return InvalidArguments;
+
+ if (options.printHelpOnly()) {
+ options.printHelp();
+ return NoError;
+ }
+
+ SyncScanner scanner = SyncScanner(&options);
+ return scanner.sync();
+}
diff --git a/src/tools/tracegen/CMakeLists.txt b/src/tools/tracegen/CMakeLists.txt
index 1404013d28..f5f6b2e184 100644
--- a/src/tools/tracegen/CMakeLists.txt
+++ b/src/tools/tracegen/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from tracegen.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tracegen Tool:
@@ -6,18 +7,18 @@
qt_get_tool_target_name(target_name tracegen)
qt_internal_add_tool(${target_name}
- BOOTSTRAP
+ CORE_LIBRARY Bootstrap
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Core # special case
+ TOOLS_TARGET Core
SOURCES
etw.cpp etw.h
helpers.cpp helpers.h
+ ctf.cpp ctf.h
lttng.cpp lttng.h
panic.cpp panic.h
provider.cpp provider.h
qtheaders.cpp qtheaders.h
tracegen.cpp
+ NO_UNITY_BUILD
)
-
-#### Keys ignored in scope 1:.:.:tracegen.pro:<TRUE>:
-# _OPTION = "host_build"
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/tracegen/ctf.cpp b/src/tools/tracegen/ctf.cpp
new file mode 100644
index 0000000000..95ffcf89cc
--- /dev/null
+++ b/src/tools/tracegen/ctf.cpp
@@ -0,0 +1,376 @@
+// 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 "ctf.h"
+#include "provider.h"
+#include "helpers.h"
+#include "panic.h"
+#include "qtheaders.h"
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qtextstream.h>
+#include <qdebug.h>
+
+
+static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider)
+{
+ writeCommonPrologue(stream);
+ const QString guard = includeGuard(fileName);
+
+ // include prefix text or qt headers only once
+ stream << "#if !defined(" << guard << ")\n";
+ stream << qtHeaders();
+ stream << "\n";
+ if (!provider.prefixText.isEmpty())
+ stream << provider.prefixText.join(u'\n') << "\n\n";
+ stream << "#endif\n\n";
+
+ /* the first guard is the usual one, the second is required
+ * by LTTNG to force the re-evaluation of TRACEPOINT_* macros
+ */
+ stream << "#if !defined(" << guard << ") || defined(TRACEPOINT_HEADER_MULTI_READ)\n";
+
+ stream << "#define " << guard << "\n\n"
+ << "#undef TRACEPOINT_INCLUDE\n"
+ << "#define TRACEPOINT_INCLUDE \"" << fileName << "\"\n\n";
+
+ stream << "#include <private/qctf_p.h>\n\n";
+
+ const QString namespaceGuard = guard + QStringLiteral("_USE_NAMESPACE");
+ stream << "#if !defined(" << namespaceGuard << ")\n"
+ << "#define " << namespaceGuard << "\n"
+ << "QT_USE_NAMESPACE\n"
+ << "#endif // " << namespaceGuard << "\n\n";
+
+ stream << "TRACEPOINT_PROVIDER(" << provider.name << ");\n\n";
+}
+
+static void writeEpilogue(QTextStream &stream, const QString &fileName)
+{
+ stream << "\n";
+ stream << "#endif // " << includeGuard(fileName) << "\n"
+ << "#include <private/qtrace_p.h>\n";
+}
+
+static void writeWrapper(QTextStream &stream,
+ const Tracepoint &tracepoint, const Provider &provider)
+{
+ const QString argList = formatFunctionSignature(tracepoint.args);
+ const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, CTF);
+ const QString &name = tracepoint.name;
+ const QString includeGuard = QStringLiteral("TP_%1_%2").arg(provider.name).arg(name).toUpper();
+
+ /* prevents the redefinion of the inline wrapper functions
+ */
+ stream << "\n"
+ << "#ifndef " << includeGuard << "\n"
+ << "#define " << includeGuard << "\n"
+ << "QT_BEGIN_NAMESPACE\n"
+ << "namespace QtPrivate {\n";
+
+ stream << "inline void trace_" << name << "(" << argList << ")\n"
+ << "{\n"
+ << " tracepoint(" << provider.name << ", " << name << paramList << ");\n"
+ << "}\n";
+
+ stream << "inline void do_trace_" << name << "(" << argList << ")\n"
+ << "{\n"
+ << " do_tracepoint(" << provider.name << ", " << name << paramList << ");\n"
+ << "}\n";
+
+ stream << "inline bool trace_" << name << "_enabled()\n"
+ << "{\n"
+ << " return tracepoint_enabled(" << provider.name << ", " << name << ");\n"
+ << "}\n";
+
+ stream << "} // namespace QtPrivate\n"
+ << "QT_END_NAMESPACE\n"
+ << "#endif // " << includeGuard << "\n\n";
+}
+
+
+static void writeMetadataGenerators(QTextStream &stream)
+{
+ stream << R"CPP(
+template <typename T>
+inline QString integerToMetadata(const QString &name)
+{
+ QString ret;
+ if (!std::is_signed<T>().value)
+ ret += QLatin1Char('u');
+ if (sizeof(T) == 8)
+ ret += QStringLiteral("int64_t ");
+ else if (sizeof(T) == 4)
+ ret += QStringLiteral("int32_t ");
+ else if (sizeof(T) == 2)
+ ret += QStringLiteral("int16_t ");
+ else if (sizeof(T) == 1)
+ ret += QStringLiteral("int8_t ");
+ ret += name + QLatin1Char(';');
+ return ret;
+}
+
+template <typename T>
+inline QString integerArrayToMetadata(const QString &size, const QString &name)
+{
+ QString ret;
+ if (!std::is_signed<T>().value)
+ ret += QLatin1Char('u');
+ if (sizeof(T) == 8)
+ ret += QStringLiteral("int64_t ");
+ else if (sizeof(T) == 4)
+ ret += QStringLiteral("int32_t ");
+ else if (sizeof(T) == 2)
+ ret += QStringLiteral("int16_t ");
+ else if (sizeof(T) == 1)
+ ret += QStringLiteral("int8_t ");
+ ret += name + QLatin1Char('[') + size + QStringLiteral("];");
+ return ret;
+}
+
+template <typename T>
+inline QString floatToMetadata(const QString &name)
+{
+ QString ret;
+ if (sizeof(T) == 8)
+ ret += QStringLiteral("double ");
+ else if (sizeof(T) == 4)
+ ret += QStringLiteral("float ");
+ ret += name + QLatin1Char(';');
+ return ret;
+}
+
+template <typename T>
+inline QString floatArrayToMetadata(const QString &size, const QString &name)
+{
+ QString ret;
+ if (sizeof(T) == 8)
+ ret += QStringLiteral("double ");
+ else if (sizeof(T) == 4)
+ ret += QStringLiteral("float ");
+ ret += name + QLatin1Char('[') + size + QLatin1Char(']');
+ return ret + QLatin1Char(';');
+}
+
+inline QString pointerToMetadata(const QString &name)
+{
+ QString ret;
+ if (QT_POINTER_SIZE == 8)
+ ret += QStringLiteral("intptr64_t ");
+ else if (QT_POINTER_SIZE == 4)
+ ret += QStringLiteral("intptr32_t ");
+ ret += name + QLatin1Char(';');
+ return ret;
+}
+
+)CPP";
+}
+
+static void writeTracepoint(QTextStream &stream,
+ const Tracepoint &tracepoint, const QString &providerName)
+{
+ stream << "TRACEPOINT_EVENT(\n"
+ << " " << providerName << ",\n"
+ << " " << tracepoint.name << ",\n";
+
+ const auto checkUnknownArgs = [](const Tracepoint &tracepoint) {
+ for (auto &field : tracepoint.fields) {
+ if (field.backendType == Tracepoint::Field::Unknown)
+ return true;
+ }
+ return false;
+ };
+
+ const auto formatType = [](const QString &type) {
+ QString ret = type;
+ if (type.endsWith(QLatin1Char('*')) || type.endsWith(QLatin1Char('&')))
+ ret = type.left(type.length() - 1).simplified();
+ if (ret.startsWith(QStringLiteral("const")))
+ ret = ret.right(ret.length() - 6).simplified();
+ return typeToTypeName(ret);
+ };
+ QString eventSize;
+ bool variableSize = false;
+ const bool emptyMetadata = checkUnknownArgs(tracepoint) || tracepoint.args.size() == 0;
+ if (!emptyMetadata) {
+ for (int i = 0; i < tracepoint.args.size(); i++) {
+ auto &arg = tracepoint.args[i];
+ auto &field = tracepoint.fields[i];
+ if (i > 0) {
+ stream << " + QStringLiteral(\"\\n\\\n \") + ";
+ eventSize += QStringLiteral(" + ");
+ }
+ const bool array = field.arrayLen > 0;
+ switch (field.backendType) {
+ case Tracepoint::Field::Boolean: {
+ stream << "QStringLiteral(\"Boolean " << arg.name << ";\")";
+ eventSize += QStringLiteral("sizeof(bool)");
+ } break;
+ case Tracepoint::Field::Integer: {
+ if (array) {
+ stream << "integerArrayToMetadata<" << formatType(arg.type)
+ << ">(QStringLiteral(\"" << field.arrayLen << "\"), QStringLiteral(\""
+ << arg.name << "\"))";
+ } else {
+ stream << "integerToMetadata<" << formatType(arg.type) << ">(QStringLiteral(\""
+ << arg.name << "\"))";
+ }
+ eventSize += QStringLiteral("sizeof(") + formatType(arg.type) + QStringLiteral(")");
+ if (array)
+ eventSize += QStringLiteral(" * ") + QString::number(field.arrayLen);
+ } break;
+ case Tracepoint::Field::Pointer:
+ case Tracepoint::Field::IntegerHex: {
+ stream << "pointerToMetadata(QStringLiteral(\"" << formatType(arg.type) << "_"
+ << arg.name << "\"))";
+ eventSize += QStringLiteral("QT_POINTER_SIZE");
+ } break;
+ case Tracepoint::Field::Float: {
+ if (array) {
+ stream << "floatArrayToMetadata<" << formatType(arg.type)
+ << ">(QStringLiteral(\"" << field.arrayLen << "\"), QStringLiteral(\""
+ << arg.name << "\"))";
+ } else {
+ stream << "floatToMetadata<" << formatType(arg.type) << ">(QStringLiteral(\""
+ << arg.name << "\"))";
+ }
+ eventSize += QStringLiteral("sizeof(") + formatType(arg.type) + QStringLiteral(")");
+ if (array)
+ eventSize += QStringLiteral(" * ") + QString::number(field.arrayLen);
+ } break;
+ case Tracepoint::Field::QtUrl:
+ case Tracepoint::Field::QtString:
+ case Tracepoint::Field::String: {
+ stream << "QStringLiteral(\"string " << arg.name << ";\")";
+ eventSize += QStringLiteral("1");
+ variableSize = true;
+ } break;
+ case Tracepoint::Field::QtRect: {
+ stream << "QStringLiteral(\"int32_t QRect_" << arg.name << "_x;\\n\\\n \")";
+ stream << " + QStringLiteral(\"int32_t QRect_" << arg.name << "_y;\\n\\\n \")";
+ stream << " + QStringLiteral(\"int32_t QRect_" << arg.name << "_width;\\n\\\n \")";
+ stream << " + QStringLiteral(\"int32_t QRect_" << arg.name << "_height;\\n\\\n \")";
+ eventSize += QStringLiteral("16");
+ } break;
+ case Tracepoint::Field::QtSize: {
+ stream << "QStringLiteral(\"int32_t QSize_" << arg.name << "_width;\\n\\\n \")";
+ stream << " + QStringLiteral(\"int32_t QSize_" << arg.name << "_height;\\n\\\n \")";
+ eventSize += QStringLiteral("8");
+ } break;
+ case Tracepoint::Field::QtRectF: {
+ stream << "QStringLiteral(\"float QRectF_" << arg.name << "_x;\\n\\\n \")";
+ stream << " + QStringLiteral(\"float QRectF_" << arg.name << "_y;\\n\\\n \")";
+ stream << " + QStringLiteral(\"float QRectF_" << arg.name << "_width;\\n\\\n \")";
+ stream << " + QStringLiteral(\"float QRectF_" << arg.name << "_height;\\n\\\n \")";
+ eventSize += QStringLiteral("16");
+ } break;
+ case Tracepoint::Field::QtSizeF: {
+ stream << "QStringLiteral(\"float QSizeF_" << arg.name << "_width;\\n\\\n \")";
+ stream << " + QStringLiteral(\"float QSizeF_" << arg.name << "_height;\\n\\\n \")";
+ eventSize += QStringLiteral("8");
+ } break;
+ case Tracepoint::Field::Unknown:
+ break;
+ case Tracepoint::Field::EnumeratedType: {
+ stream << "QStringLiteral(\"" << typeToTypeName(arg.type) << " " << arg.name << ";\")";
+ eventSize += QString::number(field.enumValueSize / 8);
+ variableSize = true;
+ } break;
+ case Tracepoint::Field::FlagType: {
+ stream << "QStringLiteral(\"uint8_t " << arg.name << "_length;\\n\\\n ";
+ stream << typeToTypeName(arg.type) << " " << arg.name << "[" << arg.name << "_length];\")";
+ eventSize += QStringLiteral("2");
+ variableSize = true;
+ } break;
+ case Tracepoint::Field::QtByteArray:
+ case Tracepoint::Field::Sequence:
+ panic("Unhandled type '%s %s", qPrintable(arg.type), qPrintable(arg.name));
+ break;
+ }
+ }
+ }
+
+ if (emptyMetadata)
+ stream << "{},\n";
+ else
+ stream << ",\n";
+ if (eventSize.length())
+ stream << eventSize << ", \n";
+ else
+ stream << "0, \n";
+ stream << (variableSize ? "true" : "false") << "\n";
+ stream << ")\n\n";
+}
+
+static void writeTracepoints(QTextStream &stream, const Provider &provider)
+{
+ for (const Tracepoint &t : provider.tracepoints) {
+ writeTracepoint(stream, t, provider.name);
+ writeWrapper(stream, t, provider);
+ }
+}
+
+static void writeEnums(QTextStream &stream, const Provider &provider)
+{
+ for (const auto &e : provider.enumerations) {
+ QString name = e.name;
+ name.replace(QStringLiteral("::"), QStringLiteral("_"));
+ stream << "TRACEPOINT_METADATA(" << provider.name << ", " << name << ", \n";
+ stream << "QStringLiteral(\"typealias enum : integer { size = " << e.valueSize << "; } {\\n\\\n";
+
+ const auto values = e.values;
+ QList<int> handledValues;
+
+ for (const auto &v : values) {
+ if (handledValues.contains(v.value))
+ continue;
+ if (v.range) {
+ stream << v.name << " = " << v.value << " ... " << v.range << ", \\n\\\n";
+ } else {
+ const QString names = aggregateListValues(v.value, values);
+ stream << names << " = " << v.value << ", \\n\\\n";
+ handledValues.append(v.value);
+ }
+ }
+ stream << "} := " << name << ";\\n\\n\"));\n\n";
+ }
+ stream << "\n";
+}
+
+static void writeFlags(QTextStream &stream, const Provider &provider)
+{
+ for (const auto &e : provider.flags) {
+ QString name = e.name;
+ name.replace(QStringLiteral("::"), QStringLiteral("_"));
+ stream << "TRACEPOINT_METADATA(" << provider.name << ", " << name << ", \n";
+ stream << "QStringLiteral(\"typealias enum : integer { size = 8; } {\\n\\\n";
+
+ const auto values = e.values;
+ QList<int> handledValues;
+
+ for (const auto &v : values) {
+ if (handledValues.contains(v.value))
+ continue;
+ const QString names = aggregateListValues(v.value, values);
+ stream << names << " = " << v.value << ", \\n\\\n";
+ handledValues.append(v.value);
+ }
+ stream << "} := " << name << ";\\n\\n\"));\n\n";
+ }
+ stream << "\n";
+}
+
+void writeCtf(QFile &file, const Provider &provider)
+{
+ QTextStream stream(&file);
+
+ const QString fileName = QFileInfo(file.fileName()).fileName();
+
+ writePrologue(stream, fileName, provider);
+ writeMetadataGenerators(stream);
+ writeEnums(stream, provider);
+ writeFlags(stream, provider);
+ writeTracepoints(stream, provider);
+ writeEpilogue(stream, fileName);
+}
diff --git a/src/tools/tracegen/ctf.h b/src/tools/tracegen/ctf.h
new file mode 100644
index 0000000000..113e30919f
--- /dev/null
+++ b/src/tools/tracegen/ctf.h
@@ -0,0 +1,12 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CTF_H
+#define CTF_H
+
+struct Provider;
+class QFile;
+
+void writeCtf(QFile &device, const Provider &p);
+
+#endif // CTF_H
diff --git a/src/tools/tracegen/etw.cpp b/src/tools/tracegen/etw.cpp
index eac518dbab..f54a7896ea 100644
--- a/src/tools/tracegen/etw.cpp
+++ b/src/tools/tracegen/etw.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "etw.h"
#include "provider.h"
@@ -47,19 +11,31 @@
#include <qtextstream.h>
#include <quuid.h>
+using namespace Qt::StringLiterals;
+
static inline QString providerVar(const QString &providerName)
{
- return providerName + QLatin1String("_provider");
+ return providerName + "_provider"_L1;
}
static void writeEtwMacro(QTextStream &stream, const Tracepoint::Field &field)
{
const QString &name = field.name;
+ if (field.arrayLen > 0) {
+ for (int i = 0; i < field.arrayLen; i++) {
+ stream << "TraceLoggingValue(" << name << "[" << i << "], \"" << name << "[" << i << "]\")";
+ if (i + 1 < field.arrayLen)
+ stream << ",\n ";
+ }
+ return;
+ }
+
switch (field.backendType) {
case Tracepoint::Field::QtString:
stream << "TraceLoggingCountedWideString(reinterpret_cast<LPCWSTR>("
- << name << ".utf16()), " << name << ".size(), \"" << name << "\")";
+ << name << ".utf16()), static_cast<ULONG>(" << name << ".size()), \""
+ << name << "\")";
return;
case Tracepoint::Field::QtByteArray:
stream << "TraceLoggingBinary(" << name << ".constData(), "
@@ -69,14 +45,31 @@ static void writeEtwMacro(QTextStream &stream, const Tracepoint::Field &field)
stream << "TraceLoggingValue(" << name << ".toEncoded().constData(), \"" << name << "\")";
return;
case Tracepoint::Field::QtRect:
+ case Tracepoint::Field::QtRectF:
stream << "TraceLoggingValue(" << name << ".x(), \"x\"), "
<< "TraceLoggingValue(" << name << ".y(), \"y\"), "
<< "TraceLoggingValue(" << name << ".width(), \"width\"), "
<< "TraceLoggingValue(" << name << ".height(), \"height\")";
return;
+ case Tracepoint::Field::QtSize:
+ case Tracepoint::Field::QtSizeF:
+ stream << "TraceLoggingValue(" << name << ".width(), \"width\"), "
+ << "TraceLoggingValue(" << name << ".height(), \"height\")";
+ return;
case Tracepoint::Field::Pointer:
stream << "TraceLoggingPointer(" << name << ", \"" << name << "\")";
return;
+ case Tracepoint::Field::Unknown:
+ // Write down the previously stringified data (like we do for QString).
+ // The string is already created in writeWrapper().
+ // Variable name is name##Str.
+ stream << "TraceLoggingCountedWideString(reinterpret_cast<LPCWSTR>(" << name
+ << "Str.utf16()), static_cast<ULONG>(" << name << "Str.size()), \"" << name << "\")";
+ return;
+ case Tracepoint::Field::EnumeratedType:
+ case Tracepoint::Field::FlagType:
+ stream << "TraceLoggingString(trace_convert_" << typeToTypeName(field.paramType) << "(" << name << ").toUtf8().constData(), \"" << name << "\")";
+ return;
default:
break;
}
@@ -111,6 +104,7 @@ static QString createGuid(const QUuid &uuid)
static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider)
{
+ writeCommonPrologue(stream);
QUuid uuid = QUuid::createUuidV5(QUuid(), provider.name.toLocal8Bit());
const QString providerV = providerVar(provider.name);
@@ -121,7 +115,7 @@ static void writePrologue(QTextStream &stream, const QString &fileName, const Pr
stream << "#ifndef " << guard << "\n"
<< "#define " << guard << "\n"
<< "\n"
- << "#include <windows.h>\n"
+ << "#include <qt_windows.h>\n"
<< "#include <TraceLoggingProvider.h>\n"
<< "\n";
@@ -138,7 +132,7 @@ static void writePrologue(QTextStream &stream, const QString &fileName, const Pr
stream << "\n";
if (!provider.prefixText.isEmpty())
- stream << provider.prefixText.join(QLatin1Char('\n')) << "\n\n";
+ stream << provider.prefixText.join(u'\n') << "\n\n";
stream << "#ifdef TRACEPOINT_DEFINE\n"
<< "/* " << guidString << " */\n"
@@ -169,20 +163,29 @@ static void writeEpilogue(QTextStream &stream, const QString &fileName)
<< "#include <private/qtrace_p.h>\n";
}
-static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint,
+static void writeWrapper(QTextStream &stream, const Provider &provider, const Tracepoint &tracepoint,
const QString &providerName)
{
const QString argList = formatFunctionSignature(tracepoint.args);
- const QString paramList = formatParameterList(tracepoint.args, ETW);
+ const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, ETW);
const QString &name = tracepoint.name;
const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper();
- const QString provider = providerVar(providerName);
+ const QString provar = providerVar(providerName);
stream << "\n";
stream << "inline void trace_" << name << "(" << argList << ")\n"
- << "{\n"
- << " TraceLoggingWrite(" << provider << ", \"" << name << "\"";
+ << "{\n";
+
+ // Convert all unknown types to QString's using QDebug.
+ // Note the naming convention: it's field.name##Str
+ for (const Tracepoint::Field &field : tracepoint.fields) {
+ if (field.backendType == Tracepoint::Field::Unknown) {
+ stream << " const QString " << field.name << "Str = QDebug::toString(" << field.name
+ << ");\n";
+ }
+ }
+ stream << " TraceLoggingWrite(" << provar << ", \"" << name << "\"";
for (const Tracepoint::Field &field : tracepoint.fields) {
stream << ",\n";
@@ -200,10 +203,56 @@ static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint,
stream << "inline bool trace_" << name << "_enabled()\n"
<< "{\n"
- << " return TraceLoggingProviderEnabled(" << provider << ", 0, 0);\n"
+ << " return TraceLoggingProviderEnabled(" << provar << ", 0, 0);\n"
<< "}\n";
}
+static void writeEnumConverter(QTextStream &stream, const TraceEnum &enumeration)
+{
+ stream << "inline QString trace_convert_" << typeToTypeName(enumeration.name) << "(" << enumeration.name << " val)\n";
+ stream << "{\n";
+ for (const auto &v : enumeration.values) {
+ if (v.range != 0) {
+ stream << " if (val >= " << v.value << " && val <= " << v.range << ")\n"
+ << " return QStringLiteral(\"" << v.name << " + \") + QString::number((int)val - " << v.value << ");\n";
+ }
+ }
+ stream << "\n QString ret;\n switch (val) {\n";
+
+ QList<int> handledValues;
+ for (const auto &v : enumeration.values) {
+ if (v.range == 0 && !handledValues.contains(v.value)) {
+ stream << " case " << v.value << ": ret = QStringLiteral(\""
+ << aggregateListValues(v.value, enumeration.values) << "\"); break;\n";
+ handledValues.append(v.value);
+ }
+ }
+
+ stream << " }\n return ret;\n}\n";
+}
+
+static void writeFlagConverter(QTextStream &stream, const TraceFlags &flag)
+{
+ stream << "inline QString trace_convert_" << typeToTypeName(flag.name) << "(" << flag.name << " val)\n";
+ stream << "{\n QString ret;\n";
+ for (const auto &v : flag.values) {
+ if (v.value == 0) {
+ stream << " if (val == 0)\n return QStringLiteral(\""
+ << aggregateListValues(v.value, flag.values) << "\");\n";
+ break;
+ }
+ }
+ QList<int> handledValues;
+ for (const auto &v : flag.values) {
+ if (v.value != 0 && !handledValues.contains(v.value)) {
+ stream << " if (val & " << (1 << (v.value - 1))
+ << ") { if (ret.length()) ret += QLatin1Char(\'|\'); ret += QStringLiteral(\"" << v.name << "\"); }\n";
+ handledValues.append(v.value);
+ }
+ }
+ stream << " return ret;\n}\n";
+}
+
static void writeTracepoints(QTextStream &stream, const Provider &provider)
{
if (provider.tracepoints.isEmpty())
@@ -213,12 +262,20 @@ static void writeTracepoints(QTextStream &stream, const Provider &provider)
stream << "#if !defined(" << includeGuard << ") && !defined(TRACEPOINT_DEFINE)\n"
<< "#define " << includeGuard << "\n"
+ << "QT_BEGIN_NAMESPACE\n"
<< "namespace QtPrivate {\n";
+ for (const auto &enumeration : provider.enumerations)
+ writeEnumConverter(stream, enumeration);
+
+ for (const auto &flag : provider.flags)
+ writeFlagConverter(stream, flag);
+
for (const Tracepoint &t : provider.tracepoints)
- writeWrapper(stream, t, provider.name);
+ writeWrapper(stream, provider, t, provider.name);
stream << "} // namespace QtPrivate\n"
+ << "QT_END_NAMESPACE\n"
<< "#endif // " << includeGuard << "\n\n";
}
diff --git a/src/tools/tracegen/etw.h b/src/tools/tracegen/etw.h
index 5fc9b57eaa..88508e2034 100644
--- a/src/tools/tracegen/etw.h
+++ b/src/tools/tracegen/etw.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef ETW_H
#define ETW_H
diff --git a/src/tools/tracegen/helpers.cpp b/src/tools/tracegen/helpers.cpp
index 8ffc088fed..0ea5848493 100644
--- a/src/tools/tracegen/helpers.cpp
+++ b/src/tools/tracegen/helpers.cpp
@@ -1,52 +1,33 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "helpers.h"
#include <qdebug.h>
+using namespace Qt::StringLiterals;
+
+void writeCommonPrologue(QTextStream &stream)
+{
+ stream << R"CPP(
+#ifndef Q_TRACEPOINT
+#error "Q_TRACEPOINT not set for the module, Q_TRACE not enabled."
+#endif
+)CPP";
+}
+
+QString typeToTypeName(const QString &name)
+{
+ QString ret = name;
+ return ret.replace(QStringLiteral("::"), QStringLiteral("_"));
+}
+
QString includeGuard(const QString &filename)
{
QString guard = filename.toUpper();
for (int i = 0; i < guard.size(); ++i) {
if (!guard.at(i).isLetterOrNumber())
- guard[i] = QLatin1Char('_');
+ guard[i] = u'_';
}
return guard;
@@ -60,7 +41,7 @@ static QString joinArguments(const QList<Tracepoint::Argument> &args, T joinFunc
for (const Tracepoint::Argument &arg : args) {
if (!first)
- ret += QLatin1String(", ");
+ ret += ", "_L1;
ret += joinFunction(arg);
@@ -77,14 +58,56 @@ QString formatFunctionSignature(const QList<Tracepoint::Argument> &args)
});
}
-QString formatParameterList(const QList<Tracepoint::Argument> &args, ParamType type)
+QString formatParameterList(const Provider &provider, const QList<Tracepoint::Argument> &args, const QList<Tracepoint::Field> &fields, ParamType type)
{
if (type == LTTNG) {
QString ret;
- for (const Tracepoint::Argument &arg : args)
- ret += QLatin1String(", ") + arg.name;
+ for (int i = 0; i < args.size(); i++) {
+ const Tracepoint::Argument &arg = args[i];
+ const Tracepoint::Field &field = fields[i];
+ if (field.backendType == Tracepoint::Field::FlagType)
+ ret += ", trace_convert_"_L1 + typeToTypeName(arg.type) + "("_L1 + arg.name + ")"_L1;
+ else
+ ret += ", "_L1 + arg.name;
+ }
+ return ret;
+ }
+
+ auto findEnumeration = [](const QList<TraceEnum> &enums, const QString &name) {
+ for (const auto &e : enums) {
+ if (e.name == name)
+ return e;
+ }
+ return TraceEnum();
+ };
+
+ if (type == CTF) {
+ QString ret;
+ for (int i = 0; i < args.size(); i++) {
+ const Tracepoint::Argument &arg = args[i];
+ const Tracepoint::Field &field = fields[i];
+ if (arg.arrayLen > 1) {
+ ret += ", trace::toByteArrayFromArray("_L1 + arg.name + ", "_L1 + QString::number(arg.arrayLen) + ") "_L1;
+ } else if (field.backendType == Tracepoint::Field::EnumeratedType) {
+ const TraceEnum &e = findEnumeration(provider.enumerations, arg.type);
+ QString integerType;
+ if (e.valueSize == 8)
+ integerType = QStringLiteral("quint8");
+ else if (e.valueSize == 16)
+ integerType = QStringLiteral("quint16");
+ else
+ integerType = QStringLiteral("quint32");
+ ret += ", trace::toByteArrayFromEnum<"_L1 + integerType + ">("_L1 + arg.name + ")"_L1;
+ } else if (field.backendType == Tracepoint::Field::FlagType) {
+ ret += ", trace::toByteArrayFromFlags("_L1 + arg.name + ")"_L1;
+ } else if (field.backendType == Tracepoint::Field::String) {
+ ret += ", trace::toByteArrayFromCString("_L1 + arg.name + ")"_L1;
+ } else {
+ ret += ", "_L1 + arg.name;
+ }
+ }
return ret;
}
diff --git a/src/tools/tracegen/helpers.h b/src/tools/tracegen/helpers.h
index 8acf6e2292..ea6db016a6 100644
--- a/src/tools/tracegen/helpers.h
+++ b/src/tools/tracegen/helpers.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef HELPERS_H
#define HELPERS_H
@@ -44,14 +8,30 @@
#include <qlist.h>
#include <qstring.h>
+#include <qtextstream.h>
enum ParamType {
LTTNG,
- ETW
+ ETW,
+ CTF
};
+QString typeToTypeName(const QString &type);
QString includeGuard(const QString &filename);
QString formatFunctionSignature(const QList<Tracepoint::Argument> &args);
-QString formatParameterList(const QList<Tracepoint::Argument> &args, ParamType type);
+QString formatParameterList(const Provider &provider, const QList<Tracepoint::Argument> &args, const QList<Tracepoint::Field> &fields, ParamType type);
+
+void writeCommonPrologue(QTextStream &stream);
+
+template <typename T>
+static QString aggregateListValues(int value, const QList<T> &list)
+{
+ QStringList values;
+ for (const T &l : list) {
+ if (l.value == value)
+ values << l.name;
+ }
+ return values.join(QLatin1Char('_'));
+}
#endif // HELPERS_H
diff --git a/src/tools/tracegen/lttng.cpp b/src/tools/tracegen/lttng.cpp
index e628fbd680..9711570874 100644
--- a/src/tools/tracegen/lttng.cpp
+++ b/src/tools/tracegen/lttng.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "lttng.h"
#include "provider.h"
@@ -48,23 +12,37 @@
#include <qtextstream.h>
#include <qdebug.h>
-static void writeCtfMacro(QTextStream &stream, const Tracepoint::Field &field)
+static void writeCtfMacro(QTextStream &stream, const Provider &provider, const Tracepoint::Field &field)
{
const QString &paramType = field.paramType;
const QString &name = field.name;
const QString &seqLen = field.seqLen;
const int arrayLen = field.arrayLen;
- switch (field.backendType) {
- case Tracepoint::Field::Array:
- stream << "ctf_array(" <<paramType << ", "
- << name << ", " << name << ", " << arrayLen << ")";
+ if (arrayLen > 0) {
+ if (paramType == QStringLiteral("double") || paramType == QStringLiteral("float")) {
+ const char *newline = nullptr;
+ for (int i = 0; i < arrayLen; i++) {
+ stream << newline;
+ stream << "ctf_float(" <<paramType << ", " << name << "_" << QString::number(i) << ", "
+ << name << "[" << QString::number(i) << "]" << ")";
+ newline = "\n ";
+ }
+
+ } else {
+ stream << "ctf_array(" <<paramType << ", "
+ << name << ", " << name << ", " << arrayLen << ")";
+ }
return;
+ }
+
+ switch (field.backendType) {
case Tracepoint::Field::Sequence:
stream << "ctf_sequence(" << paramType
<< ", " << name << ", " << name
<< ", unsigned int, " << seqLen << ")";
return;
+ case Tracepoint::Field::Boolean:
case Tracepoint::Field::Integer:
stream << "ctf_integer(" << paramType << ", " << name << ", " << name << ")";
return;
@@ -79,33 +57,51 @@ static void writeCtfMacro(QTextStream &stream, const Tracepoint::Field &field)
stream << "ctf_string(" << name << ", " << name << ")";
return;
case Tracepoint::Field::QtString:
- stream << "ctf_sequence(const ushort, " << name << ", "
- << name << ".utf16(), unsigned int, " << name << ".size())";
+ stream << "ctf_string(" << name << ", " << name << ".toUtf8().constData())";
return;
case Tracepoint::Field::QtByteArray:
stream << "ctf_sequence(const char, " << name << ", "
<< name << ".constData(), unsigned int, " << name << ".size())";
return;
case Tracepoint::Field::QtUrl:
- stream << "ctf_sequence(const char, " << name << ", "
- << name << ".toEncoded().constData(), unsigned int, "
- << name << ".toEncoded().size())";
+ stream << "ctf_string(" << name << ", " << name << ".toString().toUtf8().constData())";
return;
case Tracepoint::Field::QtRect:
- stream << "ctf_integer(int, x, " << name << ".x()) "
- << "ctf_integer(int, y, " << name << ".y()) "
- << "ctf_integer(int, width, " << name << ".width()) "
- << "ctf_integer(int, height, " << name << ".height()) ";
+ stream << "ctf_integer(int, QRect_" << name << "_x, " << name << ".x()) "
+ << "ctf_integer(int, QRect_" << name << "_y, " << name << ".y()) "
+ << "ctf_integer(int, QRect_" << name << "_width, " << name << ".width()) "
+ << "ctf_integer(int, QRect_" << name << "_height, " << name << ".height()) ";
+ return;
+ case Tracepoint::Field::QtSizeF:
+ stream << "ctf_float(double, QSizeF_" << name << "_width, " << name << ".width()) "
+ << "ctf_float(double, QSizeF_" << name << "_height, " << name << ".height()) ";
+ return;
+ case Tracepoint::Field::QtRectF:
+ stream << "ctf_float(double, QRectF_" << name << "_x, " << name << ".x()) "
+ << "ctf_float(double, QRectF_" << name << "_y, " << name << ".y()) "
+ << "ctf_float(double, QRectF_" << name << "_width, " << name << ".width()) "
+ << "ctf_float(double, QRectF_" << name << "_height, " << name << ".height()) ";
+ return;
+ case Tracepoint::Field::QtSize:
+ stream << "ctf_integer(int, QSize_" << name << "_width, " << name << ".width()) "
+ << "ctf_integer(int, QSize_" << name << "_height, " << name << ".height()) ";
+ return;
+ case Tracepoint::Field::EnumeratedType:
+ stream << "ctf_enum(" << provider.name << ", " << typeToTypeName(paramType) << ", int, " << name << ", " << name << ") ";
+ return;
+ case Tracepoint::Field::FlagType:
+ stream << "ctf_sequence(const char , " << name << ", "
+ << name << ".constData(), unsigned int, " << name << ".size())";
return;
case Tracepoint::Field::Unknown:
- justified_worry("Cannot deduce CTF type for '%s %s'", qPrintable(paramType),
- qPrintable(name));
+ panic("Cannot deduce CTF type for '%s %s", qPrintable(paramType), qPrintable(name));
break;
}
}
static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider)
{
+ writeCommonPrologue(stream);
const QString guard = includeGuard(fileName);
stream << "#undef TRACEPOINT_PROVIDER\n";
@@ -117,7 +113,7 @@ static void writePrologue(QTextStream &stream, const QString &fileName, const Pr
stream << qtHeaders();
stream << "\n";
if (!provider.prefixText.isEmpty())
- stream << provider.prefixText.join(QLatin1Char('\n')) << "\n\n";
+ stream << provider.prefixText.join(u'\n') << "\n\n";
stream << "#endif\n\n";
/* the first guard is the usual one, the second is required
@@ -130,6 +126,12 @@ static void writePrologue(QTextStream &stream, const QString &fileName, const Pr
<< "#define TRACEPOINT_INCLUDE \"" << fileName << "\"\n\n";
stream << "#include <lttng/tracepoint.h>\n\n";
+
+ const QString namespaceGuard = guard + QStringLiteral("_USE_NAMESPACE");
+ stream << "#if !defined(" << namespaceGuard << ")\n"
+ << "#define " << namespaceGuard << "\n"
+ << "QT_USE_NAMESPACE\n"
+ << "#endif // " << namespaceGuard << "\n\n";
}
static void writeEpilogue(QTextStream &stream, const QString &fileName)
@@ -141,12 +143,12 @@ static void writeEpilogue(QTextStream &stream, const QString &fileName)
}
static void writeWrapper(QTextStream &stream,
- const Tracepoint &tracepoint, const QString &providerName)
+ const Tracepoint &tracepoint, const Provider &provider)
{
const QString argList = formatFunctionSignature(tracepoint.args);
- const QString paramList = formatParameterList(tracepoint.args, LTTNG);
+ const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, LTTNG);
const QString &name = tracepoint.name;
- const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper();
+ const QString includeGuard = QStringLiteral("TP_%1_%2").arg(provider.name).arg(name).toUpper();
/* prevents the redefinion of the inline wrapper functions
* once LTTNG recursively includes this header file
@@ -154,28 +156,30 @@ static void writeWrapper(QTextStream &stream,
stream << "\n"
<< "#ifndef " << includeGuard << "\n"
<< "#define " << includeGuard << "\n"
+ << "QT_BEGIN_NAMESPACE\n"
<< "namespace QtPrivate {\n";
stream << "inline void trace_" << name << "(" << argList << ")\n"
<< "{\n"
- << " tracepoint(" << providerName << ", " << name << paramList << ");\n"
+ << " tracepoint(" << provider.name << ", " << name << paramList << ");\n"
<< "}\n";
stream << "inline void do_trace_" << name << "(" << argList << ")\n"
<< "{\n"
- << " do_tracepoint(" << providerName << ", " << name << paramList << ");\n"
+ << " do_tracepoint(" << provider.name << ", " << name << paramList << ");\n"
<< "}\n";
stream << "inline bool trace_" << name << "_enabled()\n"
<< "{\n"
- << " return tracepoint_enabled(" << providerName << ", " << name << ");\n"
+ << " return tracepoint_enabled(" << provider.name << ", " << name << ");\n"
<< "}\n";
stream << "} // namespace QtPrivate\n"
+ << "QT_END_NAMESPACE\n"
<< "#endif // " << includeGuard << "\n\n";
}
-static void writeTracepoint(QTextStream &stream,
+static void writeTracepoint(QTextStream &stream, const Provider &provider,
const Tracepoint &tracepoint, const QString &providerName)
{
stream << "TRACEPOINT_EVENT(\n"
@@ -185,8 +189,13 @@ static void writeTracepoint(QTextStream &stream,
const char *comma = nullptr;
- for (const Tracepoint::Argument &arg : tracepoint.args) {
- stream << comma << arg.type << ", " << arg.name;
+ for (int i = 0; i < tracepoint.args.size(); i++) {
+ const auto &arg = tracepoint.args[i];
+ const auto &field = tracepoint.fields[i];
+ if (field.backendType == Tracepoint::Field::FlagType)
+ stream << comma << "QByteArray, " << arg.name;
+ else
+ stream << comma << arg.type << ", " << arg.name;
comma = ", ";
}
@@ -197,18 +206,82 @@ static void writeTracepoint(QTextStream &stream,
for (const Tracepoint::Field &f : tracepoint.fields) {
stream << newline;
- writeCtfMacro(stream, f);
+ writeCtfMacro(stream, provider, f);
newline = "\n ";
}
stream << ")\n)\n\n";
}
+static void writeEnums(QTextStream &stream, const Provider &provider)
+{
+ for (const auto &e : provider.enumerations) {
+ stream << "TRACEPOINT_ENUM(\n"
+ << " " << provider.name << ",\n"
+ << " " << typeToTypeName(e.name) << ",\n"
+ << " TP_ENUM_VALUES(\n";
+ QList<int> handledValues;
+ for (const auto &v : e.values) {
+ if (v.range > 0) {
+ stream << " ctf_enum_range(\"" << v.name << "\", " << v.value << ", " << v.range << ")\n";
+ } else if (!handledValues.contains(v.value)) {
+ stream << " ctf_enum_value(\"" << aggregateListValues(v.value, e.values) << "\", " << v.value << ")\n";
+ handledValues.append(v.value);
+ }
+ }
+ stream << " )\n)\n\n";
+ }
+}
+
+static void writeFlags(QTextStream &stream, const Provider &provider)
+{
+ for (const auto &f : provider.flags) {
+ stream << "TRACEPOINT_ENUM(\n"
+ << " " << provider.name << ",\n"
+ << " " << typeToTypeName(f.name) << ",\n"
+ << " TP_ENUM_VALUES(\n";
+ QList<int> handledValues;
+ for (const auto &v : f.values) {
+ if (!handledValues.contains(v.value)) {
+ stream << " ctf_enum_value(\"" << aggregateListValues(v.value, f.values) << "\", " << v.value << ")\n";
+ handledValues.append(v.value);
+ }
+ }
+ stream << " )\n)\n\n";
+ }
+
+ // converters
+ const QString includeGuard = QStringLiteral("TP_%1_CONVERTERS").arg(provider.name).toUpper();
+ stream << "\n"
+ << "#ifndef " << includeGuard << "\n"
+ << "#define " << includeGuard << "\n";
+ stream << "QT_BEGIN_NAMESPACE\n";
+ stream << "namespace QtPrivate {\n";
+ for (const auto &f : provider.flags) {
+ stream << "inline QByteArray trace_convert_" << typeToTypeName(f.name) << "(" << f.name << " val)\n";
+ stream << "{\n";
+ stream << " QByteArray ret;\n";
+ stream << " if (val == 0) { ret.append((char)0); return ret; }\n";
+
+ for (const auto &v : f.values) {
+ if (!v.value)
+ continue;
+ stream << " if (val & " << (1 << (v.value - 1)) << ") { ret.append((char)" << v.value << "); };\n";
+ }
+ stream << " return ret;\n";
+ stream << "}\n";
+
+ }
+ stream << "} // namespace QtPrivate\n"
+ << "QT_END_NAMESPACE\n\n"
+ << "#endif // " << includeGuard << "\n\n";
+}
+
static void writeTracepoints(QTextStream &stream, const Provider &provider)
{
for (const Tracepoint &t : provider.tracepoints) {
- writeTracepoint(stream, t, provider.name);
- writeWrapper(stream, t, provider.name);
+ writeTracepoint(stream, provider, t, provider.name);
+ writeWrapper(stream, t, provider);
}
}
@@ -219,6 +292,9 @@ void writeLttng(QFile &file, const Provider &provider)
const QString fileName = QFileInfo(file.fileName()).fileName();
writePrologue(stream, fileName, provider);
+ writeEnums(stream, provider);
+ writeFlags(stream, provider);
writeTracepoints(stream, provider);
writeEpilogue(stream, fileName);
}
+
diff --git a/src/tools/tracegen/lttng.h b/src/tools/tracegen/lttng.h
index 0307b375bc..c36c70a1d4 100644
--- a/src/tools/tracegen/lttng.h
+++ b/src/tools/tracegen/lttng.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef LTTNG_H
#define LTTNG_H
diff --git a/src/tools/tracegen/panic.cpp b/src/tools/tracegen/panic.cpp
index ac95b2e9e8..fa4e6b3ee3 100644
--- a/src/tools/tracegen/panic.cpp
+++ b/src/tools/tracegen/panic.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "panic.h"
@@ -57,16 +21,3 @@ void panic(const char *fmt, ...)
exit(EXIT_FAILURE);
}
-
-void justified_worry(const char *fmt, ...)
-{
- va_list ap;
-
- fprintf(stderr, "tracegen: warning: ");
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fputc('\n', stderr);
-}
diff --git a/src/tools/tracegen/panic.h b/src/tools/tracegen/panic.h
index 58e56c0116..ee635a8aeb 100644
--- a/src/tools/tracegen/panic.h
+++ b/src/tools/tracegen/panic.h
@@ -1,46 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PANIC_H
#define PANIC_H
void panic(const char *fmt, ...);
-void justified_worry(const char *fmt, ...);
#endif // PANIC_H
diff --git a/src/tools/tracegen/provider.cpp b/src/tools/tracegen/provider.cpp
index 7984fa12a7..bdd669c9cd 100644
--- a/src/tools/tracegen/provider.cpp
+++ b/src/tools/tracegen/provider.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "provider.h"
#include "panic.h"
@@ -45,6 +9,9 @@
#include <qtextstream.h>
#include <qregularexpression.h>
#include <qstring.h>
+#include <qtpreprocessorsupport.h>
+
+using namespace Qt::StringLiterals;
#ifdef TRACEGEN_DEBUG
#include <qdebug.h>
@@ -129,89 +96,115 @@ static QString removeBraces(QString type)
return type.remove(rx);
}
-static Tracepoint::Field::BackendType backendType(QString rawType)
+#define TYPEDATA_ENTRY(type, backendType) \
+{ QT_STRINGIFY(type), backendType }
+
+static Tracepoint::Field::Type backendType(QString rawType)
{
- static const struct {
+ static const struct TypeData {
const char *type;
- Tracepoint::Field::BackendType backendType;
+ Tracepoint::Field::Type backendType;
} typeTable[] = {
- { "bool", Tracepoint::Field::Integer },
- { "short_int", Tracepoint::Field::Integer },
- { "signed_short", Tracepoint::Field::Integer },
- { "signed_short_int", Tracepoint::Field::Integer },
- { "unsigned_short", Tracepoint::Field::Integer },
- { "unsigned_short_int", Tracepoint::Field::Integer },
- { "int", Tracepoint::Field::Integer },
- { "signed", Tracepoint::Field::Integer },
- { "signed_int", Tracepoint::Field::Integer },
- { "unsigned", Tracepoint::Field::Integer },
- { "unsigned_int", Tracepoint::Field::Integer },
- { "long", Tracepoint::Field::Integer },
- { "long_int", Tracepoint::Field::Integer },
- { "signed_long", Tracepoint::Field::Integer },
- { "signed_long_int", Tracepoint::Field::Integer },
- { "unsigned_long", Tracepoint::Field::Integer },
- { "unsigned_long_int", Tracepoint::Field::Integer },
- { "long_long", Tracepoint::Field::Integer },
- { "long_long_int", Tracepoint::Field::Integer },
- { "signed_long_long", Tracepoint::Field::Integer },
- { "signed_long_long_int", Tracepoint::Field::Integer },
- { "unsigned_long_long", Tracepoint::Field::Integer },
- { "char", Tracepoint::Field::Integer },
- { "intptr_t", Tracepoint::Field::IntegerHex },
- { "uintptr_t", Tracepoint::Field::IntegerHex },
- { "std::intptr_t", Tracepoint::Field::IntegerHex },
- { "std::uintptr_t", Tracepoint::Field::IntegerHex },
- { "float", Tracepoint::Field::Float },
- { "double", Tracepoint::Field::Float },
- { "long_double", Tracepoint::Field::Float },
- { "QString", Tracepoint::Field::QtString },
- { "QByteArray", Tracepoint::Field::QtByteArray },
- { "QUrl", Tracepoint::Field::QtUrl },
- { "QRect", Tracepoint::Field::QtRect }
+ TYPEDATA_ENTRY(short_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_short, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_short_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned_short, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned_short_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(long_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_long, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_long_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned_long, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned_long_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(long_long, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(long_long_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_long_long, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed_long_long_int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned_long_long, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(bool, Tracepoint::Field::Boolean),
+ TYPEDATA_ENTRY(int, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(signed, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(unsigned, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(long, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(qint64, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(char, Tracepoint::Field::Integer),
+ TYPEDATA_ENTRY(intptr_t, Tracepoint::Field::IntegerHex),
+ TYPEDATA_ENTRY(uintptr_t, Tracepoint::Field::IntegerHex),
+ TYPEDATA_ENTRY(std::intptr_t, Tracepoint::Field::IntegerHex),
+ TYPEDATA_ENTRY(std::uintptr_t, Tracepoint::Field::IntegerHex),
+ TYPEDATA_ENTRY(float, Tracepoint::Field::Float),
+ TYPEDATA_ENTRY(double, Tracepoint::Field::Float),
+ TYPEDATA_ENTRY(long double, Tracepoint::Field::Float),
+ TYPEDATA_ENTRY(QString, Tracepoint::Field::QtString),
+ TYPEDATA_ENTRY(QByteArray, Tracepoint::Field::QtByteArray),
+ TYPEDATA_ENTRY(QUrl, Tracepoint::Field::QtUrl),
+ TYPEDATA_ENTRY(QRect, Tracepoint::Field::QtRect),
+ TYPEDATA_ENTRY(QSize, Tracepoint::Field::QtSize),
+ TYPEDATA_ENTRY(QRectF, Tracepoint::Field::QtRectF),
+ TYPEDATA_ENTRY(QSizeF, Tracepoint::Field::QtSizeF)
};
auto backendType = [](const QString &rawType) {
static const size_t tableSize = sizeof (typeTable) / sizeof (typeTable[0]);
for (size_t i = 0; i < tableSize; ++i) {
- if (rawType == QLatin1String(typeTable[i].type))
- return typeTable[i].backendType;
+ if (rawType == QLatin1StringView(typeTable[i].type))
+ return typeTable[i];
}
- return Tracepoint::Field::Unknown;
+ TypeData unknown = { nullptr, Tracepoint::Field::Unknown };
+ return unknown;
};
- if (arrayLength(rawType) > 0)
- return Tracepoint::Field::Array;
+ int arrayLen = arrayLength(rawType);
+ if (arrayLen > 0)
+ rawType = removeBraces(rawType);
if (!sequenceLength(rawType).isNull())
return Tracepoint::Field::Sequence;
static const QRegularExpression constMatch(QStringLiteral("\\bconst\\b"));
rawType.remove(constMatch);
- rawType.remove(QLatin1Char('&'));
+ rawType.remove(u'&');
static const QRegularExpression ptrMatch(QStringLiteral("\\s*\\*\\s*"));
rawType.replace(ptrMatch, QStringLiteral("_ptr"));
rawType = rawType.trimmed();
rawType.replace(QStringLiteral(" "), QStringLiteral("_"));
- if (rawType == QLatin1String("char_ptr"))
+ if (rawType == "char_ptr"_L1)
return Tracepoint::Field::String;
- if (rawType.endsWith(QLatin1String("_ptr")))
+ if (rawType.endsWith("_ptr"_L1))
return Tracepoint::Field::Pointer;
- return backendType(rawType);
+ TypeData d = backendType(rawType);
+ return d.backendType;
}
-static Tracepoint parseTracepoint(const QString &name, const QStringList &args,
+static Tracepoint parseTracepoint(const Provider &provider, const QString &name, const QStringList &args,
const QString &fileName, const int lineNumber)
{
Tracepoint t;
t.name = name;
+ auto findEnumeration = [](const QList<TraceEnum> &enums, const QString &name) {
+ for (const auto &e : enums) {
+ if (e.name == name)
+ return e;
+ }
+ return TraceEnum();
+ };
+ auto findFlags = [](const QList<TraceFlags> &flags, const QString &name) {
+ for (const auto &f : flags) {
+ if (f.name == name)
+ return f;
+ }
+ return TraceFlags();
+ };
+
+
if (args.isEmpty())
return t;
@@ -247,14 +240,23 @@ static Tracepoint parseTracepoint(const QString &name, const QStringList &args,
t.args << std::move(a);
- Tracepoint::Field f;
- f.backendType = backendType(type);
- f.paramType = removeBraces(type);
- f.name = name;
- f.arrayLen = arrayLen;
- f.seqLen = sequenceLength(type);
+ Tracepoint::Field field;
+ const TraceEnum &e = findEnumeration(provider.enumerations, type);
+ const TraceFlags &f = findFlags(provider.flags, type);
+ if (!e.name.isEmpty()) {
+ field.backendType = Tracepoint::Field::EnumeratedType;
+ field.enumValueSize = e.valueSize;
+ } else if (!f.name.isEmpty()) {
+ field.backendType = Tracepoint::Field::FlagType;
+ } else {
+ field.backendType = backendType(type);
+ }
+ field.paramType = removeBraces(type);
+ field.name = name;
+ field.arrayLen = arrayLen;
+ field.seqLen = sequenceLength(type);
- t.fields << std::move(f);
+ t.fields << std::move(field);
++i;
}
@@ -262,6 +264,31 @@ static Tracepoint parseTracepoint(const QString &name, const QStringList &args,
return t;
}
+static int minumumValueSize(int min, int max)
+{
+ if (min < 0) {
+ if (min >= std::numeric_limits<char>::min() && max <= std::numeric_limits<char>::max())
+ return 8;
+ if (min >= std::numeric_limits<short>::min() && max <= std::numeric_limits<short>::max())
+ return 16;
+ return 32;
+ }
+ if (max <= std::numeric_limits<unsigned char>::max())
+ return 8;
+ if (max <= std::numeric_limits<unsigned short>::max())
+ return 16;
+ return 32;
+}
+
+static bool isPow2OrZero(quint32 value)
+{
+ return (value & (value - 1)) == 0;
+}
+
+static quint32 pow2Log2(quint32 v) {
+ return 32 - qCountLeadingZeroBits(v);
+}
+
Provider parseProvider(const QString &filename)
{
QFile f(filename);
@@ -272,35 +299,140 @@ Provider parseProvider(const QString &filename)
QTextStream s(&f);
static const QRegularExpression tracedef(QStringLiteral("^([A-Za-z][A-Za-z0-9_]*)\\((.*)\\)$"));
+ static const QRegularExpression enumenddef(QStringLiteral("^} ?([A-Za-z][A-Za-z0-9_:]*);"));
+ static const QRegularExpression enumdef(QStringLiteral("^([A-Za-z][A-Za-z0-9_]*)( *= *([xabcdef0-9]*))?"));
+ static const QRegularExpression rangedef(QStringLiteral("^RANGE\\(([A-Za-z][A-Za-z0-9_]*) ?, ?([0-9]*) ?... ?([0-9]*) ?\\)"));
Provider provider;
provider.name = QFileInfo(filename).baseName();
bool parsingPrefixText = false;
+ bool parsingEnum = false;
+ bool parsingFlags = false;
+ TraceEnum currentEnum;
+ TraceFlags currentFlags;
+ int currentEnumValue = 0;
+ int minEnumValue = std::numeric_limits<int>::max();
+ int maxEnumValue = std::numeric_limits<int>::min();
for (int lineNumber = 1; !s.atEnd(); ++lineNumber) {
QString line = s.readLine().trimmed();
- if (line == QLatin1String("{")) {
+ if (line == "{"_L1) {
parsingPrefixText = true;
continue;
- } else if (parsingPrefixText && line == QLatin1String("}")) {
+ } else if (parsingPrefixText && line == "}"_L1) {
parsingPrefixText = false;
continue;
} else if (parsingPrefixText) {
provider.prefixText.append(line);
continue;
+ } else if (line == "ENUM {"_L1) {
+ parsingEnum = true;
+ continue;
+ } else if (line == "FLAGS {"_L1) {
+ parsingFlags = true;
+ continue;
+ } else if (line.startsWith("}"_L1) && (parsingEnum || parsingFlags)) {
+ auto match = enumenddef.match(line);
+ if (match.hasMatch()) {
+ if (parsingEnum) {
+ currentEnum.name = match.captured(1);
+ currentEnum.valueSize = minumumValueSize(minEnumValue, maxEnumValue);
+ provider.enumerations.push_back(currentEnum);
+ currentEnum = TraceEnum();
+ parsingEnum = false;
+ } else {
+ currentFlags.name = match.captured(1);
+ provider.flags.push_back(currentFlags);
+ currentFlags = TraceFlags();
+ parsingFlags = false;
+ }
+
+ minEnumValue = std::numeric_limits<int>::max();
+ maxEnumValue = std::numeric_limits<int>::min();
+ } else {
+ panic("Syntax error while processing '%s' line %d:\n"
+ " '%s' end of enum/flags does not match",
+ qPrintable(filename), lineNumber,
+ qPrintable(line));
+ }
+
+ continue;
}
- if (line.isEmpty() || line.startsWith(QLatin1Char('#')))
+ if (line.isEmpty() || line.startsWith(u'#'))
continue;
+ if (parsingEnum || parsingFlags) {
+ auto m = enumdef.match(line);
+ if (parsingEnum && line.startsWith(QStringLiteral("RANGE"))) {
+ auto m = rangedef.match(line);
+ if (m.hasMatch()) {
+ TraceEnum::EnumValue value;
+ value.name = m.captured(1);
+ value.value = m.captured(2).toInt();
+ value.range = m.captured(3).toInt();
+ currentEnumValue = value.range + 1;
+ currentEnum.values.push_back(value);
+ maxEnumValue = qMax(maxEnumValue, value.range);
+ minEnumValue = qMin(minEnumValue, value.value);
+ }
+ } else if (m.hasMatch()) {
+ if (m.hasCaptured(3)) {
+ if (parsingEnum) {
+ TraceEnum::EnumValue value;
+ value.name = m.captured(1);
+ value.value = m.captured(3).toInt();
+ value.range = 0;
+ currentEnumValue = value.value + 1;
+ currentEnum.values.push_back(value);
+ maxEnumValue = qMax(maxEnumValue, value.value);
+ minEnumValue = qMin(minEnumValue, value.value);
+ } else {
+ TraceFlags::FlagValue value;
+ value.name = m.captured(1);
+ if (m.captured(3).startsWith(QStringLiteral("0x")))
+ value.value = m.captured(3).toInt(nullptr, 16);
+ else
+ value.value = m.captured(3).toInt();
+ if (!isPow2OrZero(value.value)) {
+ printf("Warning: '%s' line %d:\n"
+ " '%s' flag value is not power of two.\n",
+ qPrintable(filename), lineNumber,
+ qPrintable(line));
+ } else {
+ value.value = pow2Log2(value.value);
+ currentFlags.values.push_back(value);
+ }
+ }
+ } else {
+ maxEnumValue = qMax(maxEnumValue, currentEnumValue);
+ minEnumValue = qMin(minEnumValue, currentEnumValue);
+ if (parsingEnum) {
+ currentEnum.values.push_back({m.captured(0), currentEnumValue++, 0});
+ } else {
+ panic("Syntax error while processing '%s' line %d:\n"
+ " '%s' flags value not set",
+ qPrintable(filename), lineNumber,
+ qPrintable(line));
+ }
+ }
+ } else {
+ panic("Syntax error while processing '%s' line %d:\n"
+ " '%s' enum/flags does not match",
+ qPrintable(filename), lineNumber,
+ qPrintable(line));
+ }
+ continue;
+ }
+
auto match = tracedef.match(line);
if (match.hasMatch()) {
const QString name = match.captured(1);
const QString argsString = match.captured(2);
- const QStringList args = argsString.split(QLatin1Char(','), Qt::SkipEmptyParts);
+ const QStringList args = argsString.split(u',', Qt::SkipEmptyParts);
- provider.tracepoints << parseTracepoint(name, args, filename, lineNumber);
+ provider.tracepoints << parseTracepoint(provider, name, args, filename, lineNumber);
} else {
panic("Syntax error while processing '%s' line %d:\n"
" '%s' does not look like a tracepoint definition",
diff --git a/src/tools/tracegen/provider.h b/src/tools/tracegen/provider.h
index 99b10b13f1..e5e99b868e 100644
--- a/src/tools/tracegen/provider.h
+++ b/src/tools/tracegen/provider.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROVIDER_H
#define PROVIDER_H
@@ -56,9 +20,9 @@ struct Tracepoint
struct Field
{
- enum BackendType {
- Array,
+ enum Type {
Sequence,
+ Boolean,
Integer,
IntegerHex,
Float,
@@ -68,13 +32,18 @@ struct Tracepoint
QtByteArray,
QtUrl,
QtRect,
+ QtSize,
+ QtRectF,
+ QtSizeF,
+ EnumeratedType,
+ FlagType,
Unknown
};
-
- BackendType backendType;
+ Type backendType;
QString paramType;
QString name;
int arrayLen;
+ int enumValueSize;
QString seqLen;
};
@@ -83,17 +52,41 @@ struct Tracepoint
QList<Field> fields;
};
+struct TraceEnum {
+ QString name;
+ struct EnumValue {
+ QString name;
+ int value;
+ int range;
+ };
+ QList<EnumValue> values;
+ int valueSize;
+};
+
+struct TraceFlags {
+ QString name;
+ struct FlagValue {
+ QString name;
+ int value;
+ };
+ QList<FlagValue> values;
+};
+
+Q_DECLARE_TYPEINFO(TraceEnum, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(TraceFlags, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(Tracepoint::Argument, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(Tracepoint::Field, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(Tracepoint, Q_RELOCATABLE_TYPE);
+
struct Provider
{
QString name;
QList<Tracepoint> tracepoints;
QStringList prefixText;
+ QList<TraceEnum> enumerations;
+ QList<TraceFlags> flags;
};
Provider parseProvider(const QString &filename);
-Q_DECLARE_TYPEINFO(Tracepoint::Argument, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(Tracepoint::Field, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(Tracepoint, Q_RELOCATABLE_TYPE);
-
#endif // PROVIDER_H
diff --git a/src/tools/tracegen/qtheaders.cpp b/src/tools/tracegen/qtheaders.cpp
index eec3488a6d..237c22b237 100644
--- a/src/tools/tracegen/qtheaders.cpp
+++ b/src/tools/tracegen/qtheaders.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qtheaders.h"
diff --git a/src/tools/tracegen/qtheaders.h b/src/tools/tracegen/qtheaders.h
index b80d374ca8..86405c9479 100644
--- a/src/tools/tracegen/qtheaders.h
+++ b/src/tools/tracegen/qtheaders.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QTHEADERS_H
#define QTHEADERS_H
diff --git a/src/tools/tracegen/tracegen.cpp b/src/tools/tracegen/tracegen.cpp
index 978fe406d0..776d81675d 100644
--- a/src/tools/tracegen/tracegen.cpp
+++ b/src/tools/tracegen/tracegen.cpp
@@ -1,43 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "provider.h"
+#include "ctf.h"
#include "lttng.h"
#include "etw.h"
#include "panic.h"
@@ -48,12 +13,13 @@
enum class Target
{
LTTNG,
- ETW
+ ETW,
+ CTF,
};
static inline void usage(int status)
{
- printf("Usage: tracegen <lttng|etw> <input file> <output file>\n");
+ printf("Usage: tracegen <lttng|etw|ctf> <input file> <output file>\n");
exit(status);
}
@@ -70,13 +36,15 @@ static void parseArgs(int argc, char *argv[], Target *target, QString *inFile, Q
*target = Target::LTTNG;
} else if (qstrcmp(targetString, "etw") == 0) {
*target = Target::ETW;
+ } else if (qstrcmp(targetString, "ctf") == 0) {
+ *target = Target::CTF;
} else {
fprintf(stderr, "Invalid target: %s\n", targetString);
usage(EXIT_FAILURE);
}
- *inFile = QLatin1String(argv[2]);
- *outFile = QLatin1String(argv[3]);
+ *inFile = QLatin1StringView(argv[2]);
+ *outFile = QLatin1StringView(argv[3]);
}
int main(int argc, char *argv[])
@@ -97,6 +65,9 @@ int main(int argc, char *argv[])
}
switch (target) {
+ case Target::CTF:
+ writeCtf(out, p);
+ break;
case Target::LTTNG:
writeLttng(out, p);
break;
diff --git a/src/tools/tracepointgen/CMakeLists.txt b/src/tools/tracepointgen/CMakeLists.txt
new file mode 100644
index 0000000000..2f473d376a
--- /dev/null
+++ b/src/tools/tracepointgen/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tracepointgen Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name tracepointgen)
+qt_internal_add_tool(${target_name}
+ CORE_LIBRARY Bootstrap
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
+ TOOLS_TARGET Core
+ SOURCES
+ tracepointgen.cpp tracepointgen.h
+ parser.cpp parser.h
+)
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/tracepointgen/parser.cpp b/src/tools/tracepointgen/parser.cpp
new file mode 100644
index 0000000000..ad94b9a433
--- /dev/null
+++ b/src/tools/tracepointgen/parser.cpp
@@ -0,0 +1,609 @@
+// 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 "tracepointgen.h"
+#include "parser.h"
+#include <qtextstream.h>
+#include <qregularexpression.h>
+#include <qfileinfo.h>
+
+static void removeOffsetRange(qsizetype begin, qsizetype end, QList<LineNumber> &offsets)
+{
+ qsizetype count = end - begin;
+ qsizetype i = 0;
+ DEBUGPRINTF2(printf("tracepointgen: removeOffsetRange: %llu %llu\n", begin, end));
+ while (i < offsets.size()) {
+ LineNumber &cur = offsets[i];
+ if (begin > cur.end) {
+ i++;
+ } else if (begin >= cur.begin && begin <= cur.end) {
+ cur.end = begin;
+ i++;
+ } else if (begin < cur.begin && end > cur.end) {
+ offsets.remove(i);
+ DEBUGPRINTF2(printf("tracepointgen: removeOffsetRange: %llu, %llu, %d\n", cur.begin, cur.end, cur.line));
+ } else if (end >= cur.begin && end <= cur.end) {
+ cur.begin = begin;
+ cur.end -= count;
+ i++;
+ } else if (end < cur.begin) {
+ cur.begin -= count;
+ cur.end -= count;
+ i++;
+ }
+ }
+}
+
+static bool findSpaceRange(const QString &data, qsizetype &offset, qsizetype &end) {
+ qsizetype cur = data.indexOf(QLatin1Char(' '), offset);
+ if (cur >= 0) {
+ qsizetype i = cur + 1;
+ while (data.constData()[i] == QLatin1Char(' ')) i++;
+ if (i - cur > 1) {
+ offset = cur;
+ end = i - 1;
+ return true;
+ }
+ cur = data.indexOf(QLatin1Char(' '), cur + 1);
+ }
+ return false;
+}
+
+static void simplifyData(QString &data, QList<LineNumber> &offsets)
+{
+ qsizetype offset = data.indexOf(QStringLiteral("//"));
+ while (offset >= 0) {
+ qsizetype endOfLine = data.indexOf(QLatin1Char('\n'), offset);
+ if (endOfLine == -1)
+ endOfLine = data.length();
+ removeOffsetRange(offset, endOfLine, offsets);
+ data.remove(offset, endOfLine - offset);
+ offset = data.indexOf(QStringLiteral("//"), offset);
+ }
+ offset = data.indexOf(QStringLiteral("/*"));
+ while (offset >= 0) {
+ qsizetype endOfComment = data.indexOf(QStringLiteral("*/"), offset);
+ if (endOfComment == -1)
+ break;
+ removeOffsetRange(offset, endOfComment + 2, offsets);
+ data.remove(offset, endOfComment - offset + 2);
+ offset = data.indexOf(QStringLiteral("/*"), offset);
+ }
+ offset = 0;
+ qsizetype end = 0;
+ data.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ while (findSpaceRange(data, offset, end)) {
+ removeOffsetRange(offset, end, offsets);
+ data.remove(offset, end - offset);
+ }
+}
+
+static void simplifyData(QString &data)
+{
+ qsizetype offset = data.indexOf(QStringLiteral("//"));
+ while (offset >= 0) {
+ qsizetype endOfLine = data.indexOf(QLatin1Char('\n'), offset);
+ if (endOfLine == -1)
+ endOfLine = data.length();
+ data.remove(offset, endOfLine - offset);
+ offset = data.indexOf(QStringLiteral("//"), offset);
+ }
+ offset = data.indexOf(QStringLiteral("/*"));
+ while (offset >= 0) {
+ qsizetype endOfComment = data.indexOf(QStringLiteral("*/"), offset);
+ if (endOfComment == -1)
+ break;
+ data.remove(offset, endOfComment - offset + 2);
+ offset = data.indexOf(QStringLiteral("/*"), offset);
+ }
+ offset = 0;
+ qsizetype end = 0;
+ while (findSpaceRange(data, offset, end))
+ data.remove(offset, end - offset);
+}
+
+static QString preprocessMetadata(const QString &in)
+{
+ DEBUGPRINTF(printf("in: %s\n", qPrintable(in)));
+ QList<QString> lines = in.split(QLatin1Char('\\'));
+ QString out;
+ for (int i = 0; i < lines.size(); i++) {
+ QString l = lines.at(i).simplified();
+ DEBUGPRINTF(printf("line: %s\n", qPrintable(l)));
+ if (l.length() < 2)
+ continue;
+ if (l.startsWith(QStringLiteral("\"")))
+ l = l.right(l.length() - 1);
+ if (l.endsWith(QStringLiteral("\"")))
+ l = l.left(l.length() - 1);
+ l = l.simplified();
+
+ if (l.length() > 1) {
+ if (out.size() > 0)
+ out.append(QLatin1Char('\n'));
+ out.append(l);
+ }
+ }
+ DEBUGPRINTF(printf("out: %s\n", qPrintable(out)));
+ return out;
+}
+
+int Parser::lineNumber(qsizetype offset) const
+{
+ DEBUGPRINTF(printf("tracepointgen: lineNumber: offset %llu, line count: %llu\n", offset , m_offsets.size()));
+ for (const auto line : m_offsets) {
+ DEBUGPRINTF(printf("tracepointgen: lineNumber: %llu %llu %d\n", line.begin, line.end, line.line));
+ if (offset >= line.begin && offset <= line.end)
+ return line.line;
+ }
+ return 0;
+}
+
+void Parser::parseParamReplace(const QString &data, qsizetype offset, const QString &name)
+{
+ Replace rep;
+ qsizetype beginBrace = data.indexOf(QLatin1Char('('), offset);
+ qsizetype endBrace = data.indexOf(QLatin1Char(')'), beginBrace);
+ QString params = data.mid(beginBrace + 1, endBrace - beginBrace -1);
+ int punc = params.indexOf(QLatin1Char(','));
+ if (punc < 0)
+ panic("Syntax error in Q_TRACE_PARAM_REPLACE at file %s, line %llu", qPrintable(name), lineNumber(offset));
+ rep.in = params.left(punc).simplified();
+ rep.out = params.right(params.length() - punc - 1).simplified();
+ if (rep.in.endsWith(QLatin1Char('*')) || rep.out.endsWith(QLatin1Char(']')))
+ rep.out.append(QLatin1Char(' '));
+ DEBUGPRINTF(printf("tracepointgen: replace: %s with %s\n", qPrintable(rep.in), qPrintable(rep.out)));
+ m_replaces.push_back(rep);
+}
+
+void Parser::parseInstrument(const QString &data, qsizetype offset)
+{
+ qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset);
+ qsizetype endOfProvider = data.indexOf(QLatin1Char(')'), beginOfProvider);
+ Function func;
+ QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified();
+ if (provider != m_provider)
+ return;
+
+ qsizetype classMarker = data.indexOf(QStringLiteral("::"), endOfProvider);
+ qsizetype beginOfFunctionMarker = data.indexOf(QLatin1Char('{'), classMarker);
+ QString begin = data.mid(endOfProvider + 1, classMarker - endOfProvider - 1);
+ QString end = data.mid(classMarker + 2, beginOfFunctionMarker - classMarker - 2);
+ int spaceIndex = begin.lastIndexOf(QLatin1Char(' '));
+ if (spaceIndex == -1)
+ func.className = begin;
+ else
+ func.className = begin.mid(spaceIndex + 1, begin.length() - spaceIndex - 1);
+ qsizetype braceIndex = end.indexOf(QLatin1Char('('));
+ spaceIndex = end.indexOf(QLatin1Char(' '));
+ if (spaceIndex < braceIndex)
+ func.functionName = end.left(spaceIndex).simplified();
+ else
+ func.functionName = end.left(braceIndex).simplified();
+
+ qsizetype lastBraceIndex = end.lastIndexOf(QLatin1Char(')'));
+ func.functionParameters = end.mid(braceIndex + 1, lastBraceIndex - braceIndex - 1).simplified();
+
+ DEBUGPRINTF(printf("tracepointgen: %s(%s)\n", qPrintable(func.functionName), qPrintable(func.functionParameters)));
+
+ m_functions.push_back(func);
+}
+
+void Parser::parsePoint(const QString &data, qsizetype offset)
+{
+ qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset);
+ qsizetype endOfProvider = data.indexOf(QLatin1Char(','), beginOfProvider);
+ Point point;
+ QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified();
+ if (provider != m_provider)
+ return;
+
+ qsizetype endOfPoint = data.indexOf(QLatin1Char(','), endOfProvider + 1);
+ qsizetype endOfPoint2 = data.indexOf(QLatin1Char(')'), endOfProvider + 1);
+ bool params = true;
+ if (endOfPoint == -1 || endOfPoint2 < endOfPoint) {
+ endOfPoint = endOfPoint2;
+ params = false;
+ }
+ point.name = data.mid(endOfProvider + 1, endOfPoint - endOfProvider - 1).simplified();
+ if (params) {
+ int endOfParams = data.indexOf(QLatin1Char(')'), endOfPoint);
+ point.parameters = data.mid(endOfPoint + 1, endOfParams - endOfPoint - 1).simplified();
+ }
+
+ DEBUGPRINTF(printf("tracepointgen: %s(%s)\n", qPrintable(point.name), qPrintable(point.parameters)));
+
+ m_points.push_back(point);
+}
+
+void Parser::parsePrefix(const QString &data, qsizetype offset)
+{
+ qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset);
+ qsizetype endOfProvider = data.indexOf(QLatin1Char(','), beginOfProvider);
+ QString prefix;
+ QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified();
+ if (provider != m_provider)
+ return;
+
+ qsizetype endOfPoint = data.indexOf(QLatin1Char(')'), endOfProvider + 1);
+ prefix = data.mid(endOfProvider + 1, endOfPoint - endOfProvider - 1).simplified();
+
+ DEBUGPRINTF(printf("tracepointgen: prefix: %s\n", qPrintable(prefix)));
+
+ if (!m_prefixes.contains(prefix))
+ m_prefixes.push_back(preprocessMetadata(prefix));
+}
+
+QStringList Parser::findEnumValues(const QString &name, const QStringList &includes)
+{
+ QStringList split = name.split(QStringLiteral("::"));
+ QString enumName = split.last();
+ DEBUGPRINTF(printf("searching for %s\n", qPrintable(name)));
+ QStringList ret;
+ for (const QString &filename : includes) {
+ QFile input(filename);
+ if (!input.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ DEBUGPRINTF(printf("Cannot open '%s' for reading: %s\n",
+ qPrintable(filename), qPrintable(input.errorString())));
+ return ret;
+ }
+ QString data;
+ QTextStream stream(&input);
+ while (!stream.atEnd()) {
+ QString line = stream.readLine().trimmed();
+ data += line + QLatin1Char('\n');
+ }
+ simplifyData(data);
+
+ int pos = 0;
+ bool valid = true;
+ for (int i = 0; i < split.size() - 1; i++) {
+ QRegularExpression macro(QStringLiteral("(struct|class|namespace) +([A-Za-z0-9_]*)? +([A-Za-z0-9]*;?)"));
+ QRegularExpressionMatchIterator m = macro.globalMatch(data);
+ bool found = false;
+ while (m.hasNext() && !found) {
+ QRegularExpressionMatch match = m.next();
+ QString n = match.captured(2);
+ if (!n.endsWith(QLatin1Char(';')) && n == split[i] && match.capturedStart(2) > pos) {
+ pos = match.capturedStart(2);
+ found = true;
+ break;
+ }
+ if (match.hasCaptured(3)) {
+ n = match.captured(3);
+ if (!n.endsWith(QLatin1Char(';')) && n == split[i] && match.capturedStart(3) > pos) {
+ pos = match.capturedStart(3);
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid) {
+ QRegularExpression macro(QStringLiteral("enum +([A-Za-z0-9_]*)"));
+ QRegularExpressionMatchIterator m = macro.globalMatch(data);
+ while (m.hasNext()) {
+ QRegularExpressionMatch match = m.next();
+
+ if (match.capturedStart() < pos)
+ continue;
+
+ QString n = match.captured(1);
+
+ if (n == enumName) {
+ DEBUGPRINTF(printf("Found enum: %s\n", qPrintable(n)));
+ int begin = data.indexOf(QLatin1Char('{'), match.capturedEnd());
+ int end = data.indexOf(QLatin1Char('}'), begin);
+ QString block = data.mid(begin + 1, end - begin - 1);
+ const QStringList enums = block.split(QLatin1Char('\n'));
+ for (const auto &e : enums) {
+ const auto trimmed = e.trimmed();
+ if (!trimmed.isEmpty() && !trimmed.startsWith(QLatin1Char('#')))
+ ret << trimmed;
+ }
+
+ return ret;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+struct EnumNameValue
+{
+ QString name;
+ QString valueStr;
+ int value;
+};
+
+static QList<EnumNameValue> enumsToValues(const QStringList &values)
+{
+ int cur = 0;
+ QList<EnumNameValue> ret;
+ for (const QString &value : values) {
+ EnumNameValue r;
+ if (value.contains(QLatin1Char('='))) {
+ size_t offset = value.indexOf(QLatin1Char('='));
+ r.name = value.left(offset).trimmed();
+ QString val = value.right(value.length() - offset - 1).trimmed();
+ if (val.endsWith(QLatin1Char(',')))
+ val = val.left(val.length() - 1);
+ bool valid = false;
+ int integer = val.toInt(&valid);
+ if (!valid)
+ integer = val.toInt(&valid, 16);
+ if (valid) {
+ cur = r.value = integer;
+ ret << r;
+ } else {
+ auto iter = std::find_if(ret.begin(), ret.end(), [&val](const EnumNameValue &elem){
+ return elem.name == val;
+ });
+ if (iter != ret.end()) {
+ cur = r.value = iter->value;
+ ret << r;
+ } else {
+ DEBUGPRINTF(printf("Invalid value: %s %s\n", qPrintable(r.name), qPrintable(value)));
+ }
+ }
+ } else {
+ if (value.endsWith(QLatin1Char(',')))
+ r.name = value.left(value.length() - 1);
+ else
+ r.name = value;
+ r.value = ++cur;
+ ret << r;
+ }
+ }
+ return ret;
+}
+
+void Parser::parseMetadata(const QString &data, qsizetype offset, const QStringList &includes)
+{
+ qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset);
+ qsizetype endOfProvider = data.indexOf(QLatin1Char(','), beginOfProvider);
+ QString metadata;
+ QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified();
+ if (provider != m_provider)
+ return;
+
+ qsizetype endOfPoint = data.indexOf(QLatin1Char(')'), endOfProvider + 1);
+ metadata = data.mid(endOfProvider + 1, endOfPoint - endOfProvider - 1).simplified();
+
+ DEBUGPRINTF(printf("tracepointgen: metadata: %s", qPrintable(metadata)));
+
+ QString preprocessed = preprocessMetadata(metadata);
+
+ DEBUGPRINTF2(printf("preprocessed %s\n", qPrintable(preprocessed)));
+
+ QRegularExpression macro(QStringLiteral("([A-Z]*) ?{ ?([A-Za-z0-9=_,. ]*) ?} ?([A-Za-z0-9_:]*) ?;"));
+ QRegularExpressionMatchIterator i = macro.globalMatch(preprocessed);
+ qsizetype prev = 0;
+ while (i.hasNext()) {
+ QRegularExpressionMatch match = i.next();
+ QString values = match.captured(2).trimmed();
+ int cur = match.capturedStart();
+ if (cur > prev)
+ m_metadata.append(preprocessed.mid(prev, cur - prev));
+
+ prev = match.capturedEnd() + 1;
+ DEBUGPRINTF2(printf("values: %s\n", qPrintable(values)));
+ if (values.isEmpty() || values.startsWith(QStringLiteral("AUTO"))) {
+ values.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ QStringList ranges;
+ if (values.contains(QStringLiteral("RANGE"))) {
+ QRegularExpression rangeMacro(QStringLiteral("RANGE +([A-Za-z0-9_]*) +... +([A-Za-z0-9_]*)"));
+ QRegularExpressionMatchIterator r = rangeMacro.globalMatch(values);
+ while (r.hasNext()) {
+ QRegularExpressionMatch rm = r.next();
+ ranges << rm.captured(1);
+ ranges << rm.captured(2);
+ DEBUGPRINTF2(printf("range: %s ... %s\n", qPrintable(rm.captured(1)), qPrintable(rm.captured(2))));
+ }
+ }
+
+ const auto enumOrFlag = match.captured(1);
+ const auto name = match.captured(3);
+ const bool flags = enumOrFlag == QStringLiteral("FLAGS");
+
+ QStringList values = findEnumValues(name, includes);
+ if (values.isEmpty()) {
+ if (flags && name.endsWith(QLatin1Char('s')))
+ values = findEnumValues(name.left(name.length() - 1), includes);
+ if (values.isEmpty()) {
+ DEBUGPRINTF(printf("Unable to find values for %s\n", qPrintable(name)));
+ }
+ }
+ if (!values.isEmpty()) {
+ auto moreValues = enumsToValues(values);
+ if (ranges.size()) {
+ for (int i = 0; i < ranges.size() / 2; i++) {
+ bool rangeFound = false;
+ for (auto &v : moreValues) {
+ if (v.name == ranges[2 * i]) {
+ rangeFound = true;
+ QString rangeEnd = ranges[2 * i + 1];
+ auto iter = std::find_if(moreValues.begin(), moreValues.end(), [&rangeEnd](const EnumNameValue &elem){
+ return elem.name == rangeEnd;
+ });
+ if (iter != moreValues.end())
+ v.valueStr = QStringLiteral("RANGE(%1, %2 ... %3)").arg(v.name).arg(v.value).arg(iter->value);
+ else
+ panic("Unable to find range end: %s\n", qPrintable(rangeEnd));
+ break;
+ }
+ }
+ if (rangeFound == false)
+ panic("Unable to find range begin: %s\n", qPrintable(ranges[2 * i]));
+ }
+ }
+ std::sort(moreValues.begin(), moreValues.end(), [](const EnumNameValue &a, const EnumNameValue &b) {
+ return a.value < b.value;
+ });
+ values.clear();
+ int prevValue = std::as_const(moreValues).front().value;
+ for (const auto &v : std::as_const(moreValues)) {
+ QString a;
+ if (v.valueStr.isNull()) {
+ if (v.value == prevValue + 1 && !flags)
+ a = v.name;
+ else
+ a = QStringLiteral("%1 = %2").arg(v.name).arg(v.value);
+ prevValue = v.value;
+ } else {
+ a = v.valueStr;
+ }
+ values << a;
+ }
+
+ metadata = QStringLiteral("%1 {\n %2 \n} %3;").arg(enumOrFlag).arg(values.join(QStringLiteral(",\n"))).arg(name);
+ if (!m_metadata.contains(metadata))
+ m_metadata.append(metadata);
+ }
+ } else {
+ if (!m_metadata.contains(match.captured()))
+ m_metadata.append(match.captured());
+ }
+ }
+ if (prev < preprocessed.length())
+ m_metadata.append(preprocessed.mid(prev, preprocessed.length() - prev));
+}
+
+QString Parser::resolveInclude(const QString &filename)
+{
+ QFileInfo info(filename);
+ if (info.exists())
+ return info.absoluteFilePath();
+ for (const QString &sp : std::as_const(m_includeDirs)) {
+ info = QFileInfo(sp + QLatin1Char('/') + filename);
+ if (info.exists())
+ return info.absoluteFilePath();
+ }
+ return {};
+}
+
+void Parser::addIncludesRecursive(const QString &filename, QList<QString> &includes)
+{
+ QFileInfo info(filename);
+ DEBUGPRINTF(printf("check include: %s\n", qPrintable(filename)));
+ QFile input(filename);
+ if (!input.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ DEBUGPRINTF(printf("Cannot open '%s' for reading: %s\n",
+ qPrintable(filename), qPrintable(input.errorString())));
+ return;
+ }
+ QString data;
+ QTextStream stream(&input);
+ while (!stream.atEnd()) {
+ QString line = stream.readLine().trimmed();
+ data += line + QLatin1Char(QLatin1Char('\n'));
+ }
+
+ QRegularExpression includeMacro(QStringLiteral("#include [\"<]([A-Za-z0-9_./]*.h)[\">]"));
+ QRegularExpressionMatchIterator i = includeMacro.globalMatch(data);
+ while (i.hasNext()) {
+ QRegularExpressionMatch match = i.next();
+ QString filename = match.captured(1);
+
+ QString rinc = filename;
+ if (filename.startsWith(QStringLiteral("../"))) {
+ QFileInfo info2(info.absolutePath() + QLatin1Char('/') + filename);
+ if (!info2.exists()) {
+ DEBUGPRINTF(printf("unable to find %s\n", qPrintable(filename)));
+ continue;
+ }
+ rinc = info2.absoluteFilePath();
+ filename = info2.fileName();
+ }
+
+ // only search possible qt headers
+ if (filename.startsWith(QLatin1Char('q'), Qt::CaseInsensitive)) {
+ QString resolved = resolveInclude(rinc);
+ if (!resolved.isEmpty() && !includes.contains(resolved)) {
+ includes.push_back(resolved);
+ addIncludesRecursive(resolved, includes);
+ }
+ }
+ }
+}
+
+void Parser::parse(QIODevice &input, const QString &name)
+{
+ QString data;
+ QTextStream stream(&input);
+ int lineNumber = 1;
+ qsizetype prev = 0;
+ while (!stream.atEnd()) {
+ QString line = stream.readLine().trimmed();
+ m_offsets.push_back({prev, prev + line.length(), lineNumber++});
+ prev += line.length() + 1;
+ data += line + QLatin1Char(QLatin1Char('\n'));
+ }
+
+ simplifyData(data, m_offsets);
+
+ QStringList includes;
+
+ QRegularExpression includeMacro(QStringLiteral("#include [\"<]([A-Za-z0-9_./]*.h)[\">]"));
+ QRegularExpressionMatchIterator i = includeMacro.globalMatch(data);
+ while (i.hasNext()) {
+ QRegularExpressionMatch match = i.next();
+ const QString filename = match.captured(1);
+ // only search possible qt headers
+ if (filename.startsWith(QLatin1Char('q'), Qt::CaseInsensitive)) {
+ const QString resolved = resolveInclude(filename);
+ if (!resolved.isEmpty() && !includes.contains(resolved)) {
+ includes.push_back(resolved);
+ addIncludesRecursive(resolved, includes);
+ }
+ }
+ }
+
+ QRegularExpression traceMacro(QStringLiteral("Q_TRACE_([A-Z_]*)"));
+ i = traceMacro.globalMatch(data);
+ while (i.hasNext()) {
+ QRegularExpressionMatch match = i.next();
+
+ QString macroType = match.captured(1);
+ if (macroType == QStringLiteral("PARAM_REPLACE"))
+ parseParamReplace(data, match.capturedEnd(), name);
+ else if (macroType == QStringLiteral("INSTRUMENT"))
+ parseInstrument(data, match.capturedEnd());
+ else if (macroType == QStringLiteral("POINT"))
+ parsePoint(data, match.capturedEnd());
+ else if (macroType == QStringLiteral("PREFIX"))
+ parsePrefix(data, match.capturedEnd());
+ else if (macroType == QStringLiteral("METADATA"))
+ parseMetadata(data, match.capturedEnd(), includes);
+ }
+
+ for (auto &func : m_functions) {
+ for (auto &rep : m_replaces)
+ func.functionParameters.replace(rep.in, rep.out);
+ }
+}
+
+void Parser::write(QIODevice &input) const
+{
+ QTextStream out(&input);
+ if (m_prefixes.size() > 0) {
+ out << QStringLiteral("{\n");
+ for (const auto &prefix : m_prefixes)
+ out << prefix << "\n";
+ out << QStringLiteral("}\n");
+ }
+ for (const auto &m : m_metadata)
+ out << m << "\n";
+ for (const auto &func : m_functions) {
+ out << func.className << "_" << func.functionName << "_entry(" << func.functionParameters << ")\n";
+ out << func.className << "_" << func.functionName << "_exit()\n";
+ }
+ for (const auto &point : m_points)
+ out << point.name << "(" << point.parameters << ")\n";
+}
diff --git a/src/tools/tracepointgen/parser.h b/src/tools/tracepointgen/parser.h
new file mode 100644
index 0000000000..389734983d
--- /dev/null
+++ b/src/tools/tracepointgen/parser.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <qiodevice.h>
+#include <qlist.h>
+#include <qbytearray.h>
+
+struct Function
+{
+ QString className;
+ QString functionName;
+ QString functionParameters;
+};
+
+struct Point
+{
+ QString name;
+ QString parameters;
+};
+
+struct Replace
+{
+ QString in;
+ QString out;
+};
+
+struct LineNumber
+{
+ qsizetype begin;
+ qsizetype end;
+ int line;
+};
+
+struct Parser
+{
+ Parser(const QString &provider)
+ : m_provider(provider)
+ {
+
+ }
+
+ void addIncludeDirs(const QStringList &list)
+ {
+ m_includeDirs.append(list);
+ }
+ QString resolveInclude(const QString &filename);
+ void addIncludesRecursive(const QString &filename, QStringList &includes);
+ QStringList findEnumValues(const QString &name, const QStringList &includes);
+
+ void parseParamReplace(const QString &data, qsizetype offset, const QString &name);
+ void parseInstrument(const QString &data, qsizetype offset);
+ void parsePoint(const QString &data, qsizetype offset);
+ void parsePrefix(const QString &data, qsizetype offset);
+ void parseMetadata(const QString &data, qsizetype offset, const QStringList &includes);
+ int lineNumber(qsizetype offset) const;
+
+ void parse(QIODevice &input, const QString &name);
+ void write(QIODevice &input) const;
+ bool isEmpty() const
+ {
+ return m_functions.isEmpty() && m_points.isEmpty();
+ }
+
+ QList<Function> m_functions;
+ QList<Point> m_points;
+ QList<Replace> m_replaces;
+ QList<QString> m_prefixes;
+ QList<QString> m_metadata;
+ QList<LineNumber> m_offsets;
+ QList<QString> m_includeDirs;
+ QString m_provider;
+};
+
+#endif // PARSER_H
diff --git a/src/tools/tracepointgen/tracepointgen.cpp b/src/tools/tracepointgen/tracepointgen.cpp
new file mode 100644
index 0000000000..d814c69873
--- /dev/null
+++ b/src/tools/tracepointgen/tracepointgen.cpp
@@ -0,0 +1,69 @@
+// 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 <qstring.h>
+#include <qstringlist.h>
+#include <qfile.h>
+
+#include "tracepointgen.h"
+#include "parser.h"
+
+static void usage(int status)
+{
+ printf("Generates a tracepoint file for tracegen tool from input files.\n\n");
+ printf("Usage: tracepointgen <output file> <input files> \n");
+ exit(status);
+}
+
+static void parseArgs(int argc, char *argv[], QString &provider, QString &outFile, QList<QString> &inputFiles)
+{
+ if (argc == 1)
+ usage(0);
+ if (argc < 4)
+ usage(-1);
+
+ provider = QLatin1StringView(argv[1]);
+ outFile = QLatin1StringView(argv[2]);
+ for (int i = 3; i < argc; i++)
+ inputFiles.append(QLatin1StringView(argv[i]));
+}
+
+int main(int argc, char *argv[])
+{
+ QString provider;
+ QList<QString> inputFiles;
+ QString outFile;
+
+ parseArgs(argc, argv, provider, outFile, inputFiles);
+
+ Parser parser(provider);
+
+ for (const QString &inputFile : inputFiles) {
+ if (inputFile.startsWith(QLatin1Char('I'))) {
+ QStringList includeDirs = inputFile.right(inputFile.length() - 1).split(QLatin1Char(';'));
+ parser.addIncludeDirs(includeDirs);
+ continue;
+ }
+ QFile in(inputFile);
+ if (!in.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ panic("Cannot open '%s' for reading: %s\n",
+ qPrintable(inputFile), qPrintable(in.errorString()));
+ }
+ DEBUGPRINTF(printf("tracepointgen: parse %s\n", qPrintable(inputFile)));
+ parser.parse(in, inputFile);
+ }
+ if (parser.isEmpty())
+ panic("empty provider %s\n", qPrintable(provider));
+
+ QFile out(outFile);
+
+ if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ panic("Cannot open '%s' for writing: %s\n",
+ qPrintable(outFile), qPrintable(out.errorString()));
+ }
+
+ parser.write(out);
+ out.close();
+
+ return 0;
+}
diff --git a/src/tools/tracepointgen/tracepointgen.h b/src/tools/tracepointgen/tracepointgen.h
new file mode 100644
index 0000000000..6aed3dc574
--- /dev/null
+++ b/src/tools/tracepointgen/tracepointgen.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TRACEPOINTGEN_H
+#define TRACEPOINTGEN_H
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#define DEBUG_TRACEPOINTGEN 0
+
+#if DEBUG_TRACEPOINTGEN > 0
+ #define DEBUGPRINTF(x) x
+ #if (DEBUG_TRACEPOINTGEN > 1)
+ #define DEBUGPRINTF2(x) x
+ #else
+ #define DEBUGPRINTF2(x)
+ #endif
+#else
+ #define DEBUGPRINTF(x)
+ #define DEBUGPRINTF2(x)
+#endif
+
+
+
+inline void panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "tracepointgen: fatal: ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stderr);
+
+ exit(EXIT_FAILURE);
+}
+
+#endif
diff --git a/src/tools/uic/CMakeLists.txt b/src/tools/uic/CMakeLists.txt
index e0747cbc50..9f47ec8b4b 100644
--- a/src/tools/uic/CMakeLists.txt
+++ b/src/tools/uic/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from uic.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## uic Tool:
@@ -6,9 +7,10 @@
qt_get_tool_target_name(target_name uic)
qt_internal_add_tool(${target_name}
+ TRY_RUN
TARGET_DESCRIPTION "Qt User Interface Compiler"
INSTALL_DIR "${INSTALL_LIBEXECDIR}"
- TOOLS_TARGET Widgets # special case
+ TOOLS_TARGET Widgets
SOURCES
cpp/cppwritedeclaration.cpp cpp/cppwritedeclaration.h
cpp/cppwriteincludes.cpp cpp/cppwriteincludes.h
@@ -30,6 +32,7 @@ qt_internal_add_tool(${target_name}
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_FOREACH
+ QT_USE_NODISCARD_FILE_OPEN
QT_UIC
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
@@ -37,13 +40,5 @@ qt_internal_add_tool(${target_name}
cpp
python
shared
- #PUBLIC_LIBRARIES # special case remove
- #Qt::Gui # special case remove
)
-
-#### Keys ignored in scope 1:.:.:uic.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt User Interface Compiler"
-# _OPTION = "host_build"
-
-## Scopes:
-#####################################################################
+qt_internal_return_unless_building_tools()
diff --git a/src/tools/uic/cpp/cppwritedeclaration.cpp b/src/tools/uic/cpp/cppwritedeclaration.cpp
index 995b99b692..8261963cfa 100644
--- a/src/tools/uic/cpp/cppwritedeclaration.cpp
+++ b/src/tools/uic/cpp/cppwritedeclaration.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppwritedeclaration.h"
#include "cppwriteinitialization.h"
@@ -39,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace {
void openNameSpaces(const QStringList &namespaceList, QTextStream &output)
{
@@ -75,10 +52,10 @@ void WriteDeclaration::acceptUI(DomUI *node)
QString exportMacro = node->elementExportMacro();
if (!exportMacro.isEmpty())
- exportMacro.append(QLatin1Char(' '));
+ exportMacro.append(u' ');
- QStringList namespaceList = qualifiedClassName.split(QLatin1String("::"));
- if (namespaceList.count()) {
+ QStringList namespaceList = qualifiedClassName.split("::"_L1);
+ if (namespaceList.size()) {
className = namespaceList.last();
namespaceList.removeLast();
}
@@ -88,15 +65,15 @@ void WriteDeclaration::acceptUI(DomUI *node)
// is a User using Qt-in-namespace having his own classes not in a namespace.
// In this case the generated Ui helper classes will also end up in
// the Qt namespace (which is harmless, but not "pretty")
- const bool needsMacro = namespaceList.count() == 0
- || namespaceList[0] == QLatin1String("qdesigner_internal");
+ const bool needsMacro = m_option.qtNamespace &&
+ (namespaceList.size() == 0 || namespaceList[0] == "qdesigner_internal"_L1);
if (needsMacro)
m_output << "QT_BEGIN_NAMESPACE\n\n";
openNameSpaces(namespaceList, m_output);
- if (namespaceList.count())
+ if (namespaceList.size())
m_output << "\n";
m_output << "class " << exportMacro << m_option.prefix << className << "\n"
@@ -105,7 +82,7 @@ void WriteDeclaration::acceptUI(DomUI *node)
const QStringList connections = m_uic->databaseInfo()->connections();
for (const QString &connection : connections) {
- if (connection != QLatin1String("(default)"))
+ if (connection != "(default)"_L1)
m_output << m_option.indent << "QSqlDatabase " << connection << "Connection;\n";
}
@@ -121,11 +98,11 @@ void WriteDeclaration::acceptUI(DomUI *node)
closeNameSpaces(namespaceList, m_output);
- if (namespaceList.count())
+ if (namespaceList.size())
m_output << "\n";
if (m_option.generateNamespace && !m_option.prefix.isEmpty()) {
- namespaceList.append(QLatin1String("Ui"));
+ namespaceList.append("Ui"_L1);
openNameSpaces(namespaceList, m_output);
@@ -133,7 +110,7 @@ void WriteDeclaration::acceptUI(DomUI *node)
closeNameSpaces(namespaceList, m_output);
- if (namespaceList.count())
+ if (namespaceList.size())
m_output << "\n";
}
@@ -143,7 +120,7 @@ void WriteDeclaration::acceptUI(DomUI *node)
void WriteDeclaration::acceptWidget(DomWidget *node)
{
- QString className = QLatin1String("QWidget");
+ QString className = u"QWidget"_s;
if (node->hasAttributeClass())
className = node->attributeClass();
@@ -160,7 +137,7 @@ void WriteDeclaration::acceptSpacer(DomSpacer *node)
void WriteDeclaration::acceptLayout(DomLayout *node)
{
- QString className = QLatin1String("QLayout");
+ QString className = u"QLayout"_s;
if (node->hasAttributeClass())
className = node->attributeClass();
diff --git a/src/tools/uic/cpp/cppwritedeclaration.h b/src/tools/uic/cpp/cppwritedeclaration.h
index f83bfe8f50..2dcb5981e1 100644
--- a/src/tools/uic/cpp/cppwritedeclaration.h
+++ b/src/tools/uic/cpp/cppwritedeclaration.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CPPWRITEDECLARATION_H
#define CPPWRITEDECLARATION_H
diff --git a/src/tools/uic/cpp/cppwriteincludes.cpp b/src/tools/uic/cpp/cppwriteincludes.cpp
index 809c78f700..7cf7c4e59e 100644
--- a/src/tools/uic/cpp/cppwriteincludes.cpp
+++ b/src/tools/uic/cpp/cppwriteincludes.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppwriteincludes.h"
#include "driver.h"
@@ -40,6 +15,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
enum { debugWriteIncludes = 0 };
enum { warnHeaderGeneration = 0 };
@@ -47,7 +24,7 @@ enum { warnHeaderGeneration = 0 };
static inline QString moduleHeader(const QString &module, const QString &header)
{
QString rc = module;
- rc += QLatin1Char('/');
+ rc += u'/';
rc += header;
return rc;
}
@@ -60,11 +37,11 @@ WriteIncludes::WriteIncludes(Uic *uic) : WriteIncludesBase(uic),
// When possible (no namespace) use the "QtModule/QClass" convention
// and create a re-mapping of the old header "qclass.h" to it. Do not do this
// for the "Phonon::Someclass" classes, however.
- const QString namespaceDelimiter = QLatin1String("::");
+ const QLatin1StringView namespaceDelimiter = "::"_L1;
for (const auto &e : classInfoEntries()) {
- const QString klass = QLatin1String(e.klass);
- const QString module = QLatin1String(e.module);
- QLatin1String header = QLatin1String(e.header);
+ const QString klass = QLatin1StringView(e.klass);
+ const QString module = QLatin1StringView(e.module);
+ QLatin1StringView header = QLatin1StringView(e.header);
if (klass.contains(namespaceDelimiter)) {
m_classToHeader.insert(klass, moduleHeader(module, header));
} else {
@@ -113,7 +90,7 @@ void WriteIncludes::insertIncludeForClass(const QString &className, QString head
// Quick check by class name to detect includehints provided for custom widgets.
// Remove namespaces
QString lowerClassName = className.toLower();
- static const QString namespaceSeparator = QLatin1String("::");
+ static const auto namespaceSeparator = "::"_L1;
const int namespaceIndex = lowerClassName.lastIndexOf(namespaceSeparator);
if (namespaceIndex != -1)
lowerClassName.remove(0, namespaceIndex + namespaceSeparator.size());
@@ -126,7 +103,7 @@ void WriteIncludes::insertIncludeForClass(const QString &className, QString head
if (!uic()->option().implicitIncludes)
break;
header = lowerClassName;
- header += QLatin1String(".h");
+ header += ".h"_L1;
if (warnHeaderGeneration) {
qWarning("%s: Warning: generated header '%s' for class '%s'.",
qPrintable(uic()->option().messagePrefix()),
@@ -157,7 +134,7 @@ void WriteIncludes::addCppCustomWidget(const QString &className, const DomCustom
QString header;
bool global = false;
if (!m_classToHeader.contains(className)) {
- global = domHeader->attributeLocation().toLower() == QLatin1String("global");
+ global = domHeader->attributeLocation().toLower() == "global"_L1;
header = domHeader->text();
}
insertIncludeForClass(className, header, global);
@@ -169,7 +146,7 @@ void WriteIncludes::acceptInclude(DomInclude *node)
{
bool global = true;
if (node->hasAttributeLocation())
- global = node->attributeLocation() == QLatin1String("global");
+ global = node->attributeLocation() == "global"_L1;
insertInclude(node->text(), global);
}
@@ -190,8 +167,8 @@ void WriteIncludes::insertInclude(const QString &header, bool global)
void WriteIncludes::writeHeaders(const OrderedSet &headers, bool global)
{
- const QChar openingQuote = global ? QLatin1Char('<') : QLatin1Char('"');
- const QChar closingQuote = global ? QLatin1Char('>') : QLatin1Char('"');
+ const QChar openingQuote = global ? u'<' : u'"';
+ const QChar closingQuote = global ? u'>' : u'"';
// Check for the old headers 'qslider.h' and replace by 'QtGui/QSlider'
for (const QString &header : headers) {
diff --git a/src/tools/uic/cpp/cppwriteincludes.h b/src/tools/uic/cpp/cppwriteincludes.h
index 4ff2a514fb..1ec10f39b2 100644
--- a/src/tools/uic/cpp/cppwriteincludes.h
+++ b/src/tools/uic/cpp/cppwriteincludes.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CPPWRITEINCLUDES_H
#define CPPWRITEINCLUDES_H
diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp
index 9471fd22d9..205d6a50a9 100644
--- a/src/tools/uic/cpp/cppwriteinitialization.cpp
+++ b/src/tools/uic/cpp/cppwriteinitialization.cpp
@@ -1,32 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppwriteinitialization.h"
+#include "customwidgetsinfo.h"
#include "driver.h"
#include "ui4.h"
#include "utils.h"
@@ -45,17 +21,47 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace {
+
+ // Expand "Horizontal", "Qt::Horizontal" to "Qt::Orientation::Horizontal"
+ QString expandEnum(QString value, const QString &prefix)
+ {
+ if (value.startsWith(prefix))
+ return value;
+ const auto pos = value.lastIndexOf("::"_L1);
+ if (pos == -1)
+ return prefix + "::"_L1 + value;
+ value.replace(0, pos, prefix);
+ return value;
+ }
+
+ inline QString expandSizePolicyEnum(const QString &value)
+ {
+ return expandEnum(value, "QSizePolicy::Policy"_L1);
+ }
+
+ inline QString expandToolBarArea(const QString &value)
+ {
+ return expandEnum(value, "Qt::ToolBarArea"_L1);
+ }
+
+ inline QString expandDockWidgetArea(const QString &value)
+ {
+ return expandEnum(value, "Qt::DockWidgetArea"_L1);
+ }
+
// figure out the toolbar area of a DOM attrib list.
// By legacy, it is stored as an integer. As of 4.3.0, it is the enumeration value.
QString toolBarAreaStringFromDOMAttributes(const CPP::WriteInitialization::DomPropertyMap &attributes) {
- const DomProperty *pstyle = attributes.value(QLatin1String("toolBarArea"));
+ const DomProperty *pstyle = attributes.value("toolBarArea"_L1);
QString result;
if (!pstyle)
return result;
switch (pstyle->kind()) {
case DomProperty::Number:
- result = QLatin1String(language::toolbarArea(pstyle->elementNumber()));
+ result = language::toolbarArea(pstyle->elementNumber());
break;
case DomProperty::Enum:
result = pstyle->elementEnum();
@@ -63,9 +69,7 @@ namespace {
default:
break;
}
- if (!result.startsWith(QLatin1String("Qt::")))
- result.prepend(QLatin1String("Qt::"));
- return result + QLatin1String(", ");
+ return expandToolBarArea(result) + ", "_L1;
}
// Write a statement to create a spacer item.
@@ -75,7 +79,7 @@ namespace {
int w = 0;
int h = 0;
- if (const DomProperty *sh = properties.value(QLatin1String("sizeHint"))) {
+ if (const DomProperty *sh = properties.value("sizeHint"_L1)) {
if (const DomSize *sizeHint = sh->elementSize()) {
w = sizeHint->elementWidth();
h = sizeHint->elementHeight();
@@ -84,27 +88,17 @@ namespace {
output << w << ", " << h << ", ";
// size type
- QString sizeType;
- if (const DomProperty *st = properties.value(QLatin1String("sizeType"))) {
- const QString value = st->elementEnum();
- if (value.startsWith(QLatin1String("QSizePolicy::")))
- sizeType = value;
- else
- sizeType = QLatin1String("QSizePolicy::") + value;
- } else {
- sizeType = QStringLiteral("QSizePolicy::Expanding");
- }
+ const DomProperty *st = properties.value("sizeType"_L1);
+ QString horizType = st != nullptr ? st->elementEnum() : "Expanding"_L1;
+ QString vertType = "Minimum"_L1;
// orientation
- bool isVspacer = false;
- if (const DomProperty *o = properties.value(QLatin1String("orientation"))) {
- const QString orientation = o->elementEnum();
- if (orientation == QLatin1String("Qt::Vertical") || orientation == QLatin1String("Vertical"))
- isVspacer = true;
- }
- const QString horizType = isVspacer ? QLatin1String("QSizePolicy::Minimum") : sizeType;
- const QString vertType = isVspacer ? sizeType : QLatin1String("QSizePolicy::Minimum");
- output << language::enumValue(horizType) << ", " << language::enumValue(vertType) << ')';
+ const DomProperty *o = properties.value("orientation"_L1);
+ if (o != nullptr && o->elementEnum().endsWith("Vertical"_L1))
+ std::swap(horizType, vertType);
+
+ output << language::enumValue(expandSizePolicyEnum(horizType)) << ", "
+ << language::enumValue(expandSizePolicyEnum(vertType)) << ')';
}
@@ -143,18 +137,18 @@ namespace {
// ### fixme Qt 7 remove this: Exclude deprecated properties of Qt 5.
case DomProperty::Set:
if (p->attributeName() == u"features"
- && customWidgetsInfo->extends(className, QLatin1String("QDockWidget"))
+ && customWidgetsInfo->extends(className, "QDockWidget")
&& p->elementSet() == u"QDockWidget::AllDockWidgetFeatures") {
- const QString msg = fileName + QLatin1String(": Warning: Deprecated enum value QDockWidget::AllDockWidgetFeatures was encountered.");
+ const QString msg = fileName + ": Warning: Deprecated enum value QDockWidget::AllDockWidgetFeatures was encountered."_L1;
qWarning("%s", qPrintable(msg));
return false;
}
break;
case DomProperty::Enum:
if (p->attributeName() == u"sizeAdjustPolicy"
- && customWidgetsInfo->extends(className, QLatin1String("QComboBox"))
+ && customWidgetsInfo->extends(className, "QComboBox")
&& p->elementEnum() == u"QComboBox::AdjustToMinimumContentsLength") {
- const QString msg = fileName + QLatin1String(": Warning: Deprecated enum value QComboBox::AdjustToMinimumContentsLength was encountered.");
+ const QString msg = fileName + ": Warning: Deprecated enum value QComboBox::AdjustToMinimumContentsLength was encountered."_L1;
qWarning("%s", qPrintable(msg));
return false;
}
@@ -202,6 +196,15 @@ FontHandle::FontHandle(const DomFont *domFont) :
{
}
+static QString fontWeight(const DomFont *domFont)
+{
+ if (domFont->hasElementFontWeight())
+ return domFont->elementFontWeight();
+ if (domFont->hasElementBold())
+ return domFont->elementBold() ? u"Bold"_s : u"Normal"_s;
+ return {};
+}
+
int FontHandle::compare(const FontHandle &rhs) const
{
const QString family = m_domFont->hasElementFamily() ? m_domFont->elementFamily() : QString();
@@ -216,10 +219,10 @@ int FontHandle::compare(const FontHandle &rhs) const
if (const int crc = compareInt(pointSize, rhsPointSize))
return crc;
- const int bold = m_domFont->hasElementBold() ? (m_domFont->elementBold() ? 1 : 0) : -1;
- const int rhsBold = rhs.m_domFont->hasElementBold() ? (rhs.m_domFont->elementBold() ? 1 : 0) : -1;
- if (const int crc = compareInt(bold, rhsBold))
- return crc;
+ const QString fontWeight = CPP::fontWeight(m_domFont);
+ const QString rhsFontWeight = CPP::fontWeight(rhs.m_domFont);
+ if (const int wrc = fontWeight.compare(rhsFontWeight))
+ return wrc;
const int italic = m_domFont->hasElementItalic() ? (m_domFont->elementItalic() ? 1 : 0) : -1;
const int rhsItalic = rhs.m_domFont->hasElementItalic() ? (rhs.m_domFont->elementItalic() ? 1 : 0) : -1;
@@ -231,11 +234,6 @@ int FontHandle::compare(const FontHandle &rhs) const
if (const int crc = compareInt(underline, rhsUnderline))
return crc;
- const int weight = m_domFont->hasElementWeight() ? m_domFont->elementWeight() : -1;
- const int rhsWeight = rhs.m_domFont->hasElementWeight() ? rhs.m_domFont->elementWeight() : -1;
- if (const int crc = compareInt(weight, rhsWeight))
- return crc;
-
const int strikeOut = m_domFont->hasElementStrikeOut() ? (m_domFont->elementStrikeOut() ? 1 : 0) : -1;
const int rhsStrikeOut = rhs.m_domFont->hasElementStrikeOut() ? (rhs.m_domFont->elementStrikeOut() ? 1 : 0) : -1;
if (const int crc = compareInt(strikeOut, rhsStrikeOut))
@@ -257,6 +255,13 @@ int FontHandle::compare(const FontHandle &rhs) const
if (const int src = styleStrategy.compare(rhsStyleStrategy))
return src;
+ const QString hintingPreference = m_domFont->hasElementHintingPreference()
+ ? m_domFont->elementHintingPreference() : QString();
+ const QString rhsHintingPreference = rhs.m_domFont->hasElementHintingPreference()
+ ? rhs.m_domFont->elementHintingPreference() : QString();
+ if (const int src = hintingPreference.compare(rhsHintingPreference))
+ return src;
+
return 0;
}
@@ -387,12 +392,12 @@ void WriteInitialization::LayoutDefaultHandler::acceptLayoutFunction(DomLayoutFu
if (node->hasAttributeMargin()) {
m_state[Margin] |= HasDefaultFunction;
m_functions[Margin] = node->attributeMargin();
- m_functions[Margin] += QLatin1String("()");
+ m_functions[Margin] += "()"_L1;
}
if (node->hasAttributeSpacing()) {
m_state[Spacing] |= HasDefaultFunction;
m_functions[Spacing] = node->attributeSpacing();
- m_functions[Spacing] += QLatin1String("()");
+ m_functions[Spacing] += "()"_L1;
}
}
@@ -400,7 +405,7 @@ static inline void writeContentsMargins(const QString &indent, const QString &ob
{
QString contentsMargins;
QTextStream(&contentsMargins) << value << ", " << value << ", " << value << ", " << value;
- writeSetter(indent, objectName, QLatin1String("setContentsMargins"), contentsMargins, str);
+ writeSetter(indent, objectName, "setContentsMargins"_L1, contentsMargins, str);
}
void WriteInitialization::LayoutDefaultHandler::writeProperty(int p, const QString &indent, const QString &objectName,
@@ -454,11 +459,11 @@ void WriteInitialization::LayoutDefaultHandler::writeProperties(const QString &i
// Write out properties and ignore the ones found in
// subsequent writing of the property list.
int defaultSpacing = marginType == WriteInitialization::Use43UiFile ? -1 : 6;
- writeProperty(Spacing, indent, varName, properties, QLatin1String("spacing"), QLatin1String("setSpacing"),
+ writeProperty(Spacing, indent, varName, properties, "spacing"_L1, "setSpacing"_L1,
defaultSpacing, false, str);
// We use 9 as TopLevelMargin, since Designer seem to always use 9.
static const int layoutmargins[4] = {-1, 9, 9, 0};
- writeProperty(Margin, indent, varName, properties, QLatin1String("margin"), QLatin1String("setMargin"),
+ writeProperty(Margin, indent, varName, properties, "margin"_L1, "setMargin"_L1,
layoutmargins[marginType], suppressMarginDefault, str);
}
@@ -517,16 +522,16 @@ void WriteInitialization::acceptUI(DomUI *node)
const QString widgetClassName = node->elementWidget()->attributeClass();
- const QString parameterType = widgetClassName + QLatin1String(" *");
+ const QString parameterType = widgetClassName + " *"_L1;
m_output << m_option.indent
<< language::startFunctionDefinition1("setupUi", parameterType, varName, m_option.indent);
const QStringList connections = m_uic->databaseInfo()->connections();
for (const auto &connection : connections) {
- if (connection == QLatin1String("(default)"))
+ if (connection == "(default)"_L1)
continue;
- const QString varConn = connection + QLatin1String("Connection");
+ const QString varConn = connection + "Connection"_L1;
m_output << m_indent << varConn << " = QSqlDatabase::database("
<< language::charliteral(connection, m_dindent) << ")" << language::eol;
}
@@ -535,7 +540,7 @@ void WriteInitialization::acceptUI(DomUI *node)
if (!m_buddies.empty())
m_output << language::openQtConfig(shortcutConfigKey());
- for (const Buddy &b : qAsConst(m_buddies)) {
+ for (const Buddy &b : std::as_const(m_buddies)) {
const QString buddyVarName = m_driver->widgetVariableName(b.buddyAttributeName);
if (buddyVarName.isEmpty()) {
fprintf(stderr, "%s: Warning: Buddy assignment: '%s' is not a valid widget.\n",
@@ -576,13 +581,13 @@ void WriteInitialization::acceptUI(DomUI *node)
if (language::language() == Language::Cpp) {
// Mark varName as unused to avoid compiler warnings.
m_refreshInitialization += m_indent;
- m_refreshInitialization += QLatin1String("(void)");
+ m_refreshInitialization += "(void)"_L1;
m_refreshInitialization += varName ;
m_refreshInitialization += language::eol;
} else if (language::language() == Language::Python) {
// output a 'pass' to have an empty function
m_refreshInitialization += m_indent;
- m_refreshInitialization += QLatin1String("pass");
+ m_refreshInitialization += "pass"_L1;
m_refreshInitialization += language::eol;
}
}
@@ -605,7 +610,7 @@ void WriteInitialization::addWizardPage(const QString &pageVarName, const DomWid
const auto &attributes = page->elementAttribute();
if (!attributes.empty()) {
for (const DomProperty *p : attributes) {
- if (p->attributeName() == QLatin1String("pageId")) {
+ if (p->attributeName() == "pageId"_L1) {
if (const DomString *ds = p->elementString())
id = ds->text();
break;
@@ -623,7 +628,7 @@ void WriteInitialization::addWizardPage(const QString &pageVarName, const DomWid
void WriteInitialization::acceptWidget(DomWidget *node)
{
- m_layoutMarginType = m_widgetChain.count() == 1 ? TopLevelMargin : ChildMargin;
+ m_layoutMarginType = m_widgetChain.size() == 1 ? TopLevelMargin : ChildMargin;
const QString className = node->attributeClass();
const QString varName = m_driver->findOrInsertWidget(node);
@@ -649,13 +654,13 @@ void WriteInitialization::acceptWidget(DomWidget *node)
parentWidget = savedParentWidget;
- if (cwi->extends(className, QLatin1String("QComboBox"))) {
+ if (cwi->extends(className, "QComboBox")) {
initializeComboBox(node);
- } else if (cwi->extends(className, QLatin1String("QListWidget"))) {
+ } else if (cwi->extends(className, "QListWidget")) {
initializeListWidget(node);
- } else if (cwi->extends(className, QLatin1String("QTreeWidget"))) {
+ } else if (cwi->extends(className, "QTreeWidget")) {
initializeTreeWidget(node);
- } else if (cwi->extends(className, QLatin1String("QTableWidget"))) {
+ } else if (cwi->extends(className, "QTableWidget")) {
initializeTableWidget(node);
}
@@ -665,7 +670,7 @@ void WriteInitialization::acceptWidget(DomWidget *node)
writeProperties(varName, className, node->elementProperty());
if (!parentWidget.isEmpty()
- && cwi->extends(className, QLatin1String("QMenu"))) {
+ && cwi->extends(className, "QMenu")) {
initializeMenu(node, parentWidget);
}
@@ -673,10 +678,10 @@ void WriteInitialization::acceptWidget(DomWidget *node)
m_layoutChain.push(nullptr);
m_layoutWidget = false;
- if (className == QLatin1String("QWidget") && !node->hasAttributeNative()) {
+ if (className == "QWidget"_L1 && !node->hasAttributeNative()) {
if (const DomWidget* parentWidget = m_widgetChain.top()) {
const QString parentClass = parentWidget->attributeClass();
- if (parentClass != QLatin1String("QMainWindow")
+ if (parentClass != "QMainWindow"_L1
&& !m_uic->customWidgetsInfo()->isCustomWidgetContainer(parentClass)
&& !m_uic->isContainer(parentClass))
m_layoutWidget = true;
@@ -691,32 +696,32 @@ void WriteInitialization::acceptWidget(DomWidget *node)
const DomPropertyMap attributes = propertyMap(node->elementAttribute());
- const QString pageDefaultString = QLatin1String("Page");
+ const QString pageDefaultString = u"Page"_s;
- if (cwi->extends(parentClass, QLatin1String("QMainWindow"))) {
- if (cwi->extends(className, QLatin1String("QMenuBar"))) {
+ if (cwi->extends(parentClass, "QMainWindow")) {
+ if (cwi->extends(className, "QMenuBar")) {
m_output << m_indent << parentWidget << language::derefPointer
<< "setMenuBar(" << varName << ')' << language::eol;
- } else if (cwi->extends(className, QLatin1String("QToolBar"))) {
+ } else if (cwi->extends(className, "QToolBar")) {
m_output << m_indent << parentWidget << language::derefPointer << "addToolBar("
<< language::enumValue(toolBarAreaStringFromDOMAttributes(attributes)) << varName
<< ')' << language::eol;
- if (const DomProperty *pbreak = attributes.value(QLatin1String("toolBarBreak"))) {
- if (pbreak->elementBool() == QLatin1String("true")) {
+ if (const DomProperty *pbreak = attributes.value("toolBarBreak"_L1)) {
+ if (pbreak->elementBool() == "true"_L1) {
m_output << m_indent << parentWidget << language::derefPointer
<< "insertToolBarBreak(" << varName << ')' << language::eol;
}
}
- } else if (cwi->extends(className, QLatin1String("QDockWidget"))) {
+ } else if (cwi->extends(className, "QDockWidget")) {
m_output << m_indent << parentWidget << language::derefPointer << "addDockWidget(";
- if (DomProperty *pstyle = attributes.value(QLatin1String("dockWidgetArea"))) {
- m_output << "Qt" << language::qualifier
- << language::dockWidgetArea(pstyle->elementNumber()) << ", ";
+ if (DomProperty *pstyle = attributes.value("dockWidgetArea"_L1)) {
+ QString a = expandDockWidgetArea(language::dockWidgetArea(pstyle->elementNumber()));
+ m_output << language::enumValue(a) << ", ";
}
m_output << varName << ")" << language::eol;
- } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QStatusBar"))) {
+ } else if (m_uic->customWidgetsInfo()->extends(className, "QStatusBar")) {
m_output << m_indent << parentWidget << language::derefPointer
<< "setStatusBar(" << varName << ')' << language::eol;
} else {
@@ -732,14 +737,14 @@ void WriteInitialization::acceptWidget(DomWidget *node)
if (!addPageMethod.isEmpty()) {
m_output << m_indent << parentWidget << language::derefPointer
<< addPageMethod << '(' << varName << ')' << language::eol;
- } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QWizard"))) {
+ } else if (m_uic->customWidgetsInfo()->extends(parentClass, "QWizard")) {
addWizardPage(varName, node, parentWidget);
- } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QToolBox"))) {
- const DomProperty *plabel = attributes.value(QLatin1String("label"));
+ } else if (m_uic->customWidgetsInfo()->extends(parentClass, "QToolBox")) {
+ const DomProperty *plabel = attributes.value("label"_L1);
DomString *plabelString = plabel ? plabel->elementString() : nullptr;
QString icon;
- if (const DomProperty *picon = attributes.value(QLatin1String("icon")))
- icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition
+ if (const DomProperty *picon = attributes.value("icon"_L1))
+ icon = ", "_L1 + iconCall(picon); // Side effect: Writes icon definition
m_output << m_indent << parentWidget << language::derefPointer << "addItem("
<< varName << icon << ", " << noTrCall(plabelString, pageDefaultString)
<< ')' << language::eol;
@@ -749,7 +754,7 @@ void WriteInitialization::acceptWidget(DomWidget *node)
<< language::derefPointer << "indexOf(" << varName << "), "
<< autoTrCall(plabelString, pageDefaultString) << ')' << language::eol;
- if (DomProperty *ptoolTip = attributes.value(QLatin1String("toolTip"))) {
+ if (DomProperty *ptoolTip = attributes.value("toolTip"_L1)) {
autoTrOutput(ptoolTip->elementString())
<< language::openQtConfig(toolTipConfigKey())
<< m_indent << parentWidget << language::derefPointer << "setItemToolTip(" << parentWidget
@@ -757,12 +762,12 @@ void WriteInitialization::acceptWidget(DomWidget *node)
<< autoTrCall(ptoolTip->elementString()) << ')' << language::eol
<< language::closeQtConfig(toolTipConfigKey());
}
- } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QTabWidget"))) {
- const DomProperty *ptitle = attributes.value(QLatin1String("title"));
+ } else if (m_uic->customWidgetsInfo()->extends(parentClass, "QTabWidget")) {
+ const DomProperty *ptitle = attributes.value("title"_L1);
DomString *ptitleString = ptitle ? ptitle->elementString() : nullptr;
QString icon;
- if (const DomProperty *picon = attributes.value(QLatin1String("icon")))
- icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition
+ if (const DomProperty *picon = attributes.value("icon"_L1))
+ icon = ", "_L1 + iconCall(picon); // Side effect: Writes icon definition
m_output << m_indent << parentWidget << language::derefPointer << "addTab("
<< varName << icon << ", " << language::emptyString << ')' << language::eol;
@@ -771,7 +776,7 @@ void WriteInitialization::acceptWidget(DomWidget *node)
<< language::derefPointer << "indexOf(" << varName << "), "
<< autoTrCall(ptitleString, pageDefaultString) << ')' << language::eol;
- if (const DomProperty *ptoolTip = attributes.value(QLatin1String("toolTip"))) {
+ if (const DomProperty *ptoolTip = attributes.value("toolTip"_L1)) {
autoTrOutput(ptoolTip->elementString())
<< language::openQtConfig(toolTipConfigKey())
<< m_indent << parentWidget << language::derefPointer << "setTabToolTip("
@@ -779,7 +784,7 @@ void WriteInitialization::acceptWidget(DomWidget *node)
<< "), " << autoTrCall(ptoolTip->elementString()) << ')' << language::eol
<< language::closeQtConfig(toolTipConfigKey());
}
- if (const DomProperty *pwhatsThis = attributes.value(QLatin1String("whatsThis"))) {
+ if (const DomProperty *pwhatsThis = attributes.value("whatsThis"_L1)) {
autoTrOutput(pwhatsThis->elementString())
<< language::openQtConfig(whatsThisConfigKey())
<< m_indent << parentWidget << language::derefPointer << "setTabWhatsThis("
@@ -792,41 +797,41 @@ void WriteInitialization::acceptWidget(DomWidget *node)
//
// Special handling for qtableview/qtreeview fake header attributes
//
- static const QLatin1String realPropertyNames[] = {
- QLatin1String("visible"),
- QLatin1String("cascadingSectionResizes"),
- QLatin1String("minimumSectionSize"), // before defaultSectionSize
- QLatin1String("defaultSectionSize"),
- QLatin1String("highlightSections"),
- QLatin1String("showSortIndicator"),
- QLatin1String("stretchLastSection"),
+ static const QLatin1StringView realPropertyNames[] = {
+ "visible"_L1,
+ "cascadingSectionResizes"_L1,
+ "minimumSectionSize"_L1, // before defaultSectionSize
+ "defaultSectionSize"_L1,
+ "highlightSections"_L1,
+ "showSortIndicator"_L1,
+ "stretchLastSection"_L1,
};
static const QStringList trees = {
- QLatin1String("QTreeView"), QLatin1String("QTreeWidget")
+ u"QTreeView"_s, u"QTreeWidget"_s
};
static const QStringList tables = {
- QLatin1String("QTableView"), QLatin1String("QTableWidget")
+ u"QTableView"_s, u"QTableWidget"_s
};
if (cwi->extendsOneOf(className, trees)) {
DomPropertyList headerProperties;
for (auto realPropertyName : realPropertyNames) {
- const QString fakePropertyName = QLatin1String("header")
+ const QString fakePropertyName = "header"_L1
+ QChar(realPropertyName.at(0)).toUpper() + realPropertyName.mid(1);
if (DomProperty *fakeProperty = attributes.value(fakePropertyName)) {
fakeProperty->setAttributeName(realPropertyName);
headerProperties << fakeProperty;
}
}
- writeProperties(varName + language::derefPointer + QLatin1String("header()"),
- QLatin1String("QHeaderView"), headerProperties,
+ writeProperties(varName + language::derefPointer + "header()"_L1,
+ "QHeaderView"_L1, headerProperties,
WritePropertyIgnoreObjectName);
} else if (cwi->extendsOneOf(className, tables)) {
- static const QLatin1String headerPrefixes[] = {
- QLatin1String("horizontalHeader"),
- QLatin1String("verticalHeader"),
+ static const QLatin1StringView headerPrefixes[] = {
+ "horizontalHeader"_L1,
+ "verticalHeader"_L1,
};
for (auto headerPrefix : headerPrefixes) {
@@ -840,8 +845,8 @@ void WriteInitialization::acceptWidget(DomWidget *node)
}
}
const QString headerVar = varName + language::derefPointer
- + headerPrefix + QLatin1String("()");
- writeProperties(headerVar, QLatin1String("QHeaderView"),
+ + headerPrefix + "()"_L1;
+ writeProperties(headerVar, "QHeaderView"_L1,
headerProperties, WritePropertyIgnoreObjectName);
}
}
@@ -867,7 +872,7 @@ void WriteInitialization::addButtonGroup(const DomWidget *buttonNode, const QStr
{
const DomPropertyMap attributes = propertyMap(buttonNode->elementAttribute());
// Look up the button group name as specified in the attribute and find the uniquified name
- const DomProperty *prop = attributes.value(QLatin1String("buttonGroup"));
+ const DomProperty *prop = attributes.value("buttonGroup"_L1);
if (!prop)
return;
const QString attributeName = toString(prop->elementString());
@@ -886,7 +891,7 @@ void WriteInitialization::addButtonGroup(const DomWidget *buttonNode, const QStr
const QString groupName = m_driver->findOrInsertButtonGroup(group);
// Create on demand
if (!m_buttonGroups.contains(groupName)) {
- const QString className = QLatin1String("QButtonGroup");
+ const QString className = u"QButtonGroup"_s;
m_output << m_indent;
if (createGroupOnTheFly)
m_output << className << " *";
@@ -905,7 +910,7 @@ void WriteInitialization::acceptLayout(DomLayout *node)
const QString varName = m_driver->findOrInsertLayout(node);
const DomPropertyMap properties = propertyMap(node->elementProperty());
- const bool oldLayoutProperties = properties.value(QLatin1String("margin")) != nullptr;
+ const bool oldLayoutProperties = properties.value("margin"_L1) != nullptr;
bool isGroupBox = false;
@@ -932,36 +937,36 @@ void WriteInitialization::acceptLayout(DomLayout *node)
left = top = right = bottom = false;
for (const DomProperty *p : propList) {
const QString propertyName = p->attributeName();
- if (propertyName == QLatin1String("leftMargin") && p->kind() == DomProperty::Number)
+ if (propertyName == "leftMargin"_L1 && p->kind() == DomProperty::Number)
left = true;
- else if (propertyName == QLatin1String("topMargin") && p->kind() == DomProperty::Number)
+ else if (propertyName == "topMargin"_L1 && p->kind() == DomProperty::Number)
top = true;
- else if (propertyName == QLatin1String("rightMargin") && p->kind() == DomProperty::Number)
+ else if (propertyName == "rightMargin"_L1 && p->kind() == DomProperty::Number)
right = true;
- else if (propertyName == QLatin1String("bottomMargin") && p->kind() == DomProperty::Number)
+ else if (propertyName == "bottomMargin"_L1 && p->kind() == DomProperty::Number)
bottom = true;
}
if (!left) {
DomProperty *p = new DomProperty();
- p->setAttributeName(QLatin1String("leftMargin"));
+ p->setAttributeName("leftMargin"_L1);
p->setElementNumber(0);
newPropList.append(p);
}
if (!top) {
DomProperty *p = new DomProperty();
- p->setAttributeName(QLatin1String("topMargin"));
+ p->setAttributeName("topMargin"_L1);
p->setElementNumber(0);
newPropList.append(p);
}
if (!right) {
DomProperty *p = new DomProperty();
- p->setAttributeName(QLatin1String("rightMargin"));
+ p->setAttributeName("rightMargin"_L1);
p->setElementNumber(0);
newPropList.append(p);
}
if (!bottom) {
DomProperty *p = new DomProperty();
- p->setAttributeName(QLatin1String("bottomMargin"));
+ p->setAttributeName("bottomMargin"_L1);
p->setElementNumber(0);
newPropList.append(p);
}
@@ -982,12 +987,12 @@ void WriteInitialization::acceptLayout(DomLayout *node)
m_layoutChain.pop();
// Stretch? (Unless we are compiling for UIC3)
- const QString numberNull = QString(QLatin1Char('0'));
- writePropertyList(varName, QLatin1String("setStretch"), node->attributeStretch(), numberNull);
- writePropertyList(varName, QLatin1String("setRowStretch"), node->attributeRowStretch(), numberNull);
- writePropertyList(varName, QLatin1String("setColumnStretch"), node->attributeColumnStretch(), numberNull);
- writePropertyList(varName, QLatin1String("setColumnMinimumWidth"), node->attributeColumnMinimumWidth(), numberNull);
- writePropertyList(varName, QLatin1String("setRowMinimumHeight"), node->attributeRowMinimumHeight(), numberNull);
+ const QString numberNull(u'0');
+ writePropertyList(varName, "setStretch"_L1, node->attributeStretch(), numberNull);
+ writePropertyList(varName, "setRowStretch"_L1, node->attributeRowStretch(), numberNull);
+ writePropertyList(varName, "setColumnStretch"_L1, node->attributeColumnStretch(), numberNull);
+ writePropertyList(varName, "setColumnMinimumWidth"_L1, node->attributeColumnMinimumWidth(), numberNull);
+ writePropertyList(varName, "setRowMinimumHeight"_L1, node->attributeRowMinimumHeight(), numberNull);
}
// Apply a comma-separated list of values using a function "setSomething(int idx, value)"
@@ -998,8 +1003,8 @@ void WriteInitialization::writePropertyList(const QString &varName,
{
if (value.isEmpty())
return;
- const QStringList list = value.split(QLatin1Char(','));
- const int count = list.count();
+ const QStringList list = value.split(u',');
+ const int count = list.size();
for (int i = 0; i < count; i++) {
if (list.at(i) != defaultValue) {
m_output << m_indent << varName << language::derefPointer << setFunction
@@ -1018,21 +1023,20 @@ void WriteInitialization::acceptSpacer(DomSpacer *node)
static inline QString formLayoutRole(int column, int colspan)
{
if (colspan > 1)
- return QLatin1String("QFormLayout::SpanningRole");
- return column == 0 ? QLatin1String("QFormLayout::LabelRole") : QLatin1String("QFormLayout::FieldRole");
+ return "QFormLayout::SpanningRole"_L1;
+ return column == 0 ? "QFormLayout::LabelRole"_L1 : "QFormLayout::FieldRole"_L1;
}
static QString layoutAddMethod(DomLayoutItem::Kind kind, const QString &layoutClass)
{
- const QString methodPrefix = layoutClass == QLatin1String("QFormLayout")
- ? QLatin1String("set") : QLatin1String("add");
+ const auto methodPrefix = layoutClass == "QFormLayout"_L1 ? "set"_L1 : "add"_L1;
switch (kind) {
case DomLayoutItem::Widget:
- return methodPrefix + QLatin1String("Widget");
+ return methodPrefix + "Widget"_L1;
case DomLayoutItem::Layout:
- return methodPrefix + QLatin1String("Layout");
+ return methodPrefix + "Layout"_L1;
case DomLayoutItem::Spacer:
- return methodPrefix + QLatin1String("Item");
+ return methodPrefix + "Item"_L1;
case DomLayoutItem::Unknown:
Q_ASSERT( false );
break;
@@ -1055,7 +1059,7 @@ void WriteInitialization::acceptLayoutItem(DomLayoutItem *node)
m_output << "\n" << m_indent << layoutName << language::derefPointer << ""
<< layoutAddMethod(node->kind(), layout->attributeClass()) << '(';
- if (layout->attributeClass() == QLatin1String("QGridLayout")) {
+ if (layout->attributeClass() == "QGridLayout"_L1) {
const int row = node->attributeRow();
const int col = node->attributeColumn();
@@ -1064,14 +1068,14 @@ void WriteInitialization::acceptLayoutItem(DomLayoutItem *node)
m_output << itemName << ", " << row << ", " << col << ", " << rowSpan << ", " << colSpan;
if (!node->attributeAlignment().isEmpty())
m_output << ", " << language::enumValue(node->attributeAlignment());
- } else if (layout->attributeClass() == QLatin1String("QFormLayout")) {
+ } else if (layout->attributeClass() == "QFormLayout"_L1) {
const int row = node->attributeRow();
const int colSpan = node->hasAttributeColSpan() ? node->attributeColSpan() : 1;
const QString role = formLayoutRole(node->attributeColumn(), colSpan);
m_output << row << ", " << language::enumValue(role) << ", " << itemName;
} else {
m_output << itemName;
- if (layout->attributeClass().contains(QLatin1String("Box")) && !node->attributeAlignment().isEmpty())
+ if (layout->attributeClass().contains("Box"_L1) && !node->attributeAlignment().isEmpty())
m_output << ", 0, " << language::enumValue(node->attributeAlignment());
}
m_output << ")" << language::eol << "\n";
@@ -1087,7 +1091,7 @@ void WriteInitialization::acceptActionGroup(DomActionGroup *node)
m_output << m_indent << actionName << " = " << language::operatorNew
<< "QActionGroup(" << varName << ")" << language::eol;
- writeProperties(actionName, QLatin1String("QActionGroup"), node->elementProperty());
+ writeProperties(actionName, "QActionGroup"_L1, node->elementProperty());
m_actionGroupChain.push(node);
TreeWalker::acceptActionGroup(node);
@@ -1107,7 +1111,7 @@ void WriteInitialization::acceptAction(DomAction *node)
m_output << m_indent << actionName << " = " << language::operatorNew
<< "QAction(" << varName << ')' << language::eol;
- writeProperties(actionName, QLatin1String("QAction"), node->elementProperty());
+ writeProperties(actionName, "QAction"_L1, node->elementProperty());
}
void WriteInitialization::acceptActionRef(DomActionRef *node)
@@ -1120,7 +1124,7 @@ void WriteInitialization::acceptActionRef(DomActionRef *node)
const QString varName = m_driver->findOrInsertWidget(m_widgetChain.top());
- if (m_widgetChain.top() && actionName == QLatin1String("separator")) {
+ if (m_widgetChain.top() && actionName == "separator"_L1) {
// separator is always reserved!
m_actionOut << m_indent << varName << language::derefPointer
<< "addSeparator()" << language::eol;
@@ -1151,35 +1155,50 @@ QString WriteInitialization::writeStringListProperty(const DomStringList *list)
{
QString propertyValue;
QTextStream str(&propertyValue);
- str << "QStringList()";
+ char trailingDelimiter = '}';
+ switch (language::language()) {
+ case Language::Cpp:
+ str << "QStringList{";
+ break;
+ case Language::Python:
+ str << '[';
+ trailingDelimiter = ']';
+ break;
+ }
const QStringList values = list->elementString();
- if (values.isEmpty())
- return propertyValue;
- if (needsTranslation(list)) {
- const QString comment = list->attributeComment();
- for (int i = 0; i < values.size(); ++i)
- str << '\n' << m_indent << " << " << trCall(values.at(i), comment);
- } else {
- for (int i = 0; i < values.size(); ++i)
- str << " << " << language::qstring(values.at(i), m_dindent);
+ if (!values.isEmpty()) {
+ if (needsTranslation(list)) {
+ const QString comment = list->attributeComment();
+ const qsizetype last = values.size() - 1;
+ for (qsizetype i = 0; i <= last; ++i) {
+ str << '\n' << m_indent << " " << trCall(values.at(i), comment);
+ if (i != last)
+ str << ',';
+ }
+ } else {
+ for (qsizetype i = 0; i < values.size(); ++i) {
+ if (i)
+ str << ", ";
+ str << language::qstring(values.at(i), m_dindent);
+ }
+ }
}
+ str << trailingDelimiter;
return propertyValue;
}
static QString configKeyForProperty(const QString &propertyName)
{
- if (propertyName == QLatin1String("toolTip"))
+ if (propertyName == "toolTip"_L1)
return toolTipConfigKey();
- if (propertyName == QLatin1String("whatsThis"))
+ if (propertyName == "whatsThis"_L1)
return whatsThisConfigKey();
- if (propertyName == QLatin1String("statusTip"))
+ if (propertyName == "statusTip"_L1)
return statusTipConfigKey();
- if (propertyName == QLatin1String("shortcut"))
+ if (propertyName == "shortcut"_L1)
return shortcutConfigKey();
- if (propertyName == QLatin1String("accessibleName")
- || propertyName == QLatin1String("accessibleDescription")) {
+ if (propertyName == "accessibleName"_L1 || propertyName == "accessibleDescription"_L1)
return accessibilityConfigKey();
- }
return QString();
}
@@ -1188,11 +1207,11 @@ void WriteInitialization::writeProperties(const QString &varName,
const DomPropertyList &lst,
unsigned flags)
{
- const bool isTopLevel = m_widgetChain.count() == 1;
+ const bool isTopLevel = m_widgetChain.size() == 1;
- if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QAxWidget"))) {
+ if (m_uic->customWidgetsInfo()->extends(className, "QAxWidget")) {
DomPropertyMap properties = propertyMap(lst);
- if (DomProperty *p = properties.value(QLatin1String("control"))) {
+ if (DomProperty *p = properties.value("control"_L1)) {
m_output << m_indent << varName << language::derefPointer << "setControl("
<< language::qstring(toString(p->elementString()), m_dindent)
<< ')' << language::eol;
@@ -1217,7 +1236,7 @@ void WriteInitialization::writeProperties(const QString &varName,
objectName.remove(0, language::self.size());
m_output << m_indent << indent
<< varName << language::derefPointer << "setObjectName("
- << language::qstring(objectName, m_dindent) << ')' << language::eol;
+ << language::charliteral(objectName, m_dindent) << ')' << language::eol;
}
int leftMargin, topMargin, rightMargin, bottomMargin;
@@ -1232,58 +1251,58 @@ void WriteInitialization::writeProperties(const QString &varName,
bool delayProperty = false;
// special case for the property `geometry': Do not use position
- if (isTopLevel && propertyName == QLatin1String("geometry") && p->elementRect()) {
+ if (isTopLevel && propertyName == "geometry"_L1 && p->elementRect()) {
const DomRect *r = p->elementRect();
m_output << m_indent << varName << language::derefPointer << "resize("
<< r->elementWidth() << ", " << r->elementHeight() << ')' << language::eol;
continue;
}
- if (propertyName == QLatin1String("currentRow") // QListWidget::currentRow
- && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QListWidget"))) {
+ if (propertyName == "currentRow"_L1 // QListWidget::currentRow
+ && m_uic->customWidgetsInfo()->extends(className, "QListWidget")) {
m_delayedOut << m_indent << varName << language::derefPointer
<< "setCurrentRow(" << p->elementNumber() << ')' << language::eol;
continue;
}
static const QStringList currentIndexWidgets = {
- QLatin1String("QComboBox"), QLatin1String("QStackedWidget"),
- QLatin1String("QTabWidget"), QLatin1String("QToolBox")
+ u"QComboBox"_s, u"QStackedWidget"_s,
+ u"QTabWidget"_s, u"QToolBox"_s
};
- if (propertyName == QLatin1String("currentIndex") // set currentIndex later
+ if (propertyName == "currentIndex"_L1 // set currentIndex later
&& (m_uic->customWidgetsInfo()->extendsOneOf(className, currentIndexWidgets))) {
m_delayedOut << m_indent << varName << language::derefPointer
<< "setCurrentIndex(" << p->elementNumber() << ')' << language::eol;
continue;
}
- if (propertyName == QLatin1String("tabSpacing")
- && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QToolBox"))) {
+ if (propertyName == "tabSpacing"_L1
+ && m_uic->customWidgetsInfo()->extends(className, "QToolBox")) {
m_delayedOut << m_indent << varName << language::derefPointer
<< "layout()" << language::derefPointer << "setSpacing("
<< p->elementNumber() << ')' << language::eol;
continue;
}
- if (propertyName == QLatin1String("control") // ActiveQt support
- && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QAxWidget"))) {
+ if (propertyName == "control"_L1 // ActiveQt support
+ && m_uic->customWidgetsInfo()->extends(className, "QAxWidget")) {
// already done ;)
continue;
}
- if (propertyName == QLatin1String("default")
- && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QPushButton"))) {
+ if (propertyName == "default"_L1
+ && m_uic->customWidgetsInfo()->extends(className, "QPushButton")) {
// QTBUG-44406: Setting of QPushButton::default needs to be delayed until the parent is set
delayProperty = true;
- } else if (propertyName == QLatin1String("database")
+ } else if (propertyName == "database"_L1
&& p->elementStringList()) {
// Sql support
continue;
- } else if (propertyName == QLatin1String("frameworkCode")
+ } else if (propertyName == "frameworkCode"_L1
&& p->kind() == DomProperty::Bool) {
// Sql support
continue;
- } else if (propertyName == QLatin1String("orientation")
- && m_uic->customWidgetsInfo()->extends(className, QLatin1String("Line"))) {
+ } else if (propertyName == "orientation"_L1
+ && m_uic->customWidgetsInfo()->extends(className, "Line")) {
// Line support
- QString shape = QLatin1String("QFrame::HLine");
- if (p->elementEnum() == QLatin1String("Qt::Vertical"))
- shape = QLatin1String("QFrame::VLine");
+ QString shape = u"QFrame::Shape::HLine"_s;
+ if (p->elementEnum().endsWith("::Vertical"_L1))
+ shape = u"QFrame::Shape::VLine"_s;
m_output << m_indent << varName << language::derefPointer << "setFrameShape("
<< language::enumValue(shape) << ')' << language::eol;
@@ -1291,32 +1310,32 @@ void WriteInitialization::writeProperties(const QString &varName,
if (!frameShadowEncountered) {
m_output << m_indent << varName << language::derefPointer
<< "setFrameShadow("
- << language::enumValue(QLatin1String("QFrame::Sunken"))
+ << language::enumValue("QFrame::Shadow::Sunken"_L1)
<< ')' << language::eol;
}
continue;
- } else if ((flags & WritePropertyIgnoreMargin) && propertyName == QLatin1String("margin")) {
+ } else if ((flags & WritePropertyIgnoreMargin) && propertyName == "margin"_L1) {
continue;
- } else if ((flags & WritePropertyIgnoreSpacing) && propertyName == QLatin1String("spacing")) {
+ } else if ((flags & WritePropertyIgnoreSpacing) && propertyName == "spacing"_L1) {
continue;
- } else if (propertyName == QLatin1String("leftMargin") && p->kind() == DomProperty::Number) {
+ } else if (propertyName == "leftMargin"_L1 && p->kind() == DomProperty::Number) {
leftMargin = p->elementNumber();
continue;
- } else if (propertyName == QLatin1String("topMargin") && p->kind() == DomProperty::Number) {
+ } else if (propertyName == "topMargin"_L1 && p->kind() == DomProperty::Number) {
topMargin = p->elementNumber();
continue;
- } else if (propertyName == QLatin1String("rightMargin") && p->kind() == DomProperty::Number) {
+ } else if (propertyName == "rightMargin"_L1 && p->kind() == DomProperty::Number) {
rightMargin = p->elementNumber();
continue;
- } else if (propertyName == QLatin1String("bottomMargin") && p->kind() == DomProperty::Number) {
+ } else if (propertyName == "bottomMargin"_L1 && p->kind() == DomProperty::Number) {
bottomMargin = p->elementNumber();
continue;
- } else if (propertyName == QLatin1String("numDigits") // Deprecated in Qt 4, removed in Qt 5.
- && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QLCDNumber"))) {
+ } else if (propertyName == "numDigits"_L1 // Deprecated in Qt 4, removed in Qt 5.
+ && m_uic->customWidgetsInfo()->extends(className, "QLCDNumber")) {
qWarning("Widget '%s': Deprecated property QLCDNumber::numDigits encountered. It has been replaced by QLCDNumber::digitCount.",
qPrintable(varName));
- propertyName = QLatin1String("digitCount");
- } else if (propertyName == QLatin1String("frameShadow")) {
+ propertyName = "digitCount"_L1;
+ } else if (propertyName == "frameShadow"_L1) {
frameShadowEncountered = true;
}
@@ -1332,7 +1351,7 @@ void WriteInitialization::writeProperties(const QString &varName,
str << language::derefPointer <<"set" << propertyName.at(0).toUpper()
<< QStringView{propertyName}.mid(1) << '(';
} else {
- str << language::derefPointer << QLatin1String("setProperty(\"")
+ str << language::derefPointer << "setProperty(\""_L1
<< propertyName << "\", ";
if (language::language() == Language::Cpp) {
str << "QVariant";
@@ -1354,15 +1373,16 @@ void WriteInitialization::writeProperties(const QString &varName,
propertyValue = domColor2QString(p->elementColor());
break;
case DomProperty::Cstring:
- if (propertyName == QLatin1String("buddy") && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QLabel"))) {
+ if (propertyName == "buddy"_L1 && m_uic->customWidgetsInfo()->extends(className, "QLabel")) {
Buddy buddy = { varName, p->elementCstring() };
m_buddies.append(std::move(buddy));
} else {
+ const bool useQByteArray = !stdset && language::language() == Language::Cpp;
QTextStream str(&propertyValue);
- if (!stdset)
+ if (useQByteArray)
str << "QByteArray(";
str << language::charliteral(p->elementCstring(), m_dindent);
- if (!stdset)
+ if (useQByteArray)
str << ')';
}
break;
@@ -1372,9 +1392,9 @@ void WriteInitialization::writeProperties(const QString &varName,
break;
case DomProperty::CursorShape:
if (p->hasAttributeStdset() && !p->attributeStdset())
- varNewName += language::derefPointer + QLatin1String("viewport()");
- propertyValue = QLatin1String("QCursor(Qt") + language::qualifier
- + p->elementCursorShape() + QLatin1Char(')');
+ varNewName += language::derefPointer + "viewport()"_L1;
+ propertyValue = "QCursor(Qt"_L1 + language::qualifier + "CursorShape"_L1
+ + language::qualifier + p->elementCursorShape() + u')';
break;
case DomProperty::Enum:
propertyValue = p->elementEnum();
@@ -1397,12 +1417,12 @@ void WriteInitialization::writeProperties(const QString &varName,
break;
case DomProperty::Palette: {
const DomPalette *pal = p->elementPalette();
- const QString paletteName = m_driver->unique(QLatin1String("palette"));
+ const QString paletteName = m_driver->unique("palette"_L1);
m_output << m_indent << language::stackVariable("QPalette", paletteName)
<< language::eol;
- writeColorGroup(pal->elementActive(), QLatin1String("QPalette::Active"), paletteName);
- writeColorGroup(pal->elementInactive(), QLatin1String("QPalette::Inactive"), paletteName);
- writeColorGroup(pal->elementDisabled(), QLatin1String("QPalette::Disabled"), paletteName);
+ writeColorGroup(pal->elementActive(), "QPalette::Active"_L1, paletteName);
+ writeColorGroup(pal->elementInactive(), "QPalette::Inactive"_L1, paletteName);
+ writeColorGroup(pal->elementDisabled(), "QPalette::Disabled"_L1, paletteName);
propertyValue = paletteName;
break;
@@ -1462,7 +1482,7 @@ void WriteInitialization::writeProperties(const QString &varName,
break;
}
case DomProperty::String: {
- if (propertyName == QLatin1String("objectName")) {
+ if (propertyName == "objectName"_L1) {
const QString v = p->elementString()->text();
if (v == varName)
break;
@@ -1478,17 +1498,17 @@ void WriteInitialization::writeProperties(const QString &varName,
break;
case DomProperty::UInt:
propertyValue = QString::number(p->elementUInt());
- propertyValue += QLatin1Char('u');
+ propertyValue += u'u';
break;
case DomProperty::LongLong:
- propertyValue = QLatin1String("Q_INT64_C(");
+ propertyValue = "Q_INT64_C("_L1;
propertyValue += QString::number(p->elementLongLong());
- propertyValue += QLatin1Char(')');;
+ propertyValue += u')';
break;
case DomProperty::ULongLong:
- propertyValue = QLatin1String("Q_UINT64_C(");
+ propertyValue = "Q_UINT64_C("_L1;
propertyValue += QString::number(p->elementULongLong());
- propertyValue += QLatin1Char(')');
+ propertyValue += u')';
break;
case DomProperty::Float:
propertyValue = QString::number(p->elementFloat(), 'f', 8);
@@ -1586,16 +1606,22 @@ QString WriteInitialization::writeSizePolicy(const DomSizePolicy *sp)
// insert with new name
- const QString spName = m_driver->unique(QLatin1String("sizePolicy"));
+ const QString spName = m_driver->unique("sizePolicy"_L1);
m_sizePolicyNameMap.insert(sizePolicyHandle, spName);
m_output << m_indent << language::stackVariableWithInitParameters("QSizePolicy", spName);
+ QString horizPolicy;
+ QString vertPolicy;
if (sp->hasElementHSizeType() && sp->hasElementVSizeType()) {
- m_output << "QSizePolicy" << language::qualifier << language::sizePolicy(sp->elementHSizeType())
- << ", QSizePolicy" << language::qualifier << language::sizePolicy(sp->elementVSizeType());
+ horizPolicy = language::sizePolicy(sp->elementHSizeType());
+ vertPolicy = language::sizePolicy(sp->elementVSizeType());
} else if (sp->hasAttributeHSizeType() && sp->hasAttributeVSizeType()) {
- m_output << "QSizePolicy" << language::qualifier << sp->attributeHSizeType()
- << ", QSizePolicy" << language::qualifier << sp->attributeVSizeType();
+ horizPolicy = sp->attributeHSizeType();
+ vertPolicy = sp->attributeVSizeType();
+ }
+ if (!horizPolicy.isEmpty() && !vertPolicy.isEmpty()) {
+ m_output << language::enumValue(expandSizePolicyEnum(horizPolicy))
+ << ", " << language::enumValue(expandSizePolicyEnum(vertPolicy));
}
m_output << ')' << language::eol;
@@ -1618,7 +1644,7 @@ QString WriteInitialization::writeFontProperties(const DomFont *f)
}
// insert with new name
- const QString fontName = m_driver->unique(QLatin1String("font"));
+ const QString fontName = m_driver->unique("font"_L1);
m_fontPropertiesNameMap.insert(FontHandle(f), fontName);
m_output << m_indent << language::stackVariable("QFont", fontName)
@@ -1634,10 +1660,14 @@ QString WriteInitialization::writeFontProperties(const DomFont *f)
<< ")" << language::eol;
}
- if (f->hasElementBold()) {
+ if (f->hasElementFontWeight()) {
+ m_output << m_indent << fontName << ".setWeight(QFont"
+ << language::qualifier << f->elementFontWeight() << ')' << language::eol;
+ } else if (f->hasElementBold()) {
m_output << m_indent << fontName << ".setBold("
<< language::boolValue(f->elementBold()) << ')' << language::eol;
}
+
if (f->hasElementItalic()) {
m_output << m_indent << fontName << ".setItalic("
<< language::boolValue(f->elementItalic()) << ')' << language::eol;
@@ -1664,6 +1694,11 @@ QString WriteInitialization::writeFontProperties(const DomFont *f)
m_output << m_indent << fontName << ".setStyleStrategy(QFont"
<< language::qualifier << f->elementStyleStrategy() << ')' << language::eol;
}
+ if (f->hasElementHintingPreference()) {
+ m_output << m_indent << fontName << ".setHintingPreference(QFont"
+ << language::qualifier << f->elementHintingPreference() << ')' << language::eol;
+ }
+
return fontName;
}
@@ -1673,8 +1708,9 @@ static void writeIconAddFile(QTextStream &output, const QString &indent,
{
output << indent << iconName << ".addFile("
<< language::qstring(fileName, indent) << ", QSize(), QIcon"
- << language::qualifier << mode << ", QIcon" << language::qualifier
- << state << ')' << language::eol;
+ << language::qualifier << "Mode" << language::qualifier << mode
+ << ", QIcon" << language::qualifier << "State" << language::qualifier << state
+ << ')' << language::eol;
}
// Post 4.4 write resource icon
@@ -1722,7 +1758,8 @@ static void writeIconAddPixmap(QTextStream &output, const QString &indent,
const char *mode, const char *state)
{
output << indent << iconName << ".addPixmap(" << call << ", QIcon"
- << language::qualifier << mode << ", QIcon" << language::qualifier
+ << language::qualifier << "Mode" << language::qualifier << mode
+ << ", QIcon" << language::qualifier << "State" << language::qualifier
<< state << ')' << language::eol;
}
@@ -1733,46 +1770,99 @@ void WriteInitialization::writePixmapFunctionIcon(QTextStream &output,
{
if (i->hasElementNormalOff()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementNormalOff()->text()),
+ pixCall("QPixmap"_L1, i->elementNormalOff()->text()),
"Normal", "Off");
}
if (i->hasElementNormalOn()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementNormalOn()->text()),
+ pixCall("QPixmap"_L1, i->elementNormalOn()->text()),
"Normal", "On");
}
if (i->hasElementDisabledOff()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementDisabledOff()->text()),
+ pixCall("QPixmap"_L1, i->elementDisabledOff()->text()),
"Disabled", "Off");
}
if (i->hasElementDisabledOn()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementDisabledOn()->text()),
+ pixCall("QPixmap"_L1, i->elementDisabledOn()->text()),
"Disabled", "On");
}
if (i->hasElementActiveOff()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementActiveOff()->text()),
+ pixCall("QPixmap"_L1, i->elementActiveOff()->text()),
"Active", "Off");
}
if (i->hasElementActiveOn()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementActiveOn()->text()),
+ pixCall("QPixmap"_L1, i->elementActiveOn()->text()),
"Active", "On");
}
if (i->hasElementSelectedOff()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementSelectedOff()->text()),
+ pixCall("QPixmap"_L1, i->elementSelectedOff()->text()),
"Selected", "Off");
}
if (i->hasElementSelectedOn()) {
writeIconAddPixmap(output, indent, iconName,
- pixCall(QLatin1String("QPixmap"), i->elementSelectedOn()->text()),
+ pixCall("QPixmap"_L1, i->elementSelectedOn()->text()),
"Selected", "On");
}
}
+// Write QIcon::fromTheme() (value from enum or variable)
+struct iconFromTheme
+{
+ explicit iconFromTheme(const QString &theme) : m_theme(theme) {}
+
+ QString m_theme;
+};
+
+QTextStream &operator<<(QTextStream &str, const iconFromTheme &i)
+{
+ str << "QIcon" << language::qualifier << "fromTheme(" << i.m_theme << ')';
+ return str;
+}
+
+// Write QIcon::fromTheme() for an XDG icon from string literal
+struct iconFromThemeStringLiteral
+{
+ explicit iconFromThemeStringLiteral(const QString &theme) : m_theme(theme) {}
+
+ QString m_theme;
+};
+
+QTextStream &operator<<(QTextStream &str, const iconFromThemeStringLiteral &i)
+{
+ str << "QIcon" << language::qualifier << "fromTheme(" << language::qstring(i.m_theme) << ')';
+ return str;
+}
+
+// Write QIcon::fromTheme() with a path as fallback, add a check using
+// QIcon::hasThemeIcon().
+void WriteInitialization::writeThemeIconCheckAssignment(const QString &themeValue,
+ const QString &iconName,
+ const DomResourceIcon *i)
+
+{
+ const bool isCpp = language::language() == Language::Cpp;
+ m_output << m_indent << "if ";
+ if (isCpp)
+ m_output << '(';
+ m_output << "QIcon" << language::qualifier << "hasThemeIcon("
+ << themeValue << ')' << (isCpp ? ") {" : ":") << '\n'
+ << m_dindent << iconName << " = " << iconFromTheme(themeValue)
+ << language::eol;
+ m_output << m_indent << (isCpp ? "} else {" : "else:") << '\n';
+ if (m_uic->pixmapFunction().isEmpty())
+ writeResourceIcon(m_output, iconName, m_dindent, i);
+ else
+ writePixmapFunctionIcon(m_output, iconName, m_dindent, i);
+ if (isCpp)
+ m_output << m_indent << '}';
+ m_output << '\n';
+}
+
QString WriteInitialization::writeIconProperties(const DomResourceIcon *i)
{
// check cache
@@ -1782,7 +1872,7 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i)
return it.value();
// insert with new name
- const QString iconName = m_driver->unique(QLatin1String("icon"));
+ const QString iconName = m_driver->unique("icon"_L1);
m_iconPropertiesNameMap.insert(IconHandle(i), iconName);
const bool isCpp = language::language() == Language::Cpp;
@@ -1791,13 +1881,14 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i)
m_output << m_indent;
if (isCpp)
m_output << "const QIcon ";
- m_output << iconName << " = " << pixCall(QLatin1String("QIcon"), i->text())
+ m_output << iconName << " = " << pixCall("QIcon"_L1, i->text())
<< language::eol;
return iconName;
}
// 4.4 onwards
- if (i->attributeTheme().isEmpty()) {
+ QString theme = i->attributeTheme();
+ if (theme.isEmpty()) {
// No theme: Write resource icon as is
m_output << m_indent << language::stackVariable("QIcon", iconName)
<< language::eol;
@@ -1808,12 +1899,21 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i)
return iconName;
}
+ const bool isThemeEnum = theme.startsWith("QIcon::"_L1);
+ if (isThemeEnum)
+ theme = language::enumValue(theme);
+
// Theme: Generate code to check the theme and default to resource
if (iconHasStatePixmaps(i)) {
// Theme + default state pixmaps:
// Generate code to check the theme and default to state pixmaps
m_output << m_indent << language::stackVariable("QIcon", iconName) << language::eol;
- const char themeNameStringVariableC[] = "iconThemeName";
+ if (isThemeEnum) {
+ writeThemeIconCheckAssignment(theme, iconName, i);
+ return iconName;
+ }
+
+ static constexpr auto themeNameStringVariableC = "iconThemeName"_L1;
// Store theme name in a variable
m_output << m_indent;
if (m_firstThemeIcon) { // Declare variable string
@@ -1822,32 +1922,19 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i)
m_firstThemeIcon = false;
}
m_output << themeNameStringVariableC << " = "
- << language::qstring(i->attributeTheme()) << language::eol;
- m_output << m_indent << "if ";
- if (isCpp)
- m_output << '(';
- m_output << "QIcon" << language::qualifier << "hasThemeIcon("
- << themeNameStringVariableC << ')' << (isCpp ? ") {" : ":") << '\n'
- << m_dindent << iconName << " = QIcon" << language::qualifier << "fromTheme("
- << themeNameStringVariableC << ')' << language::eol
- << m_indent << (isCpp ? "} else {" : "else:") << '\n';
- if (m_uic->pixmapFunction().isEmpty())
- writeResourceIcon(m_output, iconName, m_dindent, i);
- else
- writePixmapFunctionIcon(m_output, iconName, m_dindent, i);
- m_output << m_indent;
- if (isCpp)
- m_output << '}';
- m_output << '\n';
+ << language::qstring(theme) << language::eol;
+ writeThemeIconCheckAssignment(themeNameStringVariableC, iconName, i);
return iconName;
}
// Theme, but no state pixmaps: Construct from theme directly.
m_output << m_indent
- << language::stackVariableWithInitParameters("QIcon", iconName)
- << "QIcon" << language::qualifier << "fromTheme("
- << language::qstring(i->attributeTheme()) << "))"
- << language::eol;
+ << language::stackVariableWithInitParameters("QIcon", iconName);
+ if (isThemeEnum)
+ m_output << iconFromTheme(theme);
+ else
+ m_output << iconFromThemeStringLiteral(theme);
+ m_output << ')' << language::eol;
return iconName;
}
@@ -1867,7 +1954,7 @@ QString WriteInitialization::domColor2QString(const DomColor *c)
static inline QVersionNumber colorRoleVersionAdded(const QString &roleName)
{
- if (roleName == QLatin1String("PlaceholderText"))
+ if (roleName == "PlaceholderText"_L1)
return QVersionNumber(5, 12, 0);
return QVersionNumber();
}
@@ -1915,7 +2002,7 @@ void WriteInitialization::writeColorGroup(DomColorGroup *colorGroup, const QStri
QString WriteInitialization::writeBrushInitialization(const DomBrush *brush)
{
// Simple solid, colored brushes are cached
- const bool solidColoredBrush = !brush->hasAttributeBrushStyle() || brush->attributeBrushStyle() == QLatin1String("SolidPattern");
+ const bool solidColoredBrush = !brush->hasAttributeBrushStyle() || brush->attributeBrushStyle() == "SolidPattern"_L1;
uint rgb = 0;
if (solidColoredBrush) {
if (const DomColor *color = brush->elementColor()) {
@@ -1929,7 +2016,7 @@ QString WriteInitialization::writeBrushInitialization(const DomBrush *brush)
}
}
// Create and enter into cache if simple
- const QString brushName = m_driver->unique(QLatin1String("brush"));
+ const QString brushName = m_driver->unique("brush"_L1);
writeBrush(brush, brushName);
if (solidColoredBrush)
m_colorBrushHash.insert(rgb, brushName);
@@ -1938,24 +2025,24 @@ QString WriteInitialization::writeBrushInitialization(const DomBrush *brush)
void WriteInitialization::writeBrush(const DomBrush *brush, const QString &brushName)
{
- QString style = QLatin1String("SolidPattern");
+ QString style = u"SolidPattern"_s;
if (brush->hasAttributeBrushStyle())
style = brush->attributeBrushStyle();
- if (style == QLatin1String("LinearGradientPattern") ||
- style == QLatin1String("RadialGradientPattern") ||
- style == QLatin1String("ConicalGradientPattern")) {
+ if (style == "LinearGradientPattern"_L1 ||
+ style == "RadialGradientPattern"_L1 ||
+ style == "ConicalGradientPattern"_L1) {
const DomGradient *gradient = brush->elementGradient();
const QString gradientType = gradient->attributeType();
- const QString gradientName = m_driver->unique(QLatin1String("gradient"));
- if (gradientType == QLatin1String("LinearGradient")) {
+ const QString gradientName = m_driver->unique("gradient"_L1);
+ if (gradientType == "LinearGradient"_L1) {
m_output << m_indent
<< language::stackVariableWithInitParameters("QLinearGradient", gradientName)
<< gradient->attributeStartX()
<< ", " << gradient->attributeStartY()
<< ", " << gradient->attributeEndX()
<< ", " << gradient->attributeEndY() << ')' << language::eol;
- } else if (gradientType == QLatin1String("RadialGradient")) {
+ } else if (gradientType == "RadialGradient"_L1) {
m_output << m_indent
<< language::stackVariableWithInitParameters("QRadialGradient", gradientName)
<< gradient->attributeCentralX()
@@ -1963,7 +2050,7 @@ void WriteInitialization::writeBrush(const DomBrush *brush, const QString &brush
<< ", " << gradient->attributeRadius()
<< ", " << gradient->attributeFocalX()
<< ", " << gradient->attributeFocalY() << ')' << language::eol;
- } else if (gradientType == QLatin1String("ConicalGradient")) {
+ } else if (gradientType == "ConicalGradient"_L1) {
m_output << m_indent
<< language::stackVariableWithInitParameters("QConicalGradient", gradientName)
<< gradient->attributeCentralX()
@@ -1991,7 +2078,7 @@ void WriteInitialization::writeBrush(const DomBrush *brush, const QString &brush
m_output << m_indent
<< language::stackVariableWithInitParameters("QBrush", brushName)
<< gradientName << ')' << language::eol;
- } else if (style == QLatin1String("TexturePattern")) {
+ } else if (style == "TexturePattern"_L1) {
const DomProperty *property = brush->elementTexture();
const QString iconValue = iconCall(property);
@@ -2056,42 +2143,42 @@ QString WriteInitialization::iconCall(const DomProperty *icon)
QString WriteInitialization::pixCall(const DomProperty *p) const
{
- QString type, s;
+ QLatin1StringView type;
+ QString s;
switch (p->kind()) {
case DomProperty::IconSet:
- type = QLatin1String("QIcon");
+ type = "QIcon"_L1;
s = p->elementIconSet()->text();
break;
case DomProperty::Pixmap:
- type = QLatin1String("QPixmap");
+ type = "QPixmap"_L1;
s = p->elementPixmap()->text();
break;
default:
qWarning("%s: Warning: Unknown icon format encountered. The ui-file was generated with a too-recent version of Designer.",
qPrintable(m_option.messagePrefix()));
- return QLatin1String("QIcon()");
+ return "QIcon()"_L1;
break;
}
return pixCall(type, s);
}
-QString WriteInitialization::pixCall(const QString &t, const QString &text) const
+QString WriteInitialization::pixCall(QLatin1StringView t, const QString &text) const
{
- QString type = t;
- if (text.isEmpty()) {
- type += QLatin1String("()");
- return type;
- }
+ if (text.isEmpty())
+ return t % "()"_L1;
- QTextStream str(&type);
+ QString result;
+ QTextStream str(&result);
+ str << t;
str << '(';
- QString pixFunc = m_uic->pixmapFunction();
+ const QString pixFunc = m_uic->pixmapFunction();
if (pixFunc.isEmpty())
str << language::qstring(text, m_dindent);
else
str << pixFunc << '(' << language::charliteral(text, m_dindent) << ')';
str << ')';
- return type;
+ return result;
}
void WriteInitialization::initializeComboBox(DomWidget *w)
@@ -2106,8 +2193,8 @@ void WriteInitialization::initializeComboBox(DomWidget *w)
for (int i = 0; i < items.size(); ++i) {
const DomItem *item = items.at(i);
const DomPropertyMap properties = propertyMap(item->elementProperty());
- const DomProperty *text = properties.value(QLatin1String("text"));
- const DomProperty *icon = properties.value(QLatin1String("icon"));
+ const DomProperty *text = properties.value("text"_L1);
+ const DomProperty *icon = properties.value("icon"_L1);
QString iconValue;
if (icon)
@@ -2134,7 +2221,7 @@ QString WriteInitialization::disableSorting(DomWidget *w, const QString &varName
// turn off sortingEnabled to force programmatic item order (setItem())
QString tempName;
if (!w->elementItem().isEmpty()) {
- tempName = m_driver->unique(QLatin1String("__sortingEnabled"));
+ tempName = m_driver->unique("__sortingEnabled"_L1);
m_refreshOut << "\n";
m_refreshOut << m_indent;
if (language::language() == Language::Cpp)
@@ -2226,10 +2313,10 @@ void WriteInitialization::addQtFlagsInitializer(Item *item,
const DomPropertyMap &properties, const QString &name, int column) const
{
if (const DomProperty *p = properties.value(name)) {
- const QString orOperator = QLatin1Char('|') + language::qtQualifier;
+ const QString orOperator = u'|' + language::qtQualifier;
QString v = p->elementSet();
if (!v.isEmpty()) {
- v.replace(QLatin1Char('|'), orOperator);
+ v.replace(u'|', orOperator);
addInitializer(item, name, column, language::qtQualifier + v);
}
}
@@ -2255,20 +2342,20 @@ void WriteInitialization::addQtEnumInitializer(Item *item,
void WriteInitialization::addCommonInitializers(Item *item,
const DomPropertyMap &properties, int column)
{
- if (const DomProperty *icon = properties.value(QLatin1String("icon")))
- addInitializer(item, QLatin1String("icon"), column, iconCall(icon));
- addBrushInitializer(item, properties, QLatin1String("foreground"), column);
- addBrushInitializer(item, properties, QLatin1String("background"), column);
- if (const DomProperty *font = properties.value(QLatin1String("font")))
- addInitializer(item, QLatin1String("font"), column, writeFontProperties(font->elementFont()));
- addQtFlagsInitializer(item, properties, QLatin1String("textAlignment"), column);
- addQtEnumInitializer(item, properties, QLatin1String("checkState"), column);
- addStringInitializer(item, properties, QLatin1String("text"), column);
- addStringInitializer(item, properties, QLatin1String("toolTip"), column,
+ if (const DomProperty *icon = properties.value("icon"_L1))
+ addInitializer(item, "icon"_L1, column, iconCall(icon));
+ addBrushInitializer(item, properties, "foreground"_L1, column);
+ addBrushInitializer(item, properties, "background"_L1, column);
+ if (const DomProperty *font = properties.value("font"_L1))
+ addInitializer(item, "font"_L1, column, writeFontProperties(font->elementFont()));
+ addQtFlagsInitializer(item, properties, "textAlignment"_L1, column);
+ addQtEnumInitializer(item, properties, "checkState"_L1, column);
+ addStringInitializer(item, properties, "text"_L1, column);
+ addStringInitializer(item, properties, "toolTip"_L1, column,
toolTipConfigKey());
- addStringInitializer(item, properties, QLatin1String("whatsThis"), column,
+ addStringInitializer(item, properties, "whatsThis"_L1, column,
whatsThisConfigKey());
- addStringInitializer(item, properties, QLatin1String("statusTip"), column,
+ addStringInitializer(item, properties, "statusTip"_L1, column,
statusTipConfigKey());
}
@@ -2289,8 +2376,8 @@ void WriteInitialization::initializeListWidget(DomWidget *w)
const DomPropertyMap properties = propertyMap(domItem->elementProperty());
- Item item(QLatin1String("QListWidgetItem"), m_indent, m_output, m_refreshOut, m_driver);
- addQtFlagsInitializer(&item, properties, QLatin1String("flags"));
+ Item item("QListWidgetItem"_L1, m_indent, m_output, m_refreshOut, m_driver);
+ addQtFlagsInitializer(&item, properties, "flags"_L1);
addCommonInitializers(&item, properties);
item.writeSetupUi(varName);
@@ -2306,7 +2393,7 @@ void WriteInitialization::initializeTreeWidget(DomWidget *w)
const QString varName = m_driver->findOrInsertWidget(w);
// columns
- Item item(QLatin1String("QTreeWidgetItem"), m_indent, m_output, m_refreshOut, m_driver);
+ Item item("QTreeWidgetItem"_L1, m_indent, m_output, m_refreshOut, m_driver);
const auto &columns = w->elementColumn();
for (int i = 0; i < columns.size(); ++i) {
@@ -2315,7 +2402,7 @@ void WriteInitialization::initializeTreeWidget(DomWidget *w)
const DomPropertyMap properties = propertyMap(column->elementProperty());
addCommonInitializers(&item, properties, i);
- if (const DomProperty *p = properties.value(QLatin1String("text"))) {
+ if (const DomProperty *p = properties.value("text"_L1)) {
DomString *str = p->elementString();
if (str && str->text().isEmpty()) {
m_output << m_indent << varName << language::derefPointer
@@ -2325,7 +2412,7 @@ void WriteInitialization::initializeTreeWidget(DomWidget *w)
}
}
const QString itemName = item.writeSetupUi(QString(), Item::DontConstruct);
- item.writeRetranslateUi(varName + language::derefPointer + QLatin1String("headerItem()"));
+ item.writeRetranslateUi(varName + language::derefPointer + "headerItem()"_L1);
if (!itemName.isNull()) {
m_output << m_indent << varName << language::derefPointer
<< "setHeaderItem(" << itemName << ')' << language::eol;
@@ -2337,7 +2424,7 @@ void WriteInitialization::initializeTreeWidget(DomWidget *w)
QString tempName = disableSorting(w, varName);
const auto items = initializeTreeWidgetItems(w->elementItem());
- for (int i = 0; i < items.count(); i++) {
+ for (int i = 0; i < items.size(); i++) {
Item *itm = items[i];
itm->writeSetupUi(varName);
QString parentPath;
@@ -2368,7 +2455,7 @@ WriteInitialization::Items WriteInitialization::initializeTreeWidgetItems(const
for (int i = 0; i < numDomItems; ++i) {
const DomItem *domItem = domItems.at(i);
- Item *item = new Item(QLatin1String("QTreeWidgetItem"), m_indent, m_output, m_refreshOut, m_driver);
+ Item *item = new Item("QTreeWidgetItem"_L1, m_indent, m_output, m_refreshOut, m_driver);
items << item;
QHash<QString, DomProperty *> map;
@@ -2376,7 +2463,7 @@ WriteInitialization::Items WriteInitialization::initializeTreeWidgetItems(const
int col = -1;
const DomPropertyList properties = domItem->elementProperty();
for (DomProperty *p : properties) {
- if (p->attributeName() == QLatin1String("text")) {
+ if (p->attributeName() == "text"_L1) {
if (!map.isEmpty()) {
addCommonInitializers(item, map, col);
map.clear();
@@ -2387,7 +2474,7 @@ WriteInitialization::Items WriteInitialization::initializeTreeWidgetItems(const
}
addCommonInitializers(item, map, col);
// AbstractFromBuilder saves flags last, so they always end up in the last column's map.
- addQtFlagsInitializer(item, map, QLatin1String("flags"));
+ addQtFlagsInitializer(item, map, "flags"_L1);
const auto subItems = initializeTreeWidgetItems(domItem->elementItem());
for (Item *subItem : subItems)
@@ -2417,7 +2504,7 @@ void WriteInitialization::initializeTableWidget(DomWidget *w)
if (!column->elementProperty().isEmpty()) {
const DomPropertyMap properties = propertyMap(column->elementProperty());
- Item item(QLatin1String("QTableWidgetItem"), m_indent, m_output, m_refreshOut, m_driver);
+ Item item("QTableWidgetItem"_L1, m_indent, m_output, m_refreshOut, m_driver);
addCommonInitializers(&item, properties);
QString itemName = item.writeSetupUi(QString(), Item::ConstructItemAndVariable);
@@ -2447,7 +2534,7 @@ void WriteInitialization::initializeTableWidget(DomWidget *w)
if (!row->elementProperty().isEmpty()) {
const DomPropertyMap properties = propertyMap(row->elementProperty());
- Item item(QLatin1String("QTableWidgetItem"), m_indent, m_output, m_refreshOut, m_driver);
+ Item item("QTableWidgetItem"_L1, m_indent, m_output, m_refreshOut, m_driver);
addCommonInitializers(&item, properties);
QString itemName = item.writeSetupUi(QString(), Item::ConstructItemAndVariable);
@@ -2470,8 +2557,8 @@ void WriteInitialization::initializeTableWidget(DomWidget *w)
const int c = cell->attributeColumn();
const DomPropertyMap properties = propertyMap(cell->elementProperty());
- Item item(QLatin1String("QTableWidgetItem"), m_indent, m_output, m_refreshOut, m_driver);
- addQtFlagsInitializer(&item, properties, QLatin1String("flags"));
+ Item item("QTableWidgetItem"_L1, m_indent, m_output, m_refreshOut, m_driver);
+ addQtFlagsInitializer(&item, properties, "flags"_L1);
addCommonInitializers(&item, properties);
QString itemName = item.writeSetupUi(QString(), Item::ConstructItemAndVariable);
@@ -2523,7 +2610,7 @@ QString WriteInitialization::trCall(const QString &str, const QString &commentHi
void WriteInitialization::initializeMenu(DomWidget *w, const QString &/*parentWidget*/)
{
const QString menuName = m_driver->findOrInsertWidget(w);
- const QString menuAction = menuName + QLatin1String("Action");
+ const QString menuAction = menuName + "Action"_L1;
const DomAction *action = m_driver->actionByName(menuAction);
if (action && action->hasAttributeMenu()) {
@@ -2618,7 +2705,7 @@ ConnectionSyntax WriteInitialization::connectionSyntax(const language::SignalSlo
return ConnectionSyntax::StringBased;
}
- return sender.signature.endsWith(QLatin1String("()"))
+ return sender.signature.endsWith("()"_L1)
|| (!isCustomWidget(sender.className) && !isCustomWidget(receiver.className))
? ConnectionSyntax::MemberFunctionPtr : ConnectionSyntax::StringBased;
}
@@ -2640,10 +2727,21 @@ void WriteInitialization::acceptConnection(DomConnection *connection)
return;
}
const QString senderSignature = connection->elementSignal();
+ const QString slotSignature = connection->elementSlot();
+ const bool senderAmbiguous = m_uic->customWidgetsInfo()->isAmbiguousSignal(senderDecl.className,
+ senderSignature);
+ const bool slotAmbiguous = m_uic->customWidgetsInfo()->isAmbiguousSlot(receiverDecl.className,
+ slotSignature);
+
+ language::SignalSlotOptions signalOptions;
+ signalOptions.setFlag(language::SignalSlotOption::Ambiguous, senderAmbiguous);
+ language::SignalSlotOptions slotOptions;
+ slotOptions.setFlag(language::SignalSlotOption::Ambiguous, slotAmbiguous);
+
language::SignalSlot theSignal{senderDecl.name, senderSignature,
- senderDecl.className};
- language::SignalSlot theSlot{receiverDecl.name, connection->elementSlot(),
- receiverDecl.className};
+ senderDecl.className, signalOptions};
+ language::SignalSlot theSlot{receiverDecl.name, slotSignature,
+ receiverDecl.className, slotOptions};
m_output << m_indent;
language::formatConnection(m_output, theSignal, theSlot,
@@ -2714,7 +2812,7 @@ QString WriteInitialization::Item::writeSetupUi(const QString &parent, Item::Emp
if (generateMultiDirective)
generateMultiDirectiveBegin(m_setupUiStream, m_setupUiData.directives);
- const QString uniqueName = m_driver->unique(QLatin1String("__") + m_itemClassName.toLower());
+ const QString uniqueName = m_driver->unique("__"_L1 + m_itemClassName.toLower());
m_setupUiStream << m_indent;
if (language::language() == Language::Cpp)
m_setupUiStream << m_itemClassName << " *";
@@ -2738,7 +2836,7 @@ QString WriteInitialization::Item::writeSetupUi(const QString &parent, Item::Emp
m_setupUiStream << language::closeQtConfig(it.key());
++it;
}
- for (Item *child : qAsConst(m_children))
+ for (Item *child : std::as_const(m_children))
child->writeSetupUi(uniqueName);
return uniqueName;
}
@@ -2751,7 +2849,7 @@ void WriteInitialization::Item::writeRetranslateUi(const QString &parentPath)
if (m_retranslateUiData.policy == ItemData::GenerateWithMultiDirective)
generateMultiDirectiveBegin(m_retranslateUiStream, m_retranslateUiData.directives);
- const QString uniqueName = m_driver->unique(QLatin1String("___") + m_itemClassName.toLower());
+ const QString uniqueName = m_driver->unique("___"_L1 + m_itemClassName.toLower());
m_retranslateUiStream << m_indent;
if (language::language() == Language::Cpp)
m_retranslateUiStream << m_itemClassName << " *";
diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h
index 41eca9f0cb..0973def52d 100644
--- a/src/tools/uic/cpp/cppwriteinitialization.h
+++ b/src/tools/uic/cpp/cppwriteinitialization.h
@@ -1,36 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CPPWRITEINITIALIZATION_H
#define CPPWRITEINITIALIZATION_H
#include "treewalker.h"
-#include <qpair.h>
#include <qhash.h>
#include <qset.h>
#include <qmap.h>
@@ -144,7 +118,7 @@ private:
QString iconCall(const DomProperty *prop);
QString pixCall(const DomProperty *prop) const;
- QString pixCall(const QString &type, const QString &text) const;
+ QString pixCall(QLatin1StringView type, const QString &text) const;
QString trCall(const QString &str, const QString &comment = QString(), const QString &id = QString()) const;
QString trCall(DomString *str, const QString &defaultString = QString()) const;
QString noTrCall(DomString *str, const QString &defaultString = QString()) const;
@@ -235,6 +209,8 @@ private:
private:
QString writeFontProperties(const DomFont *f);
QString writeIconProperties(const DomResourceIcon *i);
+ void writeThemeIconCheckAssignment(const QString &themeValue, const QString &iconName,
+ const DomResourceIcon *i);
void writePixmapFunctionIcon(QTextStream &output, const QString &iconName,
const QString &indent, const DomResourceIcon *i) const;
QString writeSizePolicy(const DomSizePolicy *sp);
diff --git a/src/tools/uic/customwidgetsinfo.cpp b/src/tools/uic/customwidgetsinfo.cpp
index c838feaf73..6ec418634c 100644
--- a/src/tools/uic/customwidgetsinfo.cpp
+++ b/src/tools/uic/customwidgetsinfo.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "customwidgetsinfo.h"
#include "driver.h"
@@ -35,6 +10,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
CustomWidgetsInfo::CustomWidgetsInfo() = default;
void CustomWidgetsInfo::acceptUI(DomUI *node)
@@ -58,7 +35,7 @@ void CustomWidgetsInfo::acceptCustomWidget(DomCustomWidget *node)
m_customWidgets.insert(node->elementClass(), node);
}
-bool CustomWidgetsInfo::extends(const QString &classNameIn, QLatin1String baseClassName) const
+bool CustomWidgetsInfo::extends(const QString &classNameIn, QAnyStringView baseClassName) const
{
if (classNameIn == baseClassName)
return true;
@@ -101,10 +78,95 @@ bool CustomWidgetsInfo::isCustomWidgetContainer(const QString &className) const
return false;
}
+// FIXME in 7.0 - QTBUG-124241
+// Remove isAmbiguous logic when widget slots have been disambiguated.
+bool CustomWidgetsInfo::isAmbiguous(const QString &className, const QString &signature,
+ QMetaMethod::MethodType type) const
+{
+ using TypeMap = QHash<QString, QMetaMethod::MethodType>;
+ struct AmbiguousInClass {
+ QLatin1StringView className;
+ TypeMap methodMap;
+ };
+
+ static const QList<AmbiguousInClass> ambiguousList = {
+
+ {"QAction"_L1, {{"triggered"_L1, QMetaMethod::Signal}}},
+ {"QCommandLinkButton"_L1, {{"triggered"_L1, QMetaMethod::Signal},
+ {"clicked"_L1, QMetaMethod::Signal}}},
+ {"QPushButton"_L1, {{"triggered"_L1, QMetaMethod::Signal},
+ {"clicked"_L1, QMetaMethod::Signal}}},
+ {"QCheckBox"_L1, {{"triggered"_L1, QMetaMethod::Signal},
+ {"clicked"_L1, QMetaMethod::Signal}}},
+ {"QRadioButton"_L1, {{"triggered"_L1, QMetaMethod::Signal},
+ {"clicked"_L1, QMetaMethod::Signal}}},
+ {"QToolButton"_L1, {{"triggered"_L1, QMetaMethod::Signal},
+ {"clicked"_L1, QMetaMethod::Signal}}},
+ {"QLabel"_L1, {{"setNum"_L1, QMetaMethod::Slot}}},
+ {"QGraphicsView"_L1, {{"invalidateScene"_L1, QMetaMethod::Slot}}},
+ {"QListView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}},
+ {"QColumnView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}},
+ {"QListWidget"_L1, {{"dataChanged"_L1, QMetaMethod::Slot},
+ {"scrollToItem"_L1, QMetaMethod::Slot}}},
+ {"QTableView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}},
+ {"QTableWidget"_L1, {{"dataChanged"_L1, QMetaMethod::Slot},
+ {"scrollToItem"_L1, QMetaMethod::Slot}}},
+ {"QTreeView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot},
+ {"verticalScrollbarValueChanged"_L1, QMetaMethod::Slot},
+ {"expandRecursively"_L1, QMetaMethod::Slot}}},
+ {"QTreeWidget"_L1, {{"dataChanged"_L1, QMetaMethod::Slot},
+ {"verticalScrollbarValueChanged"_L1, QMetaMethod::Slot}
+ ,{"expandRecursively"_L1, QMetaMethod::Slot}
+ ,{"scrollToItem"_L1, QMetaMethod::Slot}}},
+ {"QUndoView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}},
+ {"QLCDNumber"_L1, {{"display"_L1, QMetaMethod::Slot}}},
+ {"QMenuBar"_L1, {{"setVisible"_L1, QMetaMethod::Slot}}},
+ {"QTextBrowser"_L1, {{"setSource"_L1, QMetaMethod::Slot}}},
+
+ /*
+ The following widgets with ambiguities are not used in the widget designer:
+
+ {"QSplashScreen"_L1, {{"showMessage"_L1, QMetaMethod::Slot}}},
+ {"QCompleter"_L1, {{"activated"_L1, QMetaMethod::Signal},
+ {"highlighted"_L1, QMetaMethod::Signal}}},
+ {"QSystemTrayIcon"_L1, {{"showMessage"_L1, QMetaMethod::Slot}}},
+ {"QStyledItemDelegate"_L1, {{"closeEditor"_L1, QMetaMethod::Signal}}},
+ {"QErrorMessage"_L1, {{"showMessage"_L1, QMetaMethod::Slot}}},
+ {"QGraphicsDropShadowEffect"_L1, {{"setOffset"_L1, QMetaMethod::Slot}}},
+ {"QGraphicsScene"_L1, {{"invalidate"_L1, QMetaMethod::Slot}}},
+ {"QItemDelegate"_L1, {{"closeEditor"_L1, QMetaMethod::Signal}}}
+ */
+ };
+
+ for (auto it = ambiguousList.constBegin(); it != ambiguousList.constEnd(); ++it) {
+ if (extends(className, it->className)) {
+ const qsizetype pos = signature.indexOf(u'(');
+ const QString method = signature.left(pos);
+ const auto methodIterator = it->methodMap.find(method);
+ return methodIterator != it->methodMap.constEnd() && type == methodIterator.value();
+ }
+ }
+ return false;
+}
+
+// Is it ambiguous, resulting in different signals for Python
+// "QAbstractButton::clicked(checked=false)"
+bool CustomWidgetsInfo::isAmbiguousSignal(const QString &className,
+ const QString &signalSignature) const
+{
+ return isAmbiguous(className, signalSignature, QMetaMethod::Signal);
+}
+
+bool CustomWidgetsInfo::isAmbiguousSlot(const QString &className,
+ const QString &signalSignature) const
+{
+ return isAmbiguous(className, signalSignature, QMetaMethod::Slot);
+}
+
QString CustomWidgetsInfo::realClassName(const QString &className) const
{
- if (className == QLatin1String("Line"))
- return QLatin1String("QFrame");
+ if (className == "Line"_L1)
+ return u"QFrame"_s;
return className;
}
@@ -119,19 +181,19 @@ QString CustomWidgetsInfo::customWidgetAddPageMethod(const QString &name) const
// add page methods for simple containers taking only the widget parameter
QString CustomWidgetsInfo::simpleContainerAddPageMethod(const QString &name) const
{
- using AddPageMethod = std::pair<const char *, const char *>;
-
- static AddPageMethod addPageMethods[] = {
- {"QStackedWidget", "addWidget"},
- {"QToolBar", "addWidget"},
- {"QDockWidget", "setWidget"},
- {"QScrollArea", "setWidget"},
- {"QSplitter", "addWidget"},
- {"QMdiArea", "addSubWindow"}
+ using AddPageMethod = std::pair<QString, QString>;
+
+ static const AddPageMethod addPageMethods[] = {
+ {u"QStackedWidget"_s, u"addWidget"_s},
+ {u"QToolBar"_s, u"addWidget"_s},
+ {u"QDockWidget"_s, u"setWidget"_s},
+ {u"QScrollArea"_s, u"setWidget"_s},
+ {u"QSplitter"_s, u"addWidget"_s},
+ {u"QMdiArea"_s, u"addSubWindow"_s}
};
for (const auto &m : addPageMethods) {
- if (extends(name, QLatin1String(m.first)))
- return QLatin1String(m.second);
+ if (extends(name, m.first))
+ return m.second;
}
return QString();
}
diff --git a/src/tools/uic/customwidgetsinfo.h b/src/tools/uic/customwidgetsinfo.h
index a1b24ab042..f336292f2a 100644
--- a/src/tools/uic/customwidgetsinfo.h
+++ b/src/tools/uic/customwidgetsinfo.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CUSTOMWIDGETSINFO_H
#define CUSTOMWIDGETSINFO_H
@@ -32,6 +7,7 @@
#include "treewalker.h"
#include <qstringlist.h>
#include <qmap.h>
+#include <QtCore/qmetaobject.h>
QT_BEGIN_NAMESPACE
@@ -56,14 +32,21 @@ public:
QString realClassName(const QString &className) const;
- bool extends(const QString &className, QLatin1String baseClassName) const;
+ bool extends(const QString &className, QAnyStringView baseClassName) const;
bool extendsOneOf(const QString &className, const QStringList &baseClassNames) const;
bool isCustomWidgetContainer(const QString &className) const;
+ bool isAmbiguousSignal(const QString &className,
+ const QString &signalSignature) const;
+ bool isAmbiguousSlot(const QString &className,
+ const QString &slotSignature) const;
+
private:
using NameCustomWidgetMap = QMap<QString, DomCustomWidget*>;
NameCustomWidgetMap m_customWidgets;
+ bool isAmbiguous(const QString &className, const QString &signature,
+ QMetaMethod::MethodType type) const;
};
QT_END_NAMESPACE
diff --git a/src/tools/uic/databaseinfo.cpp b/src/tools/uic/databaseinfo.cpp
index 9b0d1614ab..5334f06baf 100644
--- a/src/tools/uic/databaseinfo.cpp
+++ b/src/tools/uic/databaseinfo.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "databaseinfo.h"
#include "driver.h"
@@ -33,6 +8,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
DatabaseInfo::DatabaseInfo() = default;
void DatabaseInfo::acceptUI(DomUI *node)
@@ -50,11 +27,11 @@ void DatabaseInfo::acceptWidget(DomWidget *node)
{
QHash<QString, DomProperty*> properties = propertyMap(node->elementProperty());
- DomProperty *frameworkCode = properties.value(QLatin1String("frameworkCode"));
+ DomProperty *frameworkCode = properties.value("frameworkCode"_L1);
if (frameworkCode && toBool(frameworkCode->elementBool()) == false)
return;
- DomProperty *db = properties.value(QLatin1String("database"));
+ DomProperty *db = properties.value("database"_L1);
if (db && db->elementStringList()) {
QStringList info = db->elementStringList()->elementString();
if (info.isEmpty() || info.constFirst().isEmpty())
diff --git a/src/tools/uic/databaseinfo.h b/src/tools/uic/databaseinfo.h
index 4015e39dbc..0d2487a56f 100644
--- a/src/tools/uic/databaseinfo.h
+++ b/src/tools/uic/databaseinfo.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DATABASEINFO_H
#define DATABASEINFO_H
diff --git a/src/tools/uic/driver.cpp b/src/tools/uic/driver.cpp
index 8b9b4806e6..110764ee07 100644
--- a/src/tools/uic/driver.cpp
+++ b/src/tools/uic/driver.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "driver.h"
#include "uic.h"
@@ -39,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Driver::Driver()
: m_stdout(stdout, QFile::WriteOnly | QFile::Text)
{
@@ -152,7 +129,7 @@ QString Driver::normalizedName(const QString &name)
QString result = name;
std::replace_if(result.begin(), result.end(),
[] (QChar c) { return !c.isLetterOrNumber(); },
- QLatin1Char('_'));
+ u'_');
return result;
}
@@ -172,7 +149,7 @@ QString Driver::unique(const QString &instanceName, const QString &className)
} else if (!className.isEmpty()) {
name = unique(qtify(className));
} else {
- name = unique(QLatin1String("var"));
+ name = unique("var"_L1);
}
if (alreadyUsed && !className.isEmpty()) {
@@ -190,7 +167,7 @@ QString Driver::qtify(const QString &name)
{
QString qname = name;
- if (qname.at(0) == QLatin1Char('Q') || qname.at(0) == QLatin1Char('K'))
+ if (qname.at(0) == u'Q' || qname.at(0) == u'K')
qname.remove(0, 1);
for (int i = 0, size = qname.size(); i < size && qname.at(i).isUpper(); ++i)
@@ -201,8 +178,7 @@ QString Driver::qtify(const QString &name)
static bool isAnsiCCharacter(QChar c)
{
- return (c.toUpper() >= QLatin1Char('A') && c.toUpper() <= QLatin1Char('Z'))
- || c.isDigit() || c == QLatin1Char('_');
+ return (c.toUpper() >= u'A' && c.toUpper() <= u'Z') || c.isDigit() || c == u'_';
}
QString Driver::headerFileName() const
@@ -210,7 +186,7 @@ QString Driver::headerFileName() const
QString name = m_option.outputFile;
if (name.isEmpty()) {
- name = QLatin1String("ui_"); // ### use ui_ as prefix.
+ name = "ui_"_L1; // ### use ui_ as prefix.
name.append(m_option.inputFile);
}
@@ -220,23 +196,23 @@ QString Driver::headerFileName() const
QString Driver::headerFileName(const QString &fileName)
{
if (fileName.isEmpty())
- return headerFileName(QLatin1String("noname"));
+ return headerFileName(u"noname"_s);
QFileInfo info(fileName);
QString baseName = info.baseName();
// Transform into a valid C++ identifier
if (!baseName.isEmpty() && baseName.at(0).isDigit())
- baseName.prepend(QLatin1Char('_'));
+ baseName.prepend(u'_');
for (int i = 0; i < baseName.size(); ++i) {
QChar c = baseName.at(i);
if (!isAnsiCCharacter(c)) {
// Replace character by its unicode value
QString hex = QString::number(c.unicode(), 16);
- baseName.replace(i, 1, QLatin1Char('_') + hex + QLatin1Char('_'));
+ baseName.replace(i, 1, u'_' + hex + u'_');
i += hex.size() + 1;
}
}
- return baseName.toUpper() + QLatin1String("_H");
+ return baseName.toUpper() + "_H"_L1;
}
bool Driver::printDependencies(const QString &fileName)
@@ -269,9 +245,10 @@ bool Driver::uic(const QString &fileName, DomUI *ui, QTextStream *out)
bool Driver::uic(const QString &fileName, QTextStream *out)
{
QFile f;
- if (fileName.isEmpty())
- f.open(stdin, QIODevice::ReadOnly);
- else {
+ if (fileName.isEmpty()) {
+ if (!f.open(stdin, QIODevice::ReadOnly))
+ return false;
+ } else {
f.setFileName(fileName);
if (!f.open(QIODevice::ReadOnly))
return false;
diff --git a/src/tools/uic/driver.h b/src/tools/uic/driver.h
index 45ec23b4aa..8c54d03d83 100644
--- a/src/tools/uic/driver.h
+++ b/src/tools/uic/driver.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DRIVER_H
#define DRIVER_H
diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp
index 4a5f0ea309..d46b788419 100644
--- a/src/tools/uic/main.cpp
+++ b/src/tools/uic/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "uic.h"
#include "option.h"
@@ -38,15 +13,49 @@
#include <qcoreapplication.h>
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
+#include <qfileinfo.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+static const char pythonPathVar[] = "PYTHONPATH";
+
+// From the Python paths, find the component the UI file is under
+static QString pythonRoot(const QString &pythonPath, const QString &uiFileIn)
+{
+#ifdef Q_OS_WIN
+ static const Qt::CaseSensitivity fsSensitivity = Qt::CaseInsensitive;
+#else
+ static const Qt::CaseSensitivity fsSensitivity = Qt::CaseSensitive;
+#endif
+
+ if (pythonPath.isEmpty() || uiFileIn.isEmpty())
+ return {};
+ const QString uiFile = QFileInfo(uiFileIn).canonicalFilePath();
+ if (uiFile.isEmpty())
+ return {};
+ const auto uiFileSize = uiFile.size();
+ const auto paths = pythonPath.split(QDir::listSeparator(), Qt::SkipEmptyParts);
+ for (const auto &path : paths) {
+ const QString canonicalPath = QFileInfo(path).canonicalFilePath();
+ const auto canonicalPathSize = canonicalPath.size();
+ if (uiFileSize > canonicalPathSize
+ && uiFile.at(canonicalPathSize) == u'/'
+ && uiFile.startsWith(canonicalPath, fsSensitivity)) {
+ return canonicalPath;
+ }
+ }
+ return {};
+}
+
int runUic(int argc, char *argv[])
{
- qSetGlobalQHashSeed(0); // set the hash seed to 0
+ QHashSeed::setDeterministicGlobalSeed();
QCoreApplication app(argc, argv);
- QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_VERSION_STR));
+ const QString version = QString::fromLatin1(qVersion());
+ QCoreApplication::setApplicationVersion(version);
Driver driver;
@@ -54,70 +63,89 @@ int runUic(int argc, char *argv[])
// If you use this code as an example for a translated app, make sure to translate the strings.
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
- parser.setApplicationDescription(QStringLiteral("Qt User Interface Compiler version %1").arg(QString::fromLatin1(QT_VERSION_STR)));
+ parser.setApplicationDescription(u"Qt User Interface Compiler version %1"_s.arg(version));
parser.addHelpOption();
parser.addVersionOption();
- QCommandLineOption dependenciesOption(QStringList() << QStringLiteral("d") << QStringLiteral("dependencies"));
- dependenciesOption.setDescription(QStringLiteral("Display the dependencies."));
+ QCommandLineOption dependenciesOption(QStringList{u"d"_s, u"dependencies"_s});
+ dependenciesOption.setDescription(u"Display the dependencies."_s);
parser.addOption(dependenciesOption);
- QCommandLineOption outputOption(QStringList() << QStringLiteral("o") << QStringLiteral("output"));
- outputOption.setDescription(QStringLiteral("Place the output into <file>"));
- outputOption.setValueName(QStringLiteral("file"));
+ QCommandLineOption outputOption(QStringList{u"o"_s, u"output"_s});
+ outputOption.setDescription(u"Place the output into <file>"_s);
+ outputOption.setValueName(u"file"_s);
parser.addOption(outputOption);
- QCommandLineOption noAutoConnectionOption(QStringList() << QStringLiteral("a") << QStringLiteral("no-autoconnection"));
- noAutoConnectionOption.setDescription(QStringLiteral("Do not generate a call to QObject::connectSlotsByName()."));
+ QCommandLineOption noAutoConnectionOption(QStringList{u"a"_s, u"no-autoconnection"_s});
+ noAutoConnectionOption.setDescription(u"Do not generate a call to QObject::connectSlotsByName()."_s);
parser.addOption(noAutoConnectionOption);
- QCommandLineOption noProtOption(QStringList() << QStringLiteral("p") << QStringLiteral("no-protection"));
- noProtOption.setDescription(QStringLiteral("Disable header protection."));
+ QCommandLineOption noProtOption(QStringList{u"p"_s, u"no-protection"_s});
+ noProtOption.setDescription(u"Disable header protection."_s);
parser.addOption(noProtOption);
- QCommandLineOption noImplicitIncludesOption(QStringList() << QStringLiteral("n") << QStringLiteral("no-implicit-includes"));
- noImplicitIncludesOption.setDescription(QStringLiteral("Disable generation of #include-directives."));
+ QCommandLineOption noImplicitIncludesOption(QStringList{u"n"_s, u"no-implicit-includes"_s});
+ noImplicitIncludesOption.setDescription(u"Disable generation of #include-directives."_s);
parser.addOption(noImplicitIncludesOption);
- QCommandLineOption postfixOption(QStringLiteral("postfix"));
- postfixOption.setDescription(QStringLiteral("Postfix to add to all generated classnames."));
- postfixOption.setValueName(QStringLiteral("postfix"));
+ QCommandLineOption postfixOption(u"postfix"_s);
+ postfixOption.setDescription(u"Postfix to add to all generated classnames."_s);
+ postfixOption.setValueName(u"postfix"_s);
parser.addOption(postfixOption);
- QCommandLineOption translateOption(QStringList() << QStringLiteral("tr") << QStringLiteral("translate"));
- translateOption.setDescription(QStringLiteral("Use <function> for i18n."));
- translateOption.setValueName(QStringLiteral("function"));
+ QCommandLineOption noQtNamespaceOption(u"no-qt-namespace"_s);
+ noQtNamespaceOption.setDescription(
+ u"Disable wrapping the definition of the generated class in QT_{BEGIN,END}_NAMESPACE."_s);
+ parser.addOption(noQtNamespaceOption);
+
+ QCommandLineOption translateOption(QStringList{u"tr"_s, u"translate"_s});
+ translateOption.setDescription(u"Use <function> for i18n."_s);
+ translateOption.setValueName(u"function"_s);
parser.addOption(translateOption);
- QCommandLineOption includeOption(QStringList() << QStringLiteral("include"));
- includeOption.setDescription(QStringLiteral("Add #include <include-file> to <file>."));
- includeOption.setValueName(QStringLiteral("include-file"));
+ QCommandLineOption includeOption(QStringList{u"include"_s});
+ includeOption.setDescription(u"Add #include <include-file> to <file>."_s);
+ includeOption.setValueName(u"include-file"_s);
parser.addOption(includeOption);
- QCommandLineOption generatorOption(QStringList() << QStringLiteral("g") << QStringLiteral("generator"));
- generatorOption.setDescription(QStringLiteral("Select generator."));
- generatorOption.setValueName(QStringLiteral("python|cpp"));
+ QCommandLineOption generatorOption(QStringList{u"g"_s, u"generator"_s});
+ generatorOption.setDescription(u"Select generator."_s);
+ generatorOption.setValueName(u"python|cpp"_s);
parser.addOption(generatorOption);
- QCommandLineOption connectionsOption(QStringList{QStringLiteral("c"), QStringLiteral("connections")});
- connectionsOption.setDescription(QStringLiteral("Connection syntax."));
- connectionsOption.setValueName(QStringLiteral("pmf|string"));
+ QCommandLineOption connectionsOption(QStringList{u"c"_s, u"connections"_s});
+ connectionsOption.setDescription(u"Connection syntax."_s);
+ connectionsOption.setValueName(u"pmf|string"_s);
parser.addOption(connectionsOption);
- QCommandLineOption idBasedOption(QStringLiteral("idbased"));
- idBasedOption.setDescription(QStringLiteral("Use id based function for i18n"));
+ QCommandLineOption idBasedOption(u"idbased"_s);
+ idBasedOption.setDescription(u"Use id based function for i18n"_s);
parser.addOption(idBasedOption);
- QCommandLineOption fromImportsOption(QStringLiteral("from-imports"));
- fromImportsOption.setDescription(QStringLiteral("Python: generate imports relative to '.'"));
+ QCommandLineOption fromImportsOption(u"from-imports"_s);
+ fromImportsOption.setDescription(u"Python: generate imports relative to '.'"_s);
parser.addOption(fromImportsOption);
+ QCommandLineOption absoluteImportsOption(u"absolute-imports"_s);
+ absoluteImportsOption.setDescription(u"Python: generate absolute imports"_s);
+ parser.addOption(absoluteImportsOption);
+
+ // FIXME Qt 7: Flip the default?
+ QCommandLineOption rcPrefixOption(u"rc-prefix"_s);
+ rcPrefixOption.setDescription(uR"(Python: Generate "rc_file" instead of "file_rc" import)"_s);
+ parser.addOption(rcPrefixOption);
+
// FIXME Qt 7: Remove?
- QCommandLineOption useStarImportsOption(QStringLiteral("star-imports"));
- useStarImportsOption.setDescription(QStringLiteral("Python: Use * imports"));
+ QCommandLineOption useStarImportsOption(u"star-imports"_s);
+ useStarImportsOption.setDescription(u"Python: Use * imports"_s);
parser.addOption(useStarImportsOption);
- parser.addPositionalArgument(QStringLiteral("[uifile]"), QStringLiteral("Input file (*.ui), otherwise stdin."));
+ QCommandLineOption pythonPathOption(u"python-paths"_s);
+ pythonPathOption.setDescription(u"Python paths for --absolute-imports."_s);
+ pythonPathOption.setValueName(u"pathlist"_s);
+ parser.addOption(pythonPathOption);
+
+ parser.addPositionalArgument(u"[uifile]"_s, u"Input file (*.ui), otherwise stdin."_s);
parser.process(app);
@@ -126,31 +154,44 @@ int runUic(int argc, char *argv[])
driver.option().autoConnection = !parser.isSet(noAutoConnectionOption);
driver.option().headerProtection = !parser.isSet(noProtOption);
driver.option().implicitIncludes = !parser.isSet(noImplicitIncludesOption);
+ driver.option().qtNamespace = !parser.isSet(noQtNamespaceOption);
driver.option().idBased = parser.isSet(idBasedOption);
- driver.option().fromImports = parser.isSet(fromImportsOption);
- driver.option().useStarImports = parser.isSet(useStarImportsOption);
driver.option().postfix = parser.value(postfixOption);
driver.option().translateFunction = parser.value(translateOption);
driver.option().includeFile = parser.value(includeOption);
if (parser.isSet(connectionsOption)) {
const auto value = parser.value(connectionsOption);
- if (value == QLatin1String("pmf"))
+ if (value == "pmf"_L1)
driver.option().forceMemberFnPtrConnectionSyntax = 1;
- else if (value == QLatin1String("string"))
+ else if (value == "string"_L1)
driver.option().forceStringConnectionSyntax = 1;
}
+ const QString inputFile = parser.positionalArguments().value(0);
+
Language language = Language::Cpp;
if (parser.isSet(generatorOption)) {
- if (parser.value(generatorOption).compare(QLatin1String("python")) == 0)
+ if (parser.value(generatorOption).compare("python"_L1) == 0)
language = Language::Python;
}
language::setLanguage(language);
+ if (language == Language::Python) {
+ if (parser.isSet(fromImportsOption))
+ driver.option().pythonResourceImport = Option::PythonResourceImport::FromDot;
+ else if (parser.isSet(absoluteImportsOption))
+ driver.option().pythonResourceImport = Option::PythonResourceImport::Absolute;
+ driver.option().useStarImports = parser.isSet(useStarImportsOption);
+ if (parser.isSet(rcPrefixOption))
+ driver.option().rcPrefix = 1;
+ QString pythonPaths;
+ if (parser.isSet(pythonPathOption))
+ pythonPaths = parser.value(pythonPathOption);
+ else if (qEnvironmentVariableIsSet(pythonPathVar))
+ pythonPaths = QString::fromUtf8(qgetenv(pythonPathVar));
+ driver.option().pythonRoot = pythonRoot(pythonPaths, inputFile);
+ }
- QString inputFile;
- if (!parser.positionalArguments().isEmpty())
- inputFile = parser.positionalArguments().at(0);
- else // reading from stdin
+ if (inputFile.isEmpty()) // reading from stdin
driver.option().headerProtection = false;
if (driver.option().dependencies) {
diff --git a/src/tools/uic/option.h b/src/tools/uic/option.h
index b08aab2d3e..cfdd90fda3 100644
--- a/src/tools/uic/option.h
+++ b/src/tools/uic/option.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef OPTION_H
#define OPTION_H
@@ -36,6 +11,12 @@ QT_BEGIN_NAMESPACE
struct Option
{
+ enum class PythonResourceImport {
+ Default, // "import rc_file"
+ FromDot, // "from . import rc_file"
+ Absolute // "import path.rc_file"
+ };
+
unsigned int headerProtection : 1;
unsigned int copyrightHeader : 1;
unsigned int generateImplemetation : 1;
@@ -45,10 +26,11 @@ struct Option
unsigned int limitXPM_LineLength : 1;
unsigned int implicitIncludes: 1;
unsigned int idBased: 1;
- unsigned int fromImports: 1;
unsigned int forceMemberFnPtrConnectionSyntax: 1;
unsigned int forceStringConnectionSyntax: 1;
unsigned int useStarImports: 1;
+ unsigned int rcPrefix: 1; // Python: Generate "rc_file" instead of "file_rc" import
+ unsigned int qtNamespace: 1;
QString inputFile;
QString outputFile;
@@ -58,6 +40,9 @@ struct Option
QString postfix;
QString translateFunction;
QString includeFile;
+ QString pythonRoot;
+
+ PythonResourceImport pythonResourceImport = PythonResourceImport::Default;
Option()
: headerProtection(1),
@@ -69,17 +54,18 @@ struct Option
limitXPM_LineLength(0),
implicitIncludes(1),
idBased(0),
- fromImports(0),
forceMemberFnPtrConnectionSyntax(0),
forceStringConnectionSyntax(0),
useStarImports(0),
- prefix(QLatin1String("Ui_"))
- { indent.fill(QLatin1Char(' '), 4); }
+ rcPrefix(0),
+ qtNamespace(1),
+ prefix(QLatin1StringView("Ui_"))
+ { indent.fill(u' ', 4); }
QString messagePrefix() const
{
return inputFile.isEmpty() ?
- QString(QLatin1String("stdin")) :
+ QString(QLatin1StringView("stdin")) :
QDir::toNativeSeparators(inputFile);
}
};
diff --git a/src/tools/uic/python/pythonwritedeclaration.cpp b/src/tools/uic/python/pythonwritedeclaration.cpp
index 5122b18e23..665eb8fe76 100644
--- a/src/tools/uic/python/pythonwritedeclaration.cpp
+++ b/src/tools/uic/python/pythonwritedeclaration.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "pythonwritedeclaration.h"
#include <cppwriteinitialization.h>
@@ -38,6 +13,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace Python {
WriteDeclaration::WriteDeclaration(Uic *uic) :
@@ -51,7 +28,7 @@ WriteDeclaration::WriteDeclaration(Uic *uic) :
void WriteDeclaration::acceptUI(DomUI *node)
{
// remove any left-over C++ namespaces
- const QString qualifiedClassName = QLatin1String("Ui_") + node->elementClass()
+ const QString qualifiedClassName = "Ui_"_L1 + node->elementClass()
+ m_option.postfix;
m_output << "class " << language::fixClassName(qualifiedClassName) << "(object):\n";
diff --git a/src/tools/uic/python/pythonwritedeclaration.h b/src/tools/uic/python/pythonwritedeclaration.h
index a8d50b6fbf..6aed86f382 100644
--- a/src/tools/uic/python/pythonwritedeclaration.h
+++ b/src/tools/uic/python/pythonwritedeclaration.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PYTHONWRITEDECLARATION_H
#define PYTHONWRITEDECLARATION_H
diff --git a/src/tools/uic/python/pythonwriteimports.cpp b/src/tools/uic/python/pythonwriteimports.cpp
index f9dfeb28da..74eeab8387 100644
--- a/src/tools/uic/python/pythonwriteimports.cpp
+++ b/src/tools/uic/python/pythonwriteimports.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "pythonwriteimports.h"
@@ -35,12 +10,16 @@
#include <ui4.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
#include <QtCore/qtextstream.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// Generate imports for Python. Note some things differ from C++:
// - qItemView->header()->setFoo() does not require QHeaderView to be imported
// - qLabel->setFrameShape(QFrame::Box) however requires QFrame to be imported
@@ -70,26 +49,13 @@ static WriteImports::ClassesPerModule defaultClasses()
QStringLiteral("QPainter"), QStringLiteral("QPixmap"),
QStringLiteral("QTransform"), QStringLiteral("QRadialGradient")}
},
+ // Add QWidget for QWidget.setTabOrder()
{QStringLiteral("QtWidgets"),
- {QStringLiteral("QSizePolicy")}
+ {QStringLiteral("QSizePolicy"), QStringLiteral("QWidget")}
}
};
}
-// Change the name of a qrc file "dir/foo.qrc" file to the Python
-// module name "foo_rc" according to project conventions.
-static QString pythonResource(QString resource)
-{
- const int lastSlash = resource.lastIndexOf(QLatin1Char('/'));
- if (lastSlash != -1)
- resource.remove(0, lastSlash + 1);
- if (resource.endsWith(QLatin1String(".qrc"))) {
- resource.chop(4);
- resource.append(QLatin1String("_rc"));
- }
- return resource;
-}
-
// Helpers for WriteImports::ClassesPerModule maps
static void insertClass(const QString &module, const QString &className,
WriteImports::ClassesPerModule *c)
@@ -136,7 +102,7 @@ WriteImports::WriteImports(Uic *uic) : WriteIncludesBase(uic),
m_qtClasses(defaultClasses())
{
for (const auto &e : classInfoEntries())
- m_classToModule.insert(QLatin1String(e.klass), QLatin1String(e.module));
+ m_classToModule.insert(QLatin1StringView(e.klass), QLatin1StringView(e.module));
}
void WriteImports::acceptUI(DomUI *node)
@@ -162,27 +128,67 @@ void WriteImports::acceptUI(DomUI *node)
const auto includes = resources->elementInclude();
for (auto include : includes) {
if (include->hasAttributeLocation())
- writeImport(pythonResource(include->attributeLocation()));
+ writeResourceImport(include->attributeLocation());
}
output << '\n';
}
}
-void WriteImports::writeImport(const QString &module)
+QString WriteImports::resourceAbsolutePath(QString resource) const
+{
+ // If we know the project root, generate an absolute Python import
+ // to the resource. options. pythonRoot is the Python path component
+ // under which the UI file is.
+ const auto &options = uic()->option();
+ if (!options.inputFile.isEmpty() && !options.pythonRoot.isEmpty()) {
+ resource = QDir::cleanPath(QFileInfo(options.inputFile).canonicalPath() + u'/' + resource);
+ if (resource.size() > options.pythonRoot.size())
+ resource.remove(0, options.pythonRoot.size() + 1);
+ }
+ // If nothing is known, we assume the directory pointed by "../" is the root
+ while (resource.startsWith(u"../"))
+ resource.remove(0, 3);
+ resource.replace(u'/', u'.');
+ return resource;
+}
+
+void WriteImports::writeResourceImport(const QString &module)
{
- if (uic()->option().fromImports)
- uic()->output() << "from . ";
- uic()->output() << "import " << module << '\n';
+ const auto &options = uic()->option();
+ auto &str = uic()->output();
+
+ QString resource = QDir::cleanPath(module);
+ if (resource.endsWith(u".qrc"))
+ resource.chop(4);
+ const qsizetype basePos = resource.lastIndexOf(u'/') + 1;
+ // Change the name of a qrc file "dir/foo.qrc" file to the Python
+ // module name "foo_rc" according to project conventions.
+ if (options.rcPrefix)
+ resource.insert(basePos, u"rc_");
+ else
+ resource.append(u"_rc");
+
+ switch (options.pythonResourceImport) {
+ case Option::PythonResourceImport::Default:
+ str << "import " << QStringView{resource}.sliced(basePos) << '\n';
+ break;
+ case Option::PythonResourceImport::FromDot:
+ str << "from . import " << QStringView{resource}.sliced(basePos) << '\n';
+ break;
+ case Option::PythonResourceImport::Absolute:
+ str << "import " << resourceAbsolutePath(resource) << '\n';
+ break;
+ }
}
void WriteImports::doAdd(const QString &className, const DomCustomWidget *dcw)
{
const CustomWidgetsInfo *cwi = uic()->customWidgetsInfo();
- if (cwi->extends(className, QLatin1String("QListWidget")))
+ if (cwi->extends(className, "QListWidget"))
add(QStringLiteral("QListWidgetItem"));
- else if (cwi->extends(className, QLatin1String("QTreeWidget")))
+ else if (cwi->extends(className, "QTreeWidget"))
add(QStringLiteral("QTreeWidgetItem"));
- else if (cwi->extends(className, QLatin1String("QTableWidget")))
+ else if (cwi->extends(className, "QTableWidget"))
add(QStringLiteral("QTableWidgetItem"));
if (dcw != nullptr) {
@@ -209,7 +215,7 @@ bool WriteImports::addQtClass(const QString &className)
void WriteImports::addPythonCustomWidget(const QString &className, const DomCustomWidget *node)
{
- if (className.contains(QLatin1String("::")))
+ if (className.contains("::"_L1))
return; // Exclude namespaced names (just to make tests pass).
if (addQtClass(className)) // Qt custom widgets like QQuickWidget, QAxWidget, etc
@@ -222,26 +228,45 @@ void WriteImports::addPythonCustomWidget(const QString &className, const DomCust
} else { // When we do have elementHeader, we know it's a relative import.
QString modulePath = node->elementHeader()->text();
// Replace the '/' by '.'
- modulePath.replace(QLatin1Char('/'), QLatin1Char('.'));
- // '.h' is added by default on headers for <customwidget>
- if (modulePath.endsWith(QLatin1String(".h")))
+ modulePath.replace(u'/', u'.');
+ // '.h' is added by default on headers for <customwidget>.
+ if (modulePath.endsWith(".h"_L1, Qt::CaseInsensitive))
modulePath.chop(2);
+ else if (modulePath.endsWith(".hh"_L1))
+ modulePath.chop(3);
+ else if (modulePath.endsWith(".hpp"_L1))
+ modulePath.chop(4);
insertClass(modulePath, className, &m_customWidgets);
}
}
void WriteImports::acceptProperty(DomProperty *node)
{
- if (node->kind() == DomProperty::Enum) {
- // Add base classes like QFrame for QLabel::frameShape()
- const QString &enumV = node->elementEnum();
- const auto colonPos = enumV.indexOf(u"::");
- if (colonPos > 0)
- addQtClass(enumV.left(colonPos));
+ switch (node->kind()) {
+ case DomProperty::Enum:
+ addEnumBaseClass(node->elementEnum());
+ break;
+ case DomProperty::Set:
+ addEnumBaseClass(node->elementSet());
+ break;
+ default:
+ break;
}
+
WriteIncludesBase::acceptProperty(node);
}
+void WriteImports::addEnumBaseClass(const QString &v)
+{
+ // Add base classes like QFrame for QLabel::frameShape()
+ const auto colonPos = v.indexOf(u"::");
+ if (colonPos > 0) {
+ const QString base = v.left(colonPos);
+ if (base.startsWith(u'Q') && base != u"Qt")
+ addQtClass(base);
+ }
+}
+
} // namespace Python
QT_END_NAMESPACE
diff --git a/src/tools/uic/python/pythonwriteimports.h b/src/tools/uic/python/pythonwriteimports.h
index c833e216a6..4497b8dc33 100644
--- a/src/tools/uic/python/pythonwriteimports.h
+++ b/src/tools/uic/python/pythonwriteimports.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PYTHONWRITEIMPORTS_H
#define PYTHONWRITEIMPORTS_H
@@ -55,7 +30,9 @@ protected:
private:
void addPythonCustomWidget(const QString &className, const DomCustomWidget *dcw);
bool addQtClass(const QString &className);
- void writeImport(const QString &module);
+ void addEnumBaseClass(const QString &v);
+ void writeResourceImport(const QString &module);
+ QString resourceAbsolutePath(QString resource) const;
QHash<QString, QString> m_classToModule;
// Module->class (modules sorted)
diff --git a/src/tools/uic/qclass_lib_map.h b/src/tools/uic/qclass_lib_map.h
index 59b2205e2d..e9e4cfde12 100644
--- a/src/tools/uic/qclass_lib_map.h
+++ b/src/tools/uic/qclass_lib_map.h
@@ -219,6 +219,7 @@ QT_CLASS_LIB(QStack, QtCore, qstack.h)
QT_CLASS_LIB(QStdWString, QtCore, qstring.h)
QT_CLASS_LIB(QString, QtCore, qstring.h)
QT_CLASS_LIB(QLatin1String, QtCore, qstring.h)
+QT_CLASS_LIB(QLatin1StringView, QtCore, qstring.h)
QT_CLASS_LIB(QCharRef, QtCore, qstring.h)
QT_CLASS_LIB(QLatin1Literal, QtCore, qstring.h)
QT_CLASS_LIB(QAbstractConcatenable, QtCore, qstringbuilder.h)
@@ -836,6 +837,7 @@ QT_CLASS_LIB(QGraphicsSvgItem, QtSvgWidgets, qgraphicssvgitem.h)
QT_CLASS_LIB(QSvgWidget, QtSvgWidgets, qsvgwidget.h)
QT_CLASS_LIB(QSvgGenerator, QtSvg, qsvggenerator.h)
QT_CLASS_LIB(QSvgRenderer, QtSvg, qsvgrenderer.h)
+QT_CLASS_LIB(QPdfView, QtPdfWidgets, qpdfview.h)
QT_CLASS_LIB(QQuickWidget, QtQuickWidgets, qquickwidget.h)
QT_CLASS_LIB(QVideoWidget, QtMultimediaWidgets, qvideowidget.h)
QT_CLASS_LIB(QWebEngineView, QtWebEngineWidgets, qwebengineview.h)
diff --git a/src/tools/uic/shared/language.cpp b/src/tools/uic/shared/language.cpp
index 2f2ae3ebc6..d59688e346 100644
--- a/src/tools/uic/shared/language.cpp
+++ b/src/tools/uic/shared/language.cpp
@@ -1,37 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "language.h"
#include <QtCore/qtextstream.h>
+#include <QtCore/QList>
namespace language {
+using namespace Qt::StringLiterals;
+
static Encoding encoding = Encoding::Utf8;
static Language _language = Language::Cpp;
@@ -42,29 +20,29 @@ void setLanguage(Language l)
_language = l;
switch (_language) {
case Language::Cpp:
- derefPointer = QLatin1String("->");
+ derefPointer = u"->"_s;
listStart = '{';
listEnd = '}';
- nullPtr = QLatin1String("nullptr");
- operatorNew = QLatin1String("new ");
- qtQualifier = QLatin1String("Qt::");
- qualifier = QLatin1String("::");
- self = QLatin1String(""); // for testing: change to "this->";
- eol = QLatin1String(";\n");
- emptyString = QLatin1String("QString()");
+ nullPtr = u"nullptr"_s;
+ operatorNew = u"new "_s;
+ qtQualifier = u"Qt::"_s;
+ qualifier = u"::"_s;
+ self = u""_s; // for testing: change to "this->";
+ eol = u";\n"_s;
+ emptyString = u"QString()"_s;
encoding = Encoding::Utf8;
break;
case Language::Python:
- derefPointer = QLatin1String(".");
+ derefPointer = u"."_s;
listStart = '[';
listEnd = ']';
- nullPtr = QLatin1String("None");
- operatorNew = QLatin1String("");
- qtQualifier = QLatin1String("Qt.");
- qualifier = QLatin1String(".");
- self = QLatin1String("self.");
- eol = QLatin1String("\n");
- emptyString = QLatin1String("\"\"");
+ nullPtr = u"None"_s;
+ operatorNew = u""_s;
+ qtQualifier = u"Qt."_s;
+ qualifier = u"."_s;
+ self = u"self."_s;
+ eol = u"\n"_s;
+ emptyString = u"\"\""_s;
encoding = Encoding::Unicode;
break;
}
@@ -81,9 +59,9 @@ QString self;
QString eol;
QString emptyString;
-QString cppQualifier = QLatin1String("::");
-QString cppTrue = QLatin1String("true");
-QString cppFalse = QLatin1String("false");
+QString cppQualifier = u"::"_s;
+QString cppTrue = u"true"_s;
+QString cppFalse = u"false"_s;
QTextStream &operator<<(QTextStream &str, const qtConfig &c)
{
@@ -106,97 +84,97 @@ QTextStream &operator<<(QTextStream &str, const closeQtConfig &c)
struct EnumLookup
{
int value;
- const char *valueString;
+ QLatin1StringView valueString;
};
template <int N>
-const char *lookupEnum(const EnumLookup(&array)[N], int value, int defaultIndex = 0)
+QLatin1StringView lookupEnum(const EnumLookup(&array)[N], int value, int defaultIndex = 0)
{
for (int i = 0; i < N; ++i) {
if (value == array[i].value)
return array[i].valueString;
}
- const char *defaultValue = array[defaultIndex].valueString;
+ auto defaultValue = array[defaultIndex].valueString;
qWarning("uic: Warning: Invalid enumeration value %d, defaulting to %s",
- value, defaultValue);
+ value, defaultValue.data());
return defaultValue;
}
QString fixClassName(QString className)
{
if (language() == Language::Python)
- className.replace(cppQualifier, QLatin1String("_"));
+ className.replace(cppQualifier, "_"_L1);
return className;
}
-const char *toolbarArea(int v)
+QLatin1StringView toolbarArea(int v)
{
static const EnumLookup toolBarAreas[] =
{
- {0, "NoToolBarArea"},
- {0x1, "LeftToolBarArea"},
- {0x2, "RightToolBarArea"},
- {0x4, "TopToolBarArea"},
- {0x8, "BottomToolBarArea"},
- {0xf, "AllToolBarAreas"}
+ {0, "NoToolBarArea"_L1},
+ {0x1, "LeftToolBarArea"_L1},
+ {0x2, "RightToolBarArea"_L1},
+ {0x4, "TopToolBarArea"_L1},
+ {0x8, "BottomToolBarArea"_L1},
+ {0xf, "AllToolBarAreas"_L1}
};
return lookupEnum(toolBarAreas, v);
}
-const char *sizePolicy(int v)
+QLatin1StringView sizePolicy(int v)
{
static const EnumLookup sizePolicies[] =
{
- {0, "Fixed"},
- {0x1, "Minimum"},
- {0x4, "Maximum"},
- {0x5, "Preferred"},
- {0x3, "MinimumExpanding"},
- {0x7, "Expanding"},
- {0xD, "Ignored"}
+ {0, "Fixed"_L1},
+ {0x1, "Minimum"_L1},
+ {0x4, "Maximum"_L1},
+ {0x5, "Preferred"_L1},
+ {0x3, "MinimumExpanding"_L1},
+ {0x7, "Expanding"_L1},
+ {0xD, "Ignored"_L1}
};
return lookupEnum(sizePolicies, v, 3);
}
-const char *dockWidgetArea(int v)
+QLatin1StringView dockWidgetArea(int v)
{
static const EnumLookup dockWidgetAreas[] =
{
- {0, "NoDockWidgetArea"},
- {0x1, "LeftDockWidgetArea"},
- {0x2, "RightDockWidgetArea"},
- {0x4, "TopDockWidgetArea"},
- {0x8, "BottomDockWidgetArea"},
- {0xf, "AllDockWidgetAreas"}
+ {0, "NoDockWidgetArea"_L1},
+ {0x1, "LeftDockWidgetArea"_L1},
+ {0x2, "RightDockWidgetArea"_L1},
+ {0x4, "TopDockWidgetArea"_L1},
+ {0x8, "BottomDockWidgetArea"_L1},
+ {0xf, "AllDockWidgetAreas"_L1}
};
return lookupEnum(dockWidgetAreas, v);
}
-const char *paletteColorRole(int v)
+QLatin1StringView paletteColorRole(int v)
{
static const EnumLookup colorRoles[] =
{
- {0, "WindowText"},
- {1, "Button"},
- {2, "Light"},
- {3, "Midlight"},
- {4, "Dark"},
- {5, "Mid"},
- {6, "Text"},
- {7, "BrightText"},
- {8, "ButtonText"},
- {9, "Base"},
- {10, "Window"},
- {11, "Shadow"},
- {12, "Highlight"},
- {13, "HighlightedText"},
- {14, "Link"},
- {15, "LinkVisited"},
- {16, "AlternateBase"},
- {17, "NoRole"},
- {18, "ToolTipBase"},
- {19, "ToolTipText"},
- {20, "PlaceholderText"},
+ {0, "WindowText"_L1},
+ {1, "Button"_L1},
+ {2, "Light"_L1},
+ {3, "Midlight"_L1},
+ {4, "Dark"_L1},
+ {5, "Mid"_L1},
+ {6, "Text"_L1},
+ {7, "BrightText"_L1},
+ {8, "ButtonText"_L1},
+ {9, "Base"_L1},
+ {10, "Window"_L1},
+ {11, "Shadow"_L1},
+ {12, "Highlight"_L1},
+ {13, "HighlightedText"_L1},
+ {14, "Link"_L1},
+ {15, "LinkVisited"_L1},
+ {16, "AlternateBase"_L1},
+ {17, "NoRole"_L1},
+ {18, "ToolTipBase"_L1},
+ {19, "ToolTipText"_L1},
+ {20, "PlaceholderText"_L1},
};
return lookupEnum(colorRoles, v);
}
@@ -217,7 +195,7 @@ static int formatEscapedNumber(QTextStream &str, ushort value, int base, int wid
const auto oldFieldWidth = str.fieldWidth();
const auto oldFieldAlignment = str.fieldAlignment();
const auto oldIntegerBase = str.integerBase();
- str.setPadChar(QLatin1Char('0'));
+ str.setPadChar(u'0');
str.setFieldWidth(width);
str.setFieldAlignment(QTextStream::AlignRight);
str.setIntegerBase(base);
@@ -393,29 +371,65 @@ void _formatStackVariable(QTextStream &str, const char *className, QStringView v
}
}
-enum OverloadUse {
- UseOverload,
- UseOverloadWhenNoArguments, // Use overload only when the argument list is empty,
- // in this case there is no chance of connecting
- // mismatching T against const T &
- DontUseOverload
+enum class OverloadUse {
+ Always,
+ WhenAmbiguousOrEmpty, // Use overload if
+ // - signal/slot is ambiguous
+ // - argument list is empty (chance of connecting mismatching T against const T &)
+ Never,
};
// Format a member function for a signal slot connection
-static void formatMemberFnPtr(QTextStream &str, const SignalSlot &s,
- OverloadUse useQOverload = DontUseOverload)
+static bool isConstRef(const QStringView &arg)
{
- const int parenPos = s.signature.indexOf(QLatin1Char('('));
+ return arg.startsWith(u'Q') && arg != "QPoint"_L1 && arg != "QSize"_L1;
+}
+
+static QString formatOverload(const QStringView &parameters)
+{
+ QString result = "qOverload<"_L1;
+ const auto args = QStringView{parameters}.split(u',');
+ for (qsizetype i = 0, size = args.size(); i < size; ++i) {
+ const auto &arg = args.at(i);
+ if (i > 0)
+ result += u',';
+ const bool constRef = isConstRef(arg);
+ if (constRef)
+ result += "const "_L1;
+ result += arg;
+ if (constRef)
+ result += u'&';
+ }
+ result += u'>';
+ return result;
+}
+
+static void formatMemberFnPtr(QTextStream &str, const SignalSlot &s, OverloadUse useQOverload)
+{
+ const qsizetype parenPos = s.signature.indexOf(u'(');
Q_ASSERT(parenPos >= 0);
const auto functionName = QStringView{s.signature}.left(parenPos);
const auto parameters = QStringView{s.signature}.mid(parenPos + 1,
s.signature.size() - parenPos - 2);
- const bool withOverload = useQOverload == UseOverload ||
- (useQOverload == UseOverloadWhenNoArguments && parameters.isEmpty());
+
+ const bool isAmbiguous = s.options.testFlag(SignalSlotOption::Ambiguous);
+ bool withOverload = false; // just to silence the compiler
+
+ switch (useQOverload) {
+ case OverloadUse::Always:
+ withOverload = true;
+ break;
+ case OverloadUse::Never:
+ withOverload = false;
+ break;
+ case OverloadUse::WhenAmbiguousOrEmpty:
+ withOverload = parameters.empty() || isAmbiguous;
+ break;
+ }
if (withOverload)
- str << "qOverload<" << parameters << ">(";
+ str << formatOverload(parameters) << '(';
str << '&' << s.className << "::" << functionName;
@@ -428,9 +442,9 @@ static void formatMemberFnPtrConnection(QTextStream &str,
const SignalSlot &receiver)
{
str << "QObject::connect(" << sender.name << ", ";
- formatMemberFnPtr(str, sender);
+ formatMemberFnPtr(str, sender, OverloadUse::Never);
str << ", " << receiver.name << ", ";
- formatMemberFnPtr(str, receiver, UseOverloadWhenNoArguments);
+ formatMemberFnPtr(str, receiver, OverloadUse::WhenAmbiguousOrEmpty);
str << ')';
}
@@ -456,12 +470,22 @@ void formatConnection(QTextStream &str, const SignalSlot &sender, const SignalSl
break;
}
break;
- case Language::Python:
- str << sender.name << '.'
- << QStringView{sender.signature}.left(sender.signature.indexOf(QLatin1Char('(')))
- << ".connect(" << receiver.name << '.'
- << QStringView{receiver.signature}.left(receiver.signature.indexOf(QLatin1Char('(')))
+ case Language::Python: {
+ const auto paren = sender.signature.indexOf(u'(');
+ auto senderSignature = QStringView{sender.signature};
+ str << sender.name << '.' << senderSignature.left(paren);
+ // Signals like "QAbstractButton::clicked(checked=false)" require
+ // the parameter if it is used.
+ if (sender.options.testFlag(SignalSlotOption::Ambiguous)) {
+ const QStringView parameters =
+ senderSignature.mid(paren + 1, senderSignature.size() - paren - 2);
+ if (!parameters.isEmpty() && !parameters.contains(u','))
+ str << "[\"" << parameters << "\"]";
+ }
+ str << ".connect(" << receiver.name << '.'
+ << QStringView{receiver.signature}.left(receiver.signature.indexOf(u'('))
<< ')';
+ }
break;
}
}
diff --git a/src/tools/uic/shared/language.h b/src/tools/uic/shared/language.h
index 918f25e46a..de39122ee8 100644
--- a/src/tools/uic/shared/language.h
+++ b/src/tools/uic/shared/language.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef LANGUAGE_H
#define LANGUAGE_H
@@ -100,10 +75,10 @@ QTextStream &operator<<(QTextStream &, const closeQtConfig &c);
QString fixClassName(QString className);
-const char *toolbarArea(int v);
-const char *sizePolicy(int v);
-const char *dockWidgetArea(int v);
-const char *paletteColorRole(int v);
+QLatin1StringView toolbarArea(int v);
+QLatin1StringView sizePolicy(int v);
+QLatin1StringView dockWidgetArea(int v);
+QLatin1StringView paletteColorRole(int v);
enum class Encoding { Utf8, Unicode };
@@ -198,11 +173,19 @@ inline QTextStream &operator<<(QTextStream &str, const _stackVariable<withInitPa
using stackVariable = _stackVariable<false>;
using stackVariableWithInitParameters = _stackVariable<true>;
+enum class SignalSlotOption
+{
+ Ambiguous = 0x1
+};
+
+Q_DECLARE_FLAGS(SignalSlotOptions, SignalSlotOption)
+
struct SignalSlot
{
QString name;
QString signature;
QString className;
+ SignalSlotOptions options;
};
void formatConnection(QTextStream &str, const SignalSlot &sender, const SignalSlot &receiver,
diff --git a/src/tools/uic/shared/writeincludesbase.cpp b/src/tools/uic/shared/writeincludesbase.cpp
index 128200ed68..78f69d5391 100644
--- a/src/tools/uic/shared/writeincludesbase.cpp
+++ b/src/tools/uic/shared/writeincludesbase.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "writeincludesbase.h"
#include "ui4.h"
@@ -118,7 +93,7 @@ void WriteIncludesBase::add(const QString &className, const DomCustomWidget *dcw
if (cwi->extendsOneOf(className, treeViewsWithHeaders))
add(QStringLiteral("QHeaderView"));
- if (!m_laidOut && cwi->extends(className, QLatin1String("QToolBox")))
+ if (!m_laidOut && cwi->extends(className, "QToolBox"))
add(QStringLiteral("QLayout")); // spacing property of QToolBox)
if (className == QStringLiteral("Line")) { // ### hmm, deprecate me!
@@ -126,7 +101,7 @@ void WriteIncludesBase::add(const QString &className, const DomCustomWidget *dcw
return;
}
- if (cwi->extends(className, QLatin1String("QDialogButtonBox")))
+ if (cwi->extends(className, "QDialogButtonBox"))
add(QStringLiteral("QAbstractButton")); // for signal "clicked(QAbstractButton*)"
doAdd(className, dcw);
diff --git a/src/tools/uic/shared/writeincludesbase.h b/src/tools/uic/shared/writeincludesbase.h
index 15e57b8c36..71ddcc80d9 100644
--- a/src/tools/uic/shared/writeincludesbase.h
+++ b/src/tools/uic/shared/writeincludesbase.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef WRITEINCLUDES_BASE_H
#define WRITEINCLUDES_BASE_H
diff --git a/src/tools/uic/treewalker.cpp b/src/tools/uic/treewalker.cpp
index 4c9981580b..731017b1a2 100644
--- a/src/tools/uic/treewalker.cpp
+++ b/src/tools/uic/treewalker.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "treewalker.h"
#include "ui4.h"
diff --git a/src/tools/uic/treewalker.h b/src/tools/uic/treewalker.h
index abc5658924..ff5d032648 100644
--- a/src/tools/uic/treewalker.h
+++ b/src/tools/uic/treewalker.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TREEWALKER_H
#define TREEWALKER_H
diff --git a/src/tools/uic/ui4.cpp b/src/tools/uic/ui4.cpp
index a155df9b6c..d65fc4a8c3 100644
--- a/src/tools/uic/ui4.cpp
+++ b/src/tools/uic/ui4.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT!
@@ -32,6 +7,9 @@
QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
#ifdef QFORMINTERNAL_NAMESPACE
using namespace QFormInternal;
#endif
@@ -60,133 +38,133 @@ void DomUI::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("version")) {
+ if (name == u"version"_s) {
setAttributeVersion(attribute.value().toString());
continue;
}
- if (name == QLatin1String("language")) {
+ if (name == u"language"_s) {
setAttributeLanguage(attribute.value().toString());
continue;
}
- if (name == QLatin1String("displayname")) {
+ if (name == u"displayname"_s) {
setAttributeDisplayname(attribute.value().toString());
continue;
}
- if (name == QLatin1String("idbasedtr")) {
- setAttributeIdbasedtr(attribute.value() == QLatin1String("true"));
+ if (name == u"idbasedtr"_s) {
+ setAttributeIdbasedtr(attribute.value() == u"true"_s);
continue;
}
- if (name == QLatin1String("connectslotsbyname")) {
- setAttributeConnectslotsbyname(attribute.value() == QLatin1String("true"));
+ if (name == u"connectslotsbyname"_s) {
+ setAttributeConnectslotsbyname(attribute.value() == u"true"_s);
continue;
}
- if (name == QLatin1String("stdsetdef")) {
+ if (name == u"stdsetdef"_s) {
setAttributeStdsetdef(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("stdSetDef")) {
+ if (name == u"stdSetDef"_s) {
setAttributeStdSetDef(attribute.value().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("author"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"author"_s, Qt::CaseInsensitive)) {
setElementAuthor(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("comment"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"comment"_s, Qt::CaseInsensitive)) {
setElementComment(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("exportmacro"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"exportmacro"_s, Qt::CaseInsensitive)) {
setElementExportMacro(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("class"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"class"_s, Qt::CaseInsensitive)) {
setElementClass(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("widget"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"widget"_s, Qt::CaseInsensitive)) {
auto *v = new DomWidget();
v->read(reader);
setElementWidget(v);
continue;
}
- if (!tag.compare(QLatin1String("layoutdefault"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"layoutdefault"_s, Qt::CaseInsensitive)) {
auto *v = new DomLayoutDefault();
v->read(reader);
setElementLayoutDefault(v);
continue;
}
- if (!tag.compare(QLatin1String("layoutfunction"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"layoutfunction"_s, Qt::CaseInsensitive)) {
auto *v = new DomLayoutFunction();
v->read(reader);
setElementLayoutFunction(v);
continue;
}
- if (!tag.compare(QLatin1String("pixmapfunction"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"pixmapfunction"_s, Qt::CaseInsensitive)) {
setElementPixmapFunction(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("customwidgets"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"customwidgets"_s, Qt::CaseInsensitive)) {
auto *v = new DomCustomWidgets();
v->read(reader);
setElementCustomWidgets(v);
continue;
}
- if (!tag.compare(QLatin1String("tabstops"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"tabstops"_s, Qt::CaseInsensitive)) {
auto *v = new DomTabStops();
v->read(reader);
setElementTabStops(v);
continue;
}
- if (!tag.compare(QLatin1String("images"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"images"_s, Qt::CaseInsensitive)) {
qWarning("Omitting deprecated element <images>.");
reader.skipCurrentElement();
continue;
}
- if (!tag.compare(QLatin1String("includes"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"includes"_s, Qt::CaseInsensitive)) {
auto *v = new DomIncludes();
v->read(reader);
setElementIncludes(v);
continue;
}
- if (!tag.compare(QLatin1String("resources"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"resources"_s, Qt::CaseInsensitive)) {
auto *v = new DomResources();
v->read(reader);
setElementResources(v);
continue;
}
- if (!tag.compare(QLatin1String("connections"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"connections"_s, Qt::CaseInsensitive)) {
auto *v = new DomConnections();
v->read(reader);
setElementConnections(v);
continue;
}
- if (!tag.compare(QLatin1String("designerdata"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"designerdata"_s, Qt::CaseInsensitive)) {
auto *v = new DomDesignerData();
v->read(reader);
setElementDesignerdata(v);
continue;
}
- if (!tag.compare(QLatin1String("slots"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"slots"_s, Qt::CaseInsensitive)) {
auto *v = new DomSlots();
v->read(reader);
setElementSlots(v);
continue;
}
- if (!tag.compare(QLatin1String("buttongroups"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"buttongroups"_s, Qt::CaseInsensitive)) {
auto *v = new DomButtonGroups();
v->read(reader);
setElementButtonGroups(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -202,73 +180,73 @@ void DomUI::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("ui") : tagName.toLower());
if (hasAttributeVersion())
- writer.writeAttribute(QStringLiteral("version"), attributeVersion());
+ writer.writeAttribute(u"version"_s, attributeVersion());
if (hasAttributeLanguage())
- writer.writeAttribute(QStringLiteral("language"), attributeLanguage());
+ writer.writeAttribute(u"language"_s, attributeLanguage());
if (hasAttributeDisplayname())
- writer.writeAttribute(QStringLiteral("displayname"), attributeDisplayname());
+ writer.writeAttribute(u"displayname"_s, attributeDisplayname());
if (hasAttributeIdbasedtr())
- writer.writeAttribute(QStringLiteral("idbasedtr"), (attributeIdbasedtr() ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeAttribute(u"idbasedtr"_s, (attributeIdbasedtr() ? u"true"_s : u"false"_s));
if (hasAttributeConnectslotsbyname())
- writer.writeAttribute(QStringLiteral("connectslotsbyname"), (attributeConnectslotsbyname() ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeAttribute(u"connectslotsbyname"_s, (attributeConnectslotsbyname() ? u"true"_s : u"false"_s));
if (hasAttributeStdsetdef())
- writer.writeAttribute(QStringLiteral("stdsetdef"), QString::number(attributeStdsetdef()));
+ writer.writeAttribute(u"stdsetdef"_s, QString::number(attributeStdsetdef()));
if (hasAttributeStdSetDef())
- writer.writeAttribute(QStringLiteral("stdsetdef"), QString::number(attributeStdSetDef()));
+ writer.writeAttribute(u"stdsetdef"_s, QString::number(attributeStdSetDef()));
if (m_children & Author)
- writer.writeTextElement(QStringLiteral("author"), m_author);
+ writer.writeTextElement(u"author"_s, m_author);
if (m_children & Comment)
- writer.writeTextElement(QStringLiteral("comment"), m_comment);
+ writer.writeTextElement(u"comment"_s, m_comment);
if (m_children & ExportMacro)
- writer.writeTextElement(QStringLiteral("exportmacro"), m_exportMacro);
+ writer.writeTextElement(u"exportmacro"_s, m_exportMacro);
if (m_children & Class)
- writer.writeTextElement(QStringLiteral("class"), m_class);
+ writer.writeTextElement(u"class"_s, m_class);
if (m_children & Widget)
- m_widget->write(writer, QStringLiteral("widget"));
+ m_widget->write(writer, u"widget"_s);
if (m_children & LayoutDefault)
- m_layoutDefault->write(writer, QStringLiteral("layoutdefault"));
+ m_layoutDefault->write(writer, u"layoutdefault"_s);
if (m_children & LayoutFunction)
- m_layoutFunction->write(writer, QStringLiteral("layoutfunction"));
+ m_layoutFunction->write(writer, u"layoutfunction"_s);
if (m_children & PixmapFunction)
- writer.writeTextElement(QStringLiteral("pixmapfunction"), m_pixmapFunction);
+ writer.writeTextElement(u"pixmapfunction"_s, m_pixmapFunction);
if (m_children & CustomWidgets)
- m_customWidgets->write(writer, QStringLiteral("customwidgets"));
+ m_customWidgets->write(writer, u"customwidgets"_s);
if (m_children & TabStops)
- m_tabStops->write(writer, QStringLiteral("tabstops"));
+ m_tabStops->write(writer, u"tabstops"_s);
if (m_children & Includes)
- m_includes->write(writer, QStringLiteral("includes"));
+ m_includes->write(writer, u"includes"_s);
if (m_children & Resources)
- m_resources->write(writer, QStringLiteral("resources"));
+ m_resources->write(writer, u"resources"_s);
if (m_children & Connections)
- m_connections->write(writer, QStringLiteral("connections"));
+ m_connections->write(writer, u"connections"_s);
if (m_children & Designerdata)
- m_designerdata->write(writer, QStringLiteral("designerdata"));
+ m_designerdata->write(writer, u"designerdata"_s);
if (m_children & Slots)
- m_slots->write(writer, QStringLiteral("slots"));
+ m_slots->write(writer, u"slots"_s);
if (m_children & ButtonGroups)
- m_buttonGroups->write(writer, QStringLiteral("buttongroups"));
+ m_buttonGroups->write(writer, u"buttongroups"_s);
writer.writeEndElement();
}
@@ -582,13 +560,13 @@ void DomIncludes::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("include"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"include"_s, Qt::CaseInsensitive)) {
auto *v = new DomInclude();
v->read(reader);
m_include.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -604,7 +582,7 @@ void DomIncludes::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("includes") : tagName.toLower());
for (DomInclude *v : m_include)
- v->write(writer, QStringLiteral("include"));
+ v->write(writer, u"include"_s);
writer.writeEndElement();
}
@@ -622,22 +600,22 @@ void DomInclude::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("location")) {
+ if (name == u"location"_s) {
setAttributeLocation(attribute.value().toString());
continue;
}
- if (name == QLatin1String("impldecl")) {
+ if (name == u"impldecl"_s) {
setAttributeImpldecl(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -657,10 +635,10 @@ void DomInclude::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("include") : tagName.toLower());
if (hasAttributeLocation())
- writer.writeAttribute(QStringLiteral("location"), attributeLocation());
+ writer.writeAttribute(u"location"_s, attributeLocation());
if (hasAttributeImpldecl())
- writer.writeAttribute(QStringLiteral("impldecl"), attributeImpldecl());
+ writer.writeAttribute(u"impldecl"_s, attributeImpldecl());
if (!m_text.isEmpty())
writer.writeCharacters(m_text);
@@ -679,24 +657,24 @@ void DomResources::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("include"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"include"_s, Qt::CaseInsensitive)) {
auto *v = new DomResource();
v->read(reader);
m_include.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -712,10 +690,10 @@ void DomResources::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("resources") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
for (DomResource *v : m_include)
- v->write(writer, QStringLiteral("include"));
+ v->write(writer, u"include"_s);
writer.writeEndElement();
}
@@ -733,18 +711,18 @@ void DomResource::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("location")) {
+ if (name == u"location"_s) {
setAttributeLocation(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -760,7 +738,7 @@ void DomResource::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("resource") : tagName.toLower());
if (hasAttributeLocation())
- writer.writeAttribute(QStringLiteral("location"), attributeLocation());
+ writer.writeAttribute(u"location"_s, attributeLocation());
writer.writeEndElement();
}
@@ -782,42 +760,42 @@ void DomActionGroup::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("action"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"action"_s, Qt::CaseInsensitive)) {
auto *v = new DomAction();
v->read(reader);
m_action.append(v);
continue;
}
- if (!tag.compare(QLatin1String("actiongroup"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"actiongroup"_s, Qt::CaseInsensitive)) {
auto *v = new DomActionGroup();
v->read(reader);
m_actionGroup.append(v);
continue;
}
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- if (!tag.compare(QLatin1String("attribute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"attribute"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_attribute.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -833,19 +811,19 @@ void DomActionGroup::write(QXmlStreamWriter &writer, const QString &tagName) con
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("actiongroup") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
for (DomAction *v : m_action)
- v->write(writer, QStringLiteral("action"));
+ v->write(writer, u"action"_s);
for (DomActionGroup *v : m_actionGroup)
- v->write(writer, QStringLiteral("actiongroup"));
+ v->write(writer, u"actiongroup"_s);
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
for (DomProperty *v : m_attribute)
- v->write(writer, QStringLiteral("attribute"));
+ v->write(writer, u"attribute"_s);
writer.writeEndElement();
}
@@ -887,34 +865,34 @@ void DomAction::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- if (name == QLatin1String("menu")) {
+ if (name == u"menu"_s) {
setAttributeMenu(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- if (!tag.compare(QLatin1String("attribute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"attribute"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_attribute.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -930,16 +908,16 @@ void DomAction::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("action") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
if (hasAttributeMenu())
- writer.writeAttribute(QStringLiteral("menu"), attributeMenu());
+ writer.writeAttribute(u"menu"_s, attributeMenu());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
for (DomProperty *v : m_attribute)
- v->write(writer, QStringLiteral("attribute"));
+ v->write(writer, u"attribute"_s);
writer.writeEndElement();
}
@@ -963,18 +941,18 @@ void DomActionRef::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -990,7 +968,7 @@ void DomActionRef::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("actionref") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
writer.writeEndElement();
}
@@ -1008,30 +986,30 @@ void DomButtonGroup::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- if (!tag.compare(QLatin1String("attribute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"attribute"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_attribute.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1047,13 +1025,13 @@ void DomButtonGroup::write(QXmlStreamWriter &writer, const QString &tagName) con
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("buttongroup") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
for (DomProperty *v : m_attribute)
- v->write(writer, QStringLiteral("attribute"));
+ v->write(writer, u"attribute"_s);
writer.writeEndElement();
}
@@ -1082,13 +1060,13 @@ void DomButtonGroups::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("buttongroup"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"buttongroup"_s, Qt::CaseInsensitive)) {
auto *v = new DomButtonGroup();
v->read(reader);
m_buttonGroup.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1104,7 +1082,7 @@ void DomButtonGroups::write(QXmlStreamWriter &writer, const QString &tagName) co
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("buttongroups") : tagName.toLower());
for (DomButtonGroup *v : m_buttonGroup)
- v->write(writer, QStringLiteral("buttongroup"));
+ v->write(writer, u"buttongroup"_s);
writer.writeEndElement();
}
@@ -1127,13 +1105,13 @@ void DomCustomWidgets::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("customwidget"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"customwidget"_s, Qt::CaseInsensitive)) {
auto *v = new DomCustomWidget();
v->read(reader);
m_customWidget.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1149,7 +1127,7 @@ void DomCustomWidgets::write(QXmlStreamWriter &writer, const QString &tagName) c
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("customwidgets") : tagName.toLower());
for (DomCustomWidget *v : m_customWidget)
- v->write(writer, QStringLiteral("customwidget"));
+ v->write(writer, u"customwidget"_s);
writer.writeEndElement();
}
@@ -1167,18 +1145,18 @@ void DomHeader::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("location")) {
+ if (name == u"location"_s) {
setAttributeLocation(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1198,7 +1176,7 @@ void DomHeader::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("header") : tagName.toLower());
if (hasAttributeLocation())
- writer.writeAttribute(QStringLiteral("location"), attributeLocation());
+ writer.writeAttribute(u"location"_s, attributeLocation());
if (!m_text.isEmpty())
writer.writeCharacters(m_text);
@@ -1220,66 +1198,66 @@ void DomCustomWidget::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("class"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"class"_s, Qt::CaseInsensitive)) {
setElementClass(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("extends"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"extends"_s, Qt::CaseInsensitive)) {
setElementExtends(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("header"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"header"_s, Qt::CaseInsensitive)) {
auto *v = new DomHeader();
v->read(reader);
setElementHeader(v);
continue;
}
- if (!tag.compare(QLatin1String("sizehint"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"sizehint"_s, Qt::CaseInsensitive)) {
auto *v = new DomSize();
v->read(reader);
setElementSizeHint(v);
continue;
}
- if (!tag.compare(QLatin1String("addpagemethod"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"addpagemethod"_s, Qt::CaseInsensitive)) {
setElementAddPageMethod(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("container"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"container"_s, Qt::CaseInsensitive)) {
setElementContainer(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("sizepolicy"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"sizepolicy"_s, Qt::CaseInsensitive)) {
qWarning("Omitting deprecated element <sizepolicy>.");
reader.skipCurrentElement();
continue;
}
- if (!tag.compare(QLatin1String("pixmap"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"pixmap"_s, Qt::CaseInsensitive)) {
setElementPixmap(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("script"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"script"_s, Qt::CaseInsensitive)) {
qWarning("Omitting deprecated element <script>.");
reader.skipCurrentElement();
continue;
}
- if (!tag.compare(QLatin1String("properties"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"properties"_s, Qt::CaseInsensitive)) {
qWarning("Omitting deprecated element <properties>.");
reader.skipCurrentElement();
continue;
}
- if (!tag.compare(QLatin1String("slots"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"slots"_s, Qt::CaseInsensitive)) {
auto *v = new DomSlots();
v->read(reader);
setElementSlots(v);
continue;
}
- if (!tag.compare(QLatin1String("propertyspecifications"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"propertyspecifications"_s, Qt::CaseInsensitive)) {
auto *v = new DomPropertySpecifications();
v->read(reader);
setElementPropertyspecifications(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1295,31 +1273,31 @@ void DomCustomWidget::write(QXmlStreamWriter &writer, const QString &tagName) co
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("customwidget") : tagName.toLower());
if (m_children & Class)
- writer.writeTextElement(QStringLiteral("class"), m_class);
+ writer.writeTextElement(u"class"_s, m_class);
if (m_children & Extends)
- writer.writeTextElement(QStringLiteral("extends"), m_extends);
+ writer.writeTextElement(u"extends"_s, m_extends);
if (m_children & Header)
- m_header->write(writer, QStringLiteral("header"));
+ m_header->write(writer, u"header"_s);
if (m_children & SizeHint)
- m_sizeHint->write(writer, QStringLiteral("sizehint"));
+ m_sizeHint->write(writer, u"sizehint"_s);
if (m_children & AddPageMethod)
- writer.writeTextElement(QStringLiteral("addpagemethod"), m_addPageMethod);
+ writer.writeTextElement(u"addpagemethod"_s, m_addPageMethod);
if (m_children & Container)
- writer.writeTextElement(QStringLiteral("container"), QString::number(m_container));
+ writer.writeTextElement(u"container"_s, QString::number(m_container));
if (m_children & Pixmap)
- writer.writeTextElement(QStringLiteral("pixmap"), m_pixmap);
+ writer.writeTextElement(u"pixmap"_s, m_pixmap);
if (m_children & Slots)
- m_slots->write(writer, QStringLiteral("slots"));
+ m_slots->write(writer, u"slots"_s);
if (m_children & Propertyspecifications)
- m_propertyspecifications->write(writer, QStringLiteral("propertyspecifications"));
+ m_propertyspecifications->write(writer, u"propertyspecifications"_s);
writer.writeEndElement();
}
@@ -1474,22 +1452,22 @@ void DomLayoutDefault::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("spacing")) {
+ if (name == u"spacing"_s) {
setAttributeSpacing(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("margin")) {
+ if (name == u"margin"_s) {
setAttributeMargin(attribute.value().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1505,10 +1483,10 @@ void DomLayoutDefault::write(QXmlStreamWriter &writer, const QString &tagName) c
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("layoutdefault") : tagName.toLower());
if (hasAttributeSpacing())
- writer.writeAttribute(QStringLiteral("spacing"), QString::number(attributeSpacing()));
+ writer.writeAttribute(u"spacing"_s, QString::number(attributeSpacing()));
if (hasAttributeMargin())
- writer.writeAttribute(QStringLiteral("margin"), QString::number(attributeMargin()));
+ writer.writeAttribute(u"margin"_s, QString::number(attributeMargin()));
writer.writeEndElement();
}
@@ -1520,22 +1498,22 @@ void DomLayoutFunction::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("spacing")) {
+ if (name == u"spacing"_s) {
setAttributeSpacing(attribute.value().toString());
continue;
}
- if (name == QLatin1String("margin")) {
+ if (name == u"margin"_s) {
setAttributeMargin(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1551,10 +1529,10 @@ void DomLayoutFunction::write(QXmlStreamWriter &writer, const QString &tagName)
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("layoutfunction") : tagName.toLower());
if (hasAttributeSpacing())
- writer.writeAttribute(QStringLiteral("spacing"), attributeSpacing());
+ writer.writeAttribute(u"spacing"_s, attributeSpacing());
if (hasAttributeMargin())
- writer.writeAttribute(QStringLiteral("margin"), attributeMargin());
+ writer.writeAttribute(u"margin"_s, attributeMargin());
writer.writeEndElement();
}
@@ -1570,11 +1548,11 @@ void DomTabStops::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("tabstop"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"tabstop"_s, Qt::CaseInsensitive)) {
m_tabStop.append(reader.readElementText());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1590,7 +1568,7 @@ void DomTabStops::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("tabstops") : tagName.toLower());
for (const QString &v : m_tabStop)
- writer.writeTextElement(QStringLiteral("tabstop"), v);
+ writer.writeTextElement(u"tabstop"_s, v);
writer.writeEndElement();
}
@@ -1616,60 +1594,60 @@ void DomLayout::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("class")) {
+ if (name == u"class"_s) {
setAttributeClass(attribute.value().toString());
continue;
}
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- if (name == QLatin1String("stretch")) {
+ if (name == u"stretch"_s) {
setAttributeStretch(attribute.value().toString());
continue;
}
- if (name == QLatin1String("rowstretch")) {
+ if (name == u"rowstretch"_s) {
setAttributeRowStretch(attribute.value().toString());
continue;
}
- if (name == QLatin1String("columnstretch")) {
+ if (name == u"columnstretch"_s) {
setAttributeColumnStretch(attribute.value().toString());
continue;
}
- if (name == QLatin1String("rowminimumheight")) {
+ if (name == u"rowminimumheight"_s) {
setAttributeRowMinimumHeight(attribute.value().toString());
continue;
}
- if (name == QLatin1String("columnminimumwidth")) {
+ if (name == u"columnminimumwidth"_s) {
setAttributeColumnMinimumWidth(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- if (!tag.compare(QLatin1String("attribute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"attribute"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_attribute.append(v);
continue;
}
- if (!tag.compare(QLatin1String("item"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"item"_s, Qt::CaseInsensitive)) {
auto *v = new DomLayoutItem();
v->read(reader);
m_item.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1685,34 +1663,34 @@ void DomLayout::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("layout") : tagName.toLower());
if (hasAttributeClass())
- writer.writeAttribute(QStringLiteral("class"), attributeClass());
+ writer.writeAttribute(u"class"_s, attributeClass());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
if (hasAttributeStretch())
- writer.writeAttribute(QStringLiteral("stretch"), attributeStretch());
+ writer.writeAttribute(u"stretch"_s, attributeStretch());
if (hasAttributeRowStretch())
- writer.writeAttribute(QStringLiteral("rowstretch"), attributeRowStretch());
+ writer.writeAttribute(u"rowstretch"_s, attributeRowStretch());
if (hasAttributeColumnStretch())
- writer.writeAttribute(QStringLiteral("columnstretch"), attributeColumnStretch());
+ writer.writeAttribute(u"columnstretch"_s, attributeColumnStretch());
if (hasAttributeRowMinimumHeight())
- writer.writeAttribute(QStringLiteral("rowminimumheight"), attributeRowMinimumHeight());
+ writer.writeAttribute(u"rowminimumheight"_s, attributeRowMinimumHeight());
if (hasAttributeColumnMinimumWidth())
- writer.writeAttribute(QStringLiteral("columnminimumwidth"), attributeColumnMinimumWidth());
+ writer.writeAttribute(u"columnminimumwidth"_s, attributeColumnMinimumWidth());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
for (DomProperty *v : m_attribute)
- v->write(writer, QStringLiteral("attribute"));
+ v->write(writer, u"attribute"_s);
for (DomLayoutItem *v : m_item)
- v->write(writer, QStringLiteral("item"));
+ v->write(writer, u"item"_s);
writer.writeEndElement();
}
@@ -1760,52 +1738,52 @@ void DomLayoutItem::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("row")) {
+ if (name == u"row"_s) {
setAttributeRow(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("column")) {
+ if (name == u"column"_s) {
setAttributeColumn(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("rowspan")) {
+ if (name == u"rowspan"_s) {
setAttributeRowSpan(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("colspan")) {
+ if (name == u"colspan"_s) {
setAttributeColSpan(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("alignment")) {
+ if (name == u"alignment"_s) {
setAttributeAlignment(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("widget"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"widget"_s, Qt::CaseInsensitive)) {
auto *v = new DomWidget();
v->read(reader);
setElementWidget(v);
continue;
}
- if (!tag.compare(QLatin1String("layout"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"layout"_s, Qt::CaseInsensitive)) {
auto *v = new DomLayout();
v->read(reader);
setElementLayout(v);
continue;
}
- if (!tag.compare(QLatin1String("spacer"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"spacer"_s, Qt::CaseInsensitive)) {
auto *v = new DomSpacer();
v->read(reader);
setElementSpacer(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1821,34 +1799,34 @@ void DomLayoutItem::write(QXmlStreamWriter &writer, const QString &tagName) cons
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("layoutitem") : tagName.toLower());
if (hasAttributeRow())
- writer.writeAttribute(QStringLiteral("row"), QString::number(attributeRow()));
+ writer.writeAttribute(u"row"_s, QString::number(attributeRow()));
if (hasAttributeColumn())
- writer.writeAttribute(QStringLiteral("column"), QString::number(attributeColumn()));
+ writer.writeAttribute(u"column"_s, QString::number(attributeColumn()));
if (hasAttributeRowSpan())
- writer.writeAttribute(QStringLiteral("rowspan"), QString::number(attributeRowSpan()));
+ writer.writeAttribute(u"rowspan"_s, QString::number(attributeRowSpan()));
if (hasAttributeColSpan())
- writer.writeAttribute(QStringLiteral("colspan"), QString::number(attributeColSpan()));
+ writer.writeAttribute(u"colspan"_s, QString::number(attributeColSpan()));
if (hasAttributeAlignment())
- writer.writeAttribute(QStringLiteral("alignment"), attributeAlignment());
+ writer.writeAttribute(u"alignment"_s, attributeAlignment());
switch (kind()) {
case Widget:
if (m_widget != nullptr)
- m_widget->write(writer, QStringLiteral("widget"));
+ m_widget->write(writer, u"widget"_s);
break;
case Layout:
if (m_layout != nullptr)
- m_layout->write(writer, QStringLiteral("layout"));
+ m_layout->write(writer, u"layout"_s);
break;
case Spacer:
if (m_spacer != nullptr)
- m_spacer->write(writer, QStringLiteral("spacer"));
+ m_spacer->write(writer, u"spacer"_s);
break;
default:
@@ -1911,13 +1889,13 @@ void DomRow::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1933,7 +1911,7 @@ void DomRow::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("row") : tagName.toLower());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
writer.writeEndElement();
}
@@ -1956,13 +1934,13 @@ void DomColumn::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -1978,7 +1956,7 @@ void DomColumn::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("column") : tagName.toLower());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
writer.writeEndElement();
}
@@ -2002,34 +1980,34 @@ void DomItem::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("row")) {
+ if (name == u"row"_s) {
setAttributeRow(attribute.value().toInt());
continue;
}
- if (name == QLatin1String("column")) {
+ if (name == u"column"_s) {
setAttributeColumn(attribute.value().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- if (!tag.compare(QLatin1String("item"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"item"_s, Qt::CaseInsensitive)) {
auto *v = new DomItem();
v->read(reader);
m_item.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2045,16 +2023,16 @@ void DomItem::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("item") : tagName.toLower());
if (hasAttributeRow())
- writer.writeAttribute(QStringLiteral("row"), QString::number(attributeRow()));
+ writer.writeAttribute(u"row"_s, QString::number(attributeRow()));
if (hasAttributeColumn())
- writer.writeAttribute(QStringLiteral("column"), QString::number(attributeColumn()));
+ writer.writeAttribute(u"column"_s, QString::number(attributeColumn()));
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
for (DomItem *v : m_item)
- v->write(writer, QStringLiteral("item"));
+ v->write(writer, u"item"_s);
writer.writeEndElement();
}
@@ -2102,104 +2080,104 @@ void DomWidget::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("class")) {
+ if (name == u"class"_s) {
setAttributeClass(attribute.value().toString());
continue;
}
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- if (name == QLatin1String("native")) {
- setAttributeNative(attribute.value() == QLatin1String("true"));
+ if (name == u"native"_s) {
+ setAttributeNative(attribute.value() == u"true"_s);
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("class"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"class"_s, Qt::CaseInsensitive)) {
m_class.append(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- if (!tag.compare(QLatin1String("script"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"script"_s, Qt::CaseInsensitive)) {
qWarning("Omitting deprecated element <script>.");
reader.skipCurrentElement();
continue;
}
- if (!tag.compare(QLatin1String("widgetdata"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"widgetdata"_s, Qt::CaseInsensitive)) {
qWarning("Omitting deprecated element <widgetdata>.");
reader.skipCurrentElement();
continue;
}
- if (!tag.compare(QLatin1String("attribute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"attribute"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_attribute.append(v);
continue;
}
- if (!tag.compare(QLatin1String("row"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"row"_s, Qt::CaseInsensitive)) {
auto *v = new DomRow();
v->read(reader);
m_row.append(v);
continue;
}
- if (!tag.compare(QLatin1String("column"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"column"_s, Qt::CaseInsensitive)) {
auto *v = new DomColumn();
v->read(reader);
m_column.append(v);
continue;
}
- if (!tag.compare(QLatin1String("item"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"item"_s, Qt::CaseInsensitive)) {
auto *v = new DomItem();
v->read(reader);
m_item.append(v);
continue;
}
- if (!tag.compare(QLatin1String("layout"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"layout"_s, Qt::CaseInsensitive)) {
auto *v = new DomLayout();
v->read(reader);
m_layout.append(v);
continue;
}
- if (!tag.compare(QLatin1String("widget"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"widget"_s, Qt::CaseInsensitive)) {
auto *v = new DomWidget();
v->read(reader);
m_widget.append(v);
continue;
}
- if (!tag.compare(QLatin1String("action"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"action"_s, Qt::CaseInsensitive)) {
auto *v = new DomAction();
v->read(reader);
m_action.append(v);
continue;
}
- if (!tag.compare(QLatin1String("actiongroup"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"actiongroup"_s, Qt::CaseInsensitive)) {
auto *v = new DomActionGroup();
v->read(reader);
m_actionGroup.append(v);
continue;
}
- if (!tag.compare(QLatin1String("addaction"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"addaction"_s, Qt::CaseInsensitive)) {
auto *v = new DomActionRef();
v->read(reader);
m_addAction.append(v);
continue;
}
- if (!tag.compare(QLatin1String("zorder"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"zorder"_s, Qt::CaseInsensitive)) {
m_zOrder.append(reader.readElementText());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2215,49 +2193,49 @@ void DomWidget::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("widget") : tagName.toLower());
if (hasAttributeClass())
- writer.writeAttribute(QStringLiteral("class"), attributeClass());
+ writer.writeAttribute(u"class"_s, attributeClass());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
if (hasAttributeNative())
- writer.writeAttribute(QStringLiteral("native"), (attributeNative() ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeAttribute(u"native"_s, (attributeNative() ? u"true"_s : u"false"_s));
for (const QString &v : m_class)
- writer.writeTextElement(QStringLiteral("class"), v);
+ writer.writeTextElement(u"class"_s, v);
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
for (DomProperty *v : m_attribute)
- v->write(writer, QStringLiteral("attribute"));
+ v->write(writer, u"attribute"_s);
for (DomRow *v : m_row)
- v->write(writer, QStringLiteral("row"));
+ v->write(writer, u"row"_s);
for (DomColumn *v : m_column)
- v->write(writer, QStringLiteral("column"));
+ v->write(writer, u"column"_s);
for (DomItem *v : m_item)
- v->write(writer, QStringLiteral("item"));
+ v->write(writer, u"item"_s);
for (DomLayout *v : m_layout)
- v->write(writer, QStringLiteral("layout"));
+ v->write(writer, u"layout"_s);
for (DomWidget *v : m_widget)
- v->write(writer, QStringLiteral("widget"));
+ v->write(writer, u"widget"_s);
for (DomAction *v : m_action)
- v->write(writer, QStringLiteral("action"));
+ v->write(writer, u"action"_s);
for (DomActionGroup *v : m_actionGroup)
- v->write(writer, QStringLiteral("actiongroup"));
+ v->write(writer, u"actiongroup"_s);
for (DomActionRef *v : m_addAction)
- v->write(writer, QStringLiteral("addaction"));
+ v->write(writer, u"addaction"_s);
for (const QString &v : m_zOrder)
- writer.writeTextElement(QStringLiteral("zorder"), v);
+ writer.writeTextElement(u"zorder"_s, v);
writer.writeEndElement();
}
@@ -2345,24 +2323,24 @@ void DomSpacer::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2378,10 +2356,10 @@ void DomSpacer::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("spacer") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
writer.writeEndElement();
}
@@ -2399,30 +2377,30 @@ void DomColor::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("alpha")) {
+ if (name == u"alpha"_s) {
setAttributeAlpha(attribute.value().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("red"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"red"_s, Qt::CaseInsensitive)) {
setElementRed(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("green"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"green"_s, Qt::CaseInsensitive)) {
setElementGreen(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("blue"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"blue"_s, Qt::CaseInsensitive)) {
setElementBlue(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2438,16 +2416,16 @@ void DomColor::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("color") : tagName.toLower());
if (hasAttributeAlpha())
- writer.writeAttribute(QStringLiteral("alpha"), QString::number(attributeAlpha()));
+ writer.writeAttribute(u"alpha"_s, QString::number(attributeAlpha()));
if (m_children & Red)
- writer.writeTextElement(QStringLiteral("red"), QString::number(m_red));
+ writer.writeTextElement(u"red"_s, QString::number(m_red));
if (m_children & Green)
- writer.writeTextElement(QStringLiteral("green"), QString::number(m_green));
+ writer.writeTextElement(u"green"_s, QString::number(m_green));
if (m_children & Blue)
- writer.writeTextElement(QStringLiteral("blue"), QString::number(m_blue));
+ writer.writeTextElement(u"blue"_s, QString::number(m_blue));
writer.writeEndElement();
}
@@ -2495,24 +2473,24 @@ void DomGradientStop::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("position")) {
+ if (name == u"position"_s) {
setAttributePosition(attribute.value().toDouble());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("color"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"color"_s, Qt::CaseInsensitive)) {
auto *v = new DomColor();
v->read(reader);
setElementColor(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2528,10 +2506,10 @@ void DomGradientStop::write(QXmlStreamWriter &writer, const QString &tagName) co
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("gradientstop") : tagName.toLower());
if (hasAttributePosition())
- writer.writeAttribute(QStringLiteral("position"), QString::number(attributePosition(), 'f', 15));
+ writer.writeAttribute(u"position"_s, QString::number(attributePosition(), 'f', 15));
if (m_children & Color)
- m_color->write(writer, QStringLiteral("color"));
+ m_color->write(writer, u"color"_s);
writer.writeEndElement();
}
@@ -2569,72 +2547,72 @@ void DomGradient::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("startx")) {
+ if (name == u"startx"_s) {
setAttributeStartX(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("starty")) {
+ if (name == u"starty"_s) {
setAttributeStartY(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("endx")) {
+ if (name == u"endx"_s) {
setAttributeEndX(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("endy")) {
+ if (name == u"endy"_s) {
setAttributeEndY(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("centralx")) {
+ if (name == u"centralx"_s) {
setAttributeCentralX(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("centraly")) {
+ if (name == u"centraly"_s) {
setAttributeCentralY(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("focalx")) {
+ if (name == u"focalx"_s) {
setAttributeFocalX(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("focaly")) {
+ if (name == u"focaly"_s) {
setAttributeFocalY(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("radius")) {
+ if (name == u"radius"_s) {
setAttributeRadius(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("angle")) {
+ if (name == u"angle"_s) {
setAttributeAngle(attribute.value().toDouble());
continue;
}
- if (name == QLatin1String("type")) {
+ if (name == u"type"_s) {
setAttributeType(attribute.value().toString());
continue;
}
- if (name == QLatin1String("spread")) {
+ if (name == u"spread"_s) {
setAttributeSpread(attribute.value().toString());
continue;
}
- if (name == QLatin1String("coordinatemode")) {
+ if (name == u"coordinatemode"_s) {
setAttributeCoordinateMode(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("gradientstop"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"gradientstop"_s, Qt::CaseInsensitive)) {
auto *v = new DomGradientStop();
v->read(reader);
m_gradientStop.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2650,46 +2628,46 @@ void DomGradient::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("gradient") : tagName.toLower());
if (hasAttributeStartX())
- writer.writeAttribute(QStringLiteral("startx"), QString::number(attributeStartX(), 'f', 15));
+ writer.writeAttribute(u"startx"_s, QString::number(attributeStartX(), 'f', 15));
if (hasAttributeStartY())
- writer.writeAttribute(QStringLiteral("starty"), QString::number(attributeStartY(), 'f', 15));
+ writer.writeAttribute(u"starty"_s, QString::number(attributeStartY(), 'f', 15));
if (hasAttributeEndX())
- writer.writeAttribute(QStringLiteral("endx"), QString::number(attributeEndX(), 'f', 15));
+ writer.writeAttribute(u"endx"_s, QString::number(attributeEndX(), 'f', 15));
if (hasAttributeEndY())
- writer.writeAttribute(QStringLiteral("endy"), QString::number(attributeEndY(), 'f', 15));
+ writer.writeAttribute(u"endy"_s, QString::number(attributeEndY(), 'f', 15));
if (hasAttributeCentralX())
- writer.writeAttribute(QStringLiteral("centralx"), QString::number(attributeCentralX(), 'f', 15));
+ writer.writeAttribute(u"centralx"_s, QString::number(attributeCentralX(), 'f', 15));
if (hasAttributeCentralY())
- writer.writeAttribute(QStringLiteral("centraly"), QString::number(attributeCentralY(), 'f', 15));
+ writer.writeAttribute(u"centraly"_s, QString::number(attributeCentralY(), 'f', 15));
if (hasAttributeFocalX())
- writer.writeAttribute(QStringLiteral("focalx"), QString::number(attributeFocalX(), 'f', 15));
+ writer.writeAttribute(u"focalx"_s, QString::number(attributeFocalX(), 'f', 15));
if (hasAttributeFocalY())
- writer.writeAttribute(QStringLiteral("focaly"), QString::number(attributeFocalY(), 'f', 15));
+ writer.writeAttribute(u"focaly"_s, QString::number(attributeFocalY(), 'f', 15));
if (hasAttributeRadius())
- writer.writeAttribute(QStringLiteral("radius"), QString::number(attributeRadius(), 'f', 15));
+ writer.writeAttribute(u"radius"_s, QString::number(attributeRadius(), 'f', 15));
if (hasAttributeAngle())
- writer.writeAttribute(QStringLiteral("angle"), QString::number(attributeAngle(), 'f', 15));
+ writer.writeAttribute(u"angle"_s, QString::number(attributeAngle(), 'f', 15));
if (hasAttributeType())
- writer.writeAttribute(QStringLiteral("type"), attributeType());
+ writer.writeAttribute(u"type"_s, attributeType());
if (hasAttributeSpread())
- writer.writeAttribute(QStringLiteral("spread"), attributeSpread());
+ writer.writeAttribute(u"spread"_s, attributeSpread());
if (hasAttributeCoordinateMode())
- writer.writeAttribute(QStringLiteral("coordinatemode"), attributeCoordinateMode());
+ writer.writeAttribute(u"coordinatemode"_s, attributeCoordinateMode());
for (DomGradientStop *v : m_gradientStop)
- v->write(writer, QStringLiteral("gradientstop"));
+ v->write(writer, u"gradientstop"_s);
writer.writeEndElement();
}
@@ -2725,36 +2703,36 @@ void DomBrush::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("brushstyle")) {
+ if (name == u"brushstyle"_s) {
setAttributeBrushStyle(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("color"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"color"_s, Qt::CaseInsensitive)) {
auto *v = new DomColor();
v->read(reader);
setElementColor(v);
continue;
}
- if (!tag.compare(QLatin1String("texture"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"texture"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
setElementTexture(v);
continue;
}
- if (!tag.compare(QLatin1String("gradient"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"gradient"_s, Qt::CaseInsensitive)) {
auto *v = new DomGradient();
v->read(reader);
setElementGradient(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2770,22 +2748,22 @@ void DomBrush::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("brush") : tagName.toLower());
if (hasAttributeBrushStyle())
- writer.writeAttribute(QStringLiteral("brushstyle"), attributeBrushStyle());
+ writer.writeAttribute(u"brushstyle"_s, attributeBrushStyle());
switch (kind()) {
case Color:
if (m_color != nullptr)
- m_color->write(writer, QStringLiteral("color"));
+ m_color->write(writer, u"color"_s);
break;
case Texture:
if (m_texture != nullptr)
- m_texture->write(writer, QStringLiteral("texture"));
+ m_texture->write(writer, u"texture"_s);
break;
case Gradient:
if (m_gradient != nullptr)
- m_gradient->write(writer, QStringLiteral("gradient"));
+ m_gradient->write(writer, u"gradient"_s);
break;
default:
@@ -2846,24 +2824,24 @@ void DomColorRole::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("role")) {
+ if (name == u"role"_s) {
setAttributeRole(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("brush"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"brush"_s, Qt::CaseInsensitive)) {
auto *v = new DomBrush();
v->read(reader);
setElementBrush(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2879,10 +2857,10 @@ void DomColorRole::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("colorrole") : tagName.toLower());
if (hasAttributeRole())
- writer.writeAttribute(QStringLiteral("role"), attributeRole());
+ writer.writeAttribute(u"role"_s, attributeRole());
if (m_children & Brush)
- m_brush->write(writer, QStringLiteral("brush"));
+ m_brush->write(writer, u"brush"_s);
writer.writeEndElement();
}
@@ -2923,19 +2901,19 @@ void DomColorGroup::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("colorrole"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"colorrole"_s, Qt::CaseInsensitive)) {
auto *v = new DomColorRole();
v->read(reader);
m_colorRole.append(v);
continue;
}
- if (!tag.compare(QLatin1String("color"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"color"_s, Qt::CaseInsensitive)) {
auto *v = new DomColor();
v->read(reader);
m_color.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -2951,10 +2929,10 @@ void DomColorGroup::write(QXmlStreamWriter &writer, const QString &tagName) cons
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("colorgroup") : tagName.toLower());
for (DomColorRole *v : m_colorRole)
- v->write(writer, QStringLiteral("colorrole"));
+ v->write(writer, u"colorrole"_s);
for (DomColor *v : m_color)
- v->write(writer, QStringLiteral("color"));
+ v->write(writer, u"color"_s);
writer.writeEndElement();
}
@@ -2984,25 +2962,25 @@ void DomPalette::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("active"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"active"_s, Qt::CaseInsensitive)) {
auto *v = new DomColorGroup();
v->read(reader);
setElementActive(v);
continue;
}
- if (!tag.compare(QLatin1String("inactive"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"inactive"_s, Qt::CaseInsensitive)) {
auto *v = new DomColorGroup();
v->read(reader);
setElementInactive(v);
continue;
}
- if (!tag.compare(QLatin1String("disabled"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"disabled"_s, Qt::CaseInsensitive)) {
auto *v = new DomColorGroup();
v->read(reader);
setElementDisabled(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3018,13 +2996,13 @@ void DomPalette::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("palette") : tagName.toLower());
if (m_children & Active)
- m_active->write(writer, QStringLiteral("active"));
+ m_active->write(writer, u"active"_s);
if (m_children & Inactive)
- m_inactive->write(writer, QStringLiteral("inactive"));
+ m_inactive->write(writer, u"inactive"_s);
if (m_children & Disabled)
- m_disabled->write(writer, QStringLiteral("disabled"));
+ m_disabled->write(writer, u"disabled"_s);
writer.writeEndElement();
}
@@ -3103,47 +3081,55 @@ void DomFont::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("family"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"family"_s, Qt::CaseInsensitive)) {
setElementFamily(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("pointsize"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"pointsize"_s, Qt::CaseInsensitive)) {
setElementPointSize(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("weight"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"weight"_s, Qt::CaseInsensitive)) {
setElementWeight(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("italic"), Qt::CaseInsensitive)) {
- setElementItalic(reader.readElementText() == QLatin1String("true"));
+ if (!tag.compare(u"italic"_s, Qt::CaseInsensitive)) {
+ setElementItalic(reader.readElementText() == u"true"_s);
continue;
}
- if (!tag.compare(QLatin1String("bold"), Qt::CaseInsensitive)) {
- setElementBold(reader.readElementText() == QLatin1String("true"));
+ if (!tag.compare(u"bold"_s, Qt::CaseInsensitive)) {
+ setElementBold(reader.readElementText() == u"true"_s);
continue;
}
- if (!tag.compare(QLatin1String("underline"), Qt::CaseInsensitive)) {
- setElementUnderline(reader.readElementText() == QLatin1String("true"));
+ if (!tag.compare(u"underline"_s, Qt::CaseInsensitive)) {
+ setElementUnderline(reader.readElementText() == u"true"_s);
continue;
}
- if (!tag.compare(QLatin1String("strikeout"), Qt::CaseInsensitive)) {
- setElementStrikeOut(reader.readElementText() == QLatin1String("true"));
+ if (!tag.compare(u"strikeout"_s, Qt::CaseInsensitive)) {
+ setElementStrikeOut(reader.readElementText() == u"true"_s);
continue;
}
- if (!tag.compare(QLatin1String("antialiasing"), Qt::CaseInsensitive)) {
- setElementAntialiasing(reader.readElementText() == QLatin1String("true"));
+ if (!tag.compare(u"antialiasing"_s, Qt::CaseInsensitive)) {
+ setElementAntialiasing(reader.readElementText() == u"true"_s);
continue;
}
- if (!tag.compare(QLatin1String("stylestrategy"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"stylestrategy"_s, Qt::CaseInsensitive)) {
setElementStyleStrategy(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("kerning"), Qt::CaseInsensitive)) {
- setElementKerning(reader.readElementText() == QLatin1String("true"));
+ if (!tag.compare(u"kerning"_s, Qt::CaseInsensitive)) {
+ setElementKerning(reader.readElementText() == u"true"_s);
+ continue;
+ }
+ if (!tag.compare(u"hintingpreference"_s, Qt::CaseInsensitive)) {
+ setElementHintingPreference(reader.readElementText());
+ continue;
+ }
+ if (!tag.compare(u"fontweight"_s, Qt::CaseInsensitive)) {
+ setElementFontWeight(reader.readElementText());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3159,34 +3145,40 @@ void DomFont::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("font") : tagName.toLower());
if (m_children & Family)
- writer.writeTextElement(QStringLiteral("family"), m_family);
+ writer.writeTextElement(u"family"_s, m_family);
if (m_children & PointSize)
- writer.writeTextElement(QStringLiteral("pointsize"), QString::number(m_pointSize));
+ writer.writeTextElement(u"pointsize"_s, QString::number(m_pointSize));
if (m_children & Weight)
- writer.writeTextElement(QStringLiteral("weight"), QString::number(m_weight));
+ writer.writeTextElement(u"weight"_s, QString::number(m_weight));
if (m_children & Italic)
- writer.writeTextElement(QStringLiteral("italic"), (m_italic ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeTextElement(u"italic"_s, (m_italic ? u"true"_s : u"false"_s));
if (m_children & Bold)
- writer.writeTextElement(QStringLiteral("bold"), (m_bold ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeTextElement(u"bold"_s, (m_bold ? u"true"_s : u"false"_s));
if (m_children & Underline)
- writer.writeTextElement(QStringLiteral("underline"), (m_underline ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeTextElement(u"underline"_s, (m_underline ? u"true"_s : u"false"_s));
if (m_children & StrikeOut)
- writer.writeTextElement(QStringLiteral("strikeout"), (m_strikeOut ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeTextElement(u"strikeout"_s, (m_strikeOut ? u"true"_s : u"false"_s));
if (m_children & Antialiasing)
- writer.writeTextElement(QStringLiteral("antialiasing"), (m_antialiasing ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeTextElement(u"antialiasing"_s, (m_antialiasing ? u"true"_s : u"false"_s));
if (m_children & StyleStrategy)
- writer.writeTextElement(QStringLiteral("stylestrategy"), m_styleStrategy);
+ writer.writeTextElement(u"stylestrategy"_s, m_styleStrategy);
if (m_children & Kerning)
- writer.writeTextElement(QStringLiteral("kerning"), (m_kerning ? QLatin1String("true") : QLatin1String("false")));
+ writer.writeTextElement(u"kerning"_s, (m_kerning ? u"true"_s : u"false"_s));
+
+ if (m_children & HintingPreference)
+ writer.writeTextElement(u"hintingpreference"_s, m_hintingPreference);
+
+ if (m_children & FontWeight)
+ writer.writeTextElement(u"fontweight"_s, m_fontWeight);
writer.writeEndElement();
}
@@ -3251,6 +3243,18 @@ void DomFont::setElementKerning(bool a)
m_kerning = a;
}
+void DomFont::setElementHintingPreference(const QString &a)
+{
+ m_children |= HintingPreference;
+ m_hintingPreference = a;
+}
+
+void DomFont::setElementFontWeight(const QString &a)
+{
+ m_children |= FontWeight;
+ m_fontWeight = a;
+}
+
void DomFont::clearElementFamily()
{
m_children &= ~Family;
@@ -3301,6 +3305,16 @@ void DomFont::clearElementKerning()
m_children &= ~Kerning;
}
+void DomFont::clearElementHintingPreference()
+{
+ m_children &= ~HintingPreference;
+}
+
+void DomFont::clearElementFontWeight()
+{
+ m_children &= ~FontWeight;
+}
+
DomPoint::~DomPoint() = default;
void DomPoint::read(QXmlStreamReader &reader)
@@ -3309,15 +3323,15 @@ void DomPoint::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("x"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"x"_s, Qt::CaseInsensitive)) {
setElementX(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("y"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"y"_s, Qt::CaseInsensitive)) {
setElementY(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3333,10 +3347,10 @@ void DomPoint::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("point") : tagName.toLower());
if (m_children & X)
- writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x));
+ writer.writeTextElement(u"x"_s, QString::number(m_x));
if (m_children & Y)
- writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y));
+ writer.writeTextElement(u"y"_s, QString::number(m_y));
writer.writeEndElement();
}
@@ -3371,23 +3385,23 @@ void DomRect::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("x"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"x"_s, Qt::CaseInsensitive)) {
setElementX(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("y"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"y"_s, Qt::CaseInsensitive)) {
setElementY(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("width"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"width"_s, Qt::CaseInsensitive)) {
setElementWidth(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("height"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"height"_s, Qt::CaseInsensitive)) {
setElementHeight(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3403,16 +3417,16 @@ void DomRect::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("rect") : tagName.toLower());
if (m_children & X)
- writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x));
+ writer.writeTextElement(u"x"_s, QString::number(m_x));
if (m_children & Y)
- writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y));
+ writer.writeTextElement(u"y"_s, QString::number(m_y));
if (m_children & Width)
- writer.writeTextElement(QStringLiteral("width"), QString::number(m_width));
+ writer.writeTextElement(u"width"_s, QString::number(m_width));
if (m_children & Height)
- writer.writeTextElement(QStringLiteral("height"), QString::number(m_height));
+ writer.writeTextElement(u"height"_s, QString::number(m_height));
writer.writeEndElement();
}
@@ -3468,22 +3482,22 @@ void DomLocale::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("language")) {
+ if (name == u"language"_s) {
setAttributeLanguage(attribute.value().toString());
continue;
}
- if (name == QLatin1String("country")) {
+ if (name == u"country"_s) {
setAttributeCountry(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3499,10 +3513,10 @@ void DomLocale::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("locale") : tagName.toLower());
if (hasAttributeLanguage())
- writer.writeAttribute(QStringLiteral("language"), attributeLanguage());
+ writer.writeAttribute(u"language"_s, attributeLanguage());
if (hasAttributeCountry())
- writer.writeAttribute(QStringLiteral("country"), attributeCountry());
+ writer.writeAttribute(u"country"_s, attributeCountry());
writer.writeEndElement();
}
@@ -3514,38 +3528,38 @@ void DomSizePolicy::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("hsizetype")) {
+ if (name == u"hsizetype"_s) {
setAttributeHSizeType(attribute.value().toString());
continue;
}
- if (name == QLatin1String("vsizetype")) {
+ if (name == u"vsizetype"_s) {
setAttributeVSizeType(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("hsizetype"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"hsizetype"_s, Qt::CaseInsensitive)) {
setElementHSizeType(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("vsizetype"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"vsizetype"_s, Qt::CaseInsensitive)) {
setElementVSizeType(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("horstretch"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"horstretch"_s, Qt::CaseInsensitive)) {
setElementHorStretch(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("verstretch"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"verstretch"_s, Qt::CaseInsensitive)) {
setElementVerStretch(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3561,22 +3575,22 @@ void DomSizePolicy::write(QXmlStreamWriter &writer, const QString &tagName) cons
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("sizepolicy") : tagName.toLower());
if (hasAttributeHSizeType())
- writer.writeAttribute(QStringLiteral("hsizetype"), attributeHSizeType());
+ writer.writeAttribute(u"hsizetype"_s, attributeHSizeType());
if (hasAttributeVSizeType())
- writer.writeAttribute(QStringLiteral("vsizetype"), attributeVSizeType());
+ writer.writeAttribute(u"vsizetype"_s, attributeVSizeType());
if (m_children & HSizeType)
- writer.writeTextElement(QStringLiteral("hsizetype"), QString::number(m_hSizeType));
+ writer.writeTextElement(u"hsizetype"_s, QString::number(m_hSizeType));
if (m_children & VSizeType)
- writer.writeTextElement(QStringLiteral("vsizetype"), QString::number(m_vSizeType));
+ writer.writeTextElement(u"vsizetype"_s, QString::number(m_vSizeType));
if (m_children & HorStretch)
- writer.writeTextElement(QStringLiteral("horstretch"), QString::number(m_horStretch));
+ writer.writeTextElement(u"horstretch"_s, QString::number(m_horStretch));
if (m_children & VerStretch)
- writer.writeTextElement(QStringLiteral("verstretch"), QString::number(m_verStretch));
+ writer.writeTextElement(u"verstretch"_s, QString::number(m_verStretch));
writer.writeEndElement();
}
@@ -3633,15 +3647,15 @@ void DomSize::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("width"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"width"_s, Qt::CaseInsensitive)) {
setElementWidth(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("height"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"height"_s, Qt::CaseInsensitive)) {
setElementHeight(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3657,10 +3671,10 @@ void DomSize::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("size") : tagName.toLower());
if (m_children & Width)
- writer.writeTextElement(QStringLiteral("width"), QString::number(m_width));
+ writer.writeTextElement(u"width"_s, QString::number(m_width));
if (m_children & Height)
- writer.writeTextElement(QStringLiteral("height"), QString::number(m_height));
+ writer.writeTextElement(u"height"_s, QString::number(m_height));
writer.writeEndElement();
}
@@ -3695,19 +3709,19 @@ void DomDate::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("year"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"year"_s, Qt::CaseInsensitive)) {
setElementYear(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("month"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"month"_s, Qt::CaseInsensitive)) {
setElementMonth(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("day"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"day"_s, Qt::CaseInsensitive)) {
setElementDay(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3723,13 +3737,13 @@ void DomDate::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("date") : tagName.toLower());
if (m_children & Year)
- writer.writeTextElement(QStringLiteral("year"), QString::number(m_year));
+ writer.writeTextElement(u"year"_s, QString::number(m_year));
if (m_children & Month)
- writer.writeTextElement(QStringLiteral("month"), QString::number(m_month));
+ writer.writeTextElement(u"month"_s, QString::number(m_month));
if (m_children & Day)
- writer.writeTextElement(QStringLiteral("day"), QString::number(m_day));
+ writer.writeTextElement(u"day"_s, QString::number(m_day));
writer.writeEndElement();
}
@@ -3775,19 +3789,19 @@ void DomTime::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("hour"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"hour"_s, Qt::CaseInsensitive)) {
setElementHour(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("minute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"minute"_s, Qt::CaseInsensitive)) {
setElementMinute(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("second"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"second"_s, Qt::CaseInsensitive)) {
setElementSecond(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3803,13 +3817,13 @@ void DomTime::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("time") : tagName.toLower());
if (m_children & Hour)
- writer.writeTextElement(QStringLiteral("hour"), QString::number(m_hour));
+ writer.writeTextElement(u"hour"_s, QString::number(m_hour));
if (m_children & Minute)
- writer.writeTextElement(QStringLiteral("minute"), QString::number(m_minute));
+ writer.writeTextElement(u"minute"_s, QString::number(m_minute));
if (m_children & Second)
- writer.writeTextElement(QStringLiteral("second"), QString::number(m_second));
+ writer.writeTextElement(u"second"_s, QString::number(m_second));
writer.writeEndElement();
}
@@ -3855,31 +3869,31 @@ void DomDateTime::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("hour"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"hour"_s, Qt::CaseInsensitive)) {
setElementHour(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("minute"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"minute"_s, Qt::CaseInsensitive)) {
setElementMinute(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("second"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"second"_s, Qt::CaseInsensitive)) {
setElementSecond(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("year"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"year"_s, Qt::CaseInsensitive)) {
setElementYear(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("month"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"month"_s, Qt::CaseInsensitive)) {
setElementMonth(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("day"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"day"_s, Qt::CaseInsensitive)) {
setElementDay(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -3895,22 +3909,22 @@ void DomDateTime::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("datetime") : tagName.toLower());
if (m_children & Hour)
- writer.writeTextElement(QStringLiteral("hour"), QString::number(m_hour));
+ writer.writeTextElement(u"hour"_s, QString::number(m_hour));
if (m_children & Minute)
- writer.writeTextElement(QStringLiteral("minute"), QString::number(m_minute));
+ writer.writeTextElement(u"minute"_s, QString::number(m_minute));
if (m_children & Second)
- writer.writeTextElement(QStringLiteral("second"), QString::number(m_second));
+ writer.writeTextElement(u"second"_s, QString::number(m_second));
if (m_children & Year)
- writer.writeTextElement(QStringLiteral("year"), QString::number(m_year));
+ writer.writeTextElement(u"year"_s, QString::number(m_year));
if (m_children & Month)
- writer.writeTextElement(QStringLiteral("month"), QString::number(m_month));
+ writer.writeTextElement(u"month"_s, QString::number(m_month));
if (m_children & Day)
- writer.writeTextElement(QStringLiteral("day"), QString::number(m_day));
+ writer.writeTextElement(u"day"_s, QString::number(m_day));
writer.writeEndElement();
}
@@ -3991,34 +4005,34 @@ void DomStringList::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("notr")) {
+ if (name == u"notr"_s) {
setAttributeNotr(attribute.value().toString());
continue;
}
- if (name == QLatin1String("comment")) {
+ if (name == u"comment"_s) {
setAttributeComment(attribute.value().toString());
continue;
}
- if (name == QLatin1String("extracomment")) {
+ if (name == u"extracomment"_s) {
setAttributeExtraComment(attribute.value().toString());
continue;
}
- if (name == QLatin1String("id")) {
+ if (name == u"id"_s) {
setAttributeId(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("string"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"string"_s, Qt::CaseInsensitive)) {
m_string.append(reader.readElementText());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4034,19 +4048,19 @@ void DomStringList::write(QXmlStreamWriter &writer, const QString &tagName) cons
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("stringlist") : tagName.toLower());
if (hasAttributeNotr())
- writer.writeAttribute(QStringLiteral("notr"), attributeNotr());
+ writer.writeAttribute(u"notr"_s, attributeNotr());
if (hasAttributeComment())
- writer.writeAttribute(QStringLiteral("comment"), attributeComment());
+ writer.writeAttribute(u"comment"_s, attributeComment());
if (hasAttributeExtraComment())
- writer.writeAttribute(QStringLiteral("extracomment"), attributeExtraComment());
+ writer.writeAttribute(u"extracomment"_s, attributeExtraComment());
if (hasAttributeId())
- writer.writeAttribute(QStringLiteral("id"), attributeId());
+ writer.writeAttribute(u"id"_s, attributeId());
for (const QString &v : m_string)
- writer.writeTextElement(QStringLiteral("string"), v);
+ writer.writeTextElement(u"string"_s, v);
writer.writeEndElement();
}
@@ -4064,22 +4078,22 @@ void DomResourcePixmap::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("resource")) {
+ if (name == u"resource"_s) {
setAttributeResource(attribute.value().toString());
continue;
}
- if (name == QLatin1String("alias")) {
+ if (name == u"alias"_s) {
setAttributeAlias(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4099,10 +4113,10 @@ void DomResourcePixmap::write(QXmlStreamWriter &writer, const QString &tagName)
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("resourcepixmap") : tagName.toLower());
if (hasAttributeResource())
- writer.writeAttribute(QStringLiteral("resource"), attributeResource());
+ writer.writeAttribute(u"resource"_s, attributeResource());
if (hasAttributeAlias())
- writer.writeAttribute(QStringLiteral("alias"), attributeAlias());
+ writer.writeAttribute(u"alias"_s, attributeAlias());
if (!m_text.isEmpty())
writer.writeCharacters(m_text);
@@ -4127,70 +4141,70 @@ void DomResourceIcon::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("theme")) {
+ if (name == u"theme"_s) {
setAttributeTheme(attribute.value().toString());
continue;
}
- if (name == QLatin1String("resource")) {
+ if (name == u"resource"_s) {
setAttributeResource(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("normaloff"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"normaloff"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementNormalOff(v);
continue;
}
- if (!tag.compare(QLatin1String("normalon"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"normalon"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementNormalOn(v);
continue;
}
- if (!tag.compare(QLatin1String("disabledoff"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"disabledoff"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementDisabledOff(v);
continue;
}
- if (!tag.compare(QLatin1String("disabledon"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"disabledon"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementDisabledOn(v);
continue;
}
- if (!tag.compare(QLatin1String("activeoff"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"activeoff"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementActiveOff(v);
continue;
}
- if (!tag.compare(QLatin1String("activeon"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"activeon"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementActiveOn(v);
continue;
}
- if (!tag.compare(QLatin1String("selectedoff"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"selectedoff"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementSelectedOff(v);
continue;
}
- if (!tag.compare(QLatin1String("selectedon"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"selectedon"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementSelectedOn(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4210,34 +4224,34 @@ void DomResourceIcon::write(QXmlStreamWriter &writer, const QString &tagName) co
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("resourceicon") : tagName.toLower());
if (hasAttributeTheme())
- writer.writeAttribute(QStringLiteral("theme"), attributeTheme());
+ writer.writeAttribute(u"theme"_s, attributeTheme());
if (hasAttributeResource())
- writer.writeAttribute(QStringLiteral("resource"), attributeResource());
+ writer.writeAttribute(u"resource"_s, attributeResource());
if (m_children & NormalOff)
- m_normalOff->write(writer, QStringLiteral("normaloff"));
+ m_normalOff->write(writer, u"normaloff"_s);
if (m_children & NormalOn)
- m_normalOn->write(writer, QStringLiteral("normalon"));
+ m_normalOn->write(writer, u"normalon"_s);
if (m_children & DisabledOff)
- m_disabledOff->write(writer, QStringLiteral("disabledoff"));
+ m_disabledOff->write(writer, u"disabledoff"_s);
if (m_children & DisabledOn)
- m_disabledOn->write(writer, QStringLiteral("disabledon"));
+ m_disabledOn->write(writer, u"disabledon"_s);
if (m_children & ActiveOff)
- m_activeOff->write(writer, QStringLiteral("activeoff"));
+ m_activeOff->write(writer, u"activeoff"_s);
if (m_children & ActiveOn)
- m_activeOn->write(writer, QStringLiteral("activeon"));
+ m_activeOn->write(writer, u"activeon"_s);
if (m_children & SelectedOff)
- m_selectedOff->write(writer, QStringLiteral("selectedoff"));
+ m_selectedOff->write(writer, u"selectedoff"_s);
if (m_children & SelectedOn)
- m_selectedOn->write(writer, QStringLiteral("selectedon"));
+ m_selectedOn->write(writer, u"selectedon"_s);
if (!m_text.isEmpty())
writer.writeCharacters(m_text);
@@ -4428,30 +4442,30 @@ void DomString::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("notr")) {
+ if (name == u"notr"_s) {
setAttributeNotr(attribute.value().toString());
continue;
}
- if (name == QLatin1String("comment")) {
+ if (name == u"comment"_s) {
setAttributeComment(attribute.value().toString());
continue;
}
- if (name == QLatin1String("extracomment")) {
+ if (name == u"extracomment"_s) {
setAttributeExtraComment(attribute.value().toString());
continue;
}
- if (name == QLatin1String("id")) {
+ if (name == u"id"_s) {
setAttributeId(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4471,16 +4485,16 @@ void DomString::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("string") : tagName.toLower());
if (hasAttributeNotr())
- writer.writeAttribute(QStringLiteral("notr"), attributeNotr());
+ writer.writeAttribute(u"notr"_s, attributeNotr());
if (hasAttributeComment())
- writer.writeAttribute(QStringLiteral("comment"), attributeComment());
+ writer.writeAttribute(u"comment"_s, attributeComment());
if (hasAttributeExtraComment())
- writer.writeAttribute(QStringLiteral("extracomment"), attributeExtraComment());
+ writer.writeAttribute(u"extracomment"_s, attributeExtraComment());
if (hasAttributeId())
- writer.writeAttribute(QStringLiteral("id"), attributeId());
+ writer.writeAttribute(u"id"_s, attributeId());
if (!m_text.isEmpty())
writer.writeCharacters(m_text);
@@ -4496,15 +4510,15 @@ void DomPointF::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("x"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"x"_s, Qt::CaseInsensitive)) {
setElementX(reader.readElementText().toDouble());
continue;
}
- if (!tag.compare(QLatin1String("y"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"y"_s, Qt::CaseInsensitive)) {
setElementY(reader.readElementText().toDouble());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4520,10 +4534,10 @@ void DomPointF::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("pointf") : tagName.toLower());
if (m_children & X)
- writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x, 'f', 15));
+ writer.writeTextElement(u"x"_s, QString::number(m_x, 'f', 15));
if (m_children & Y)
- writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y, 'f', 15));
+ writer.writeTextElement(u"y"_s, QString::number(m_y, 'f', 15));
writer.writeEndElement();
}
@@ -4558,23 +4572,23 @@ void DomRectF::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("x"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"x"_s, Qt::CaseInsensitive)) {
setElementX(reader.readElementText().toDouble());
continue;
}
- if (!tag.compare(QLatin1String("y"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"y"_s, Qt::CaseInsensitive)) {
setElementY(reader.readElementText().toDouble());
continue;
}
- if (!tag.compare(QLatin1String("width"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"width"_s, Qt::CaseInsensitive)) {
setElementWidth(reader.readElementText().toDouble());
continue;
}
- if (!tag.compare(QLatin1String("height"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"height"_s, Qt::CaseInsensitive)) {
setElementHeight(reader.readElementText().toDouble());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4590,16 +4604,16 @@ void DomRectF::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("rectf") : tagName.toLower());
if (m_children & X)
- writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x, 'f', 15));
+ writer.writeTextElement(u"x"_s, QString::number(m_x, 'f', 15));
if (m_children & Y)
- writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y, 'f', 15));
+ writer.writeTextElement(u"y"_s, QString::number(m_y, 'f', 15));
if (m_children & Width)
- writer.writeTextElement(QStringLiteral("width"), QString::number(m_width, 'f', 15));
+ writer.writeTextElement(u"width"_s, QString::number(m_width, 'f', 15));
if (m_children & Height)
- writer.writeTextElement(QStringLiteral("height"), QString::number(m_height, 'f', 15));
+ writer.writeTextElement(u"height"_s, QString::number(m_height, 'f', 15));
writer.writeEndElement();
}
@@ -4656,15 +4670,15 @@ void DomSizeF::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("width"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"width"_s, Qt::CaseInsensitive)) {
setElementWidth(reader.readElementText().toDouble());
continue;
}
- if (!tag.compare(QLatin1String("height"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"height"_s, Qt::CaseInsensitive)) {
setElementHeight(reader.readElementText().toDouble());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4680,10 +4694,10 @@ void DomSizeF::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("sizef") : tagName.toLower());
if (m_children & Width)
- writer.writeTextElement(QStringLiteral("width"), QString::number(m_width, 'f', 15));
+ writer.writeTextElement(u"width"_s, QString::number(m_width, 'f', 15));
if (m_children & Height)
- writer.writeTextElement(QStringLiteral("height"), QString::number(m_height, 'f', 15));
+ writer.writeTextElement(u"height"_s, QString::number(m_height, 'f', 15));
writer.writeEndElement();
}
@@ -4718,11 +4732,11 @@ void DomChar::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("unicode"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"unicode"_s, Qt::CaseInsensitive)) {
setElementUnicode(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4738,7 +4752,7 @@ void DomChar::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("char") : tagName.toLower());
if (m_children & Unicode)
- writer.writeTextElement(QStringLiteral("unicode"), QString::number(m_unicode));
+ writer.writeTextElement(u"unicode"_s, QString::number(m_unicode));
writer.writeEndElement();
}
@@ -4765,13 +4779,13 @@ void DomUrl::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("string"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"string"_s, Qt::CaseInsensitive)) {
auto *v = new DomString();
v->read(reader);
setElementString(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -4787,7 +4801,7 @@ void DomUrl::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("url") : tagName.toLower());
if (m_children & String)
- m_string->write(writer, QStringLiteral("string"));
+ m_string->write(writer, u"string"_s);
writer.writeEndElement();
}
@@ -4900,196 +4914,196 @@ void DomProperty::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- if (name == QLatin1String("stdset")) {
+ if (name == u"stdset"_s) {
setAttributeStdset(attribute.value().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("bool"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"bool"_s, Qt::CaseInsensitive)) {
setElementBool(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("color"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"color"_s, Qt::CaseInsensitive)) {
auto *v = new DomColor();
v->read(reader);
setElementColor(v);
continue;
}
- if (!tag.compare(QLatin1String("cstring"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"cstring"_s, Qt::CaseInsensitive)) {
setElementCstring(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("cursor"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"cursor"_s, Qt::CaseInsensitive)) {
setElementCursor(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("cursorshape"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"cursorshape"_s, Qt::CaseInsensitive)) {
setElementCursorShape(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("enum"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"enum"_s, Qt::CaseInsensitive)) {
setElementEnum(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("font"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"font"_s, Qt::CaseInsensitive)) {
auto *v = new DomFont();
v->read(reader);
setElementFont(v);
continue;
}
- if (!tag.compare(QLatin1String("iconset"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"iconset"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourceIcon();
v->read(reader);
setElementIconSet(v);
continue;
}
- if (!tag.compare(QLatin1String("pixmap"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"pixmap"_s, Qt::CaseInsensitive)) {
auto *v = new DomResourcePixmap();
v->read(reader);
setElementPixmap(v);
continue;
}
- if (!tag.compare(QLatin1String("palette"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"palette"_s, Qt::CaseInsensitive)) {
auto *v = new DomPalette();
v->read(reader);
setElementPalette(v);
continue;
}
- if (!tag.compare(QLatin1String("point"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"point"_s, Qt::CaseInsensitive)) {
auto *v = new DomPoint();
v->read(reader);
setElementPoint(v);
continue;
}
- if (!tag.compare(QLatin1String("rect"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"rect"_s, Qt::CaseInsensitive)) {
auto *v = new DomRect();
v->read(reader);
setElementRect(v);
continue;
}
- if (!tag.compare(QLatin1String("set"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"set"_s, Qt::CaseInsensitive)) {
setElementSet(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("locale"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"locale"_s, Qt::CaseInsensitive)) {
auto *v = new DomLocale();
v->read(reader);
setElementLocale(v);
continue;
}
- if (!tag.compare(QLatin1String("sizepolicy"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"sizepolicy"_s, Qt::CaseInsensitive)) {
auto *v = new DomSizePolicy();
v->read(reader);
setElementSizePolicy(v);
continue;
}
- if (!tag.compare(QLatin1String("size"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"size"_s, Qt::CaseInsensitive)) {
auto *v = new DomSize();
v->read(reader);
setElementSize(v);
continue;
}
- if (!tag.compare(QLatin1String("string"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"string"_s, Qt::CaseInsensitive)) {
auto *v = new DomString();
v->read(reader);
setElementString(v);
continue;
}
- if (!tag.compare(QLatin1String("stringlist"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"stringlist"_s, Qt::CaseInsensitive)) {
auto *v = new DomStringList();
v->read(reader);
setElementStringList(v);
continue;
}
- if (!tag.compare(QLatin1String("number"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"number"_s, Qt::CaseInsensitive)) {
setElementNumber(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("float"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"float"_s, Qt::CaseInsensitive)) {
setElementFloat(reader.readElementText().toFloat());
continue;
}
- if (!tag.compare(QLatin1String("double"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"double"_s, Qt::CaseInsensitive)) {
setElementDouble(reader.readElementText().toDouble());
continue;
}
- if (!tag.compare(QLatin1String("date"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"date"_s, Qt::CaseInsensitive)) {
auto *v = new DomDate();
v->read(reader);
setElementDate(v);
continue;
}
- if (!tag.compare(QLatin1String("time"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"time"_s, Qt::CaseInsensitive)) {
auto *v = new DomTime();
v->read(reader);
setElementTime(v);
continue;
}
- if (!tag.compare(QLatin1String("datetime"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"datetime"_s, Qt::CaseInsensitive)) {
auto *v = new DomDateTime();
v->read(reader);
setElementDateTime(v);
continue;
}
- if (!tag.compare(QLatin1String("pointf"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"pointf"_s, Qt::CaseInsensitive)) {
auto *v = new DomPointF();
v->read(reader);
setElementPointF(v);
continue;
}
- if (!tag.compare(QLatin1String("rectf"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"rectf"_s, Qt::CaseInsensitive)) {
auto *v = new DomRectF();
v->read(reader);
setElementRectF(v);
continue;
}
- if (!tag.compare(QLatin1String("sizef"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"sizef"_s, Qt::CaseInsensitive)) {
auto *v = new DomSizeF();
v->read(reader);
setElementSizeF(v);
continue;
}
- if (!tag.compare(QLatin1String("longlong"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"longlong"_s, Qt::CaseInsensitive)) {
setElementLongLong(reader.readElementText().toLongLong());
continue;
}
- if (!tag.compare(QLatin1String("char"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"char"_s, Qt::CaseInsensitive)) {
auto *v = new DomChar();
v->read(reader);
setElementChar(v);
continue;
}
- if (!tag.compare(QLatin1String("url"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"url"_s, Qt::CaseInsensitive)) {
auto *v = new DomUrl();
v->read(reader);
setElementUrl(v);
continue;
}
- if (!tag.compare(QLatin1String("uint"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"uint"_s, Qt::CaseInsensitive)) {
setElementUInt(reader.readElementText().toUInt());
continue;
}
- if (!tag.compare(QLatin1String("ulonglong"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"ulonglong"_s, Qt::CaseInsensitive)) {
setElementULongLong(reader.readElementText().toULongLong());
continue;
}
- if (!tag.compare(QLatin1String("brush"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"brush"_s, Qt::CaseInsensitive)) {
auto *v = new DomBrush();
v->read(reader);
setElementBrush(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -5105,163 +5119,163 @@ void DomProperty::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("property") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
if (hasAttributeStdset())
- writer.writeAttribute(QStringLiteral("stdset"), QString::number(attributeStdset()));
+ writer.writeAttribute(u"stdset"_s, QString::number(attributeStdset()));
switch (kind()) {
case Bool:
- writer.writeTextElement(QStringLiteral("bool"), elementBool());
+ writer.writeTextElement(u"bool"_s, elementBool());
break;
case Color:
if (m_color != nullptr)
- m_color->write(writer, QStringLiteral("color"));
+ m_color->write(writer, u"color"_s);
break;
case Cstring:
- writer.writeTextElement(QStringLiteral("cstring"), elementCstring());
+ writer.writeTextElement(u"cstring"_s, elementCstring());
break;
case Cursor:
- writer.writeTextElement(QStringLiteral("cursor"), QString::number(elementCursor()));
+ writer.writeTextElement(u"cursor"_s, QString::number(elementCursor()));
break;
case CursorShape:
- writer.writeTextElement(QStringLiteral("cursorShape"), elementCursorShape());
+ writer.writeTextElement(u"cursorShape"_s, elementCursorShape());
break;
case Enum:
- writer.writeTextElement(QStringLiteral("enum"), elementEnum());
+ writer.writeTextElement(u"enum"_s, elementEnum());
break;
case Font:
if (m_font != nullptr)
- m_font->write(writer, QStringLiteral("font"));
+ m_font->write(writer, u"font"_s);
break;
case IconSet:
if (m_iconSet != nullptr)
- m_iconSet->write(writer, QStringLiteral("iconset"));
+ m_iconSet->write(writer, u"iconset"_s);
break;
case Pixmap:
if (m_pixmap != nullptr)
- m_pixmap->write(writer, QStringLiteral("pixmap"));
+ m_pixmap->write(writer, u"pixmap"_s);
break;
case Palette:
if (m_palette != nullptr)
- m_palette->write(writer, QStringLiteral("palette"));
+ m_palette->write(writer, u"palette"_s);
break;
case Point:
if (m_point != nullptr)
- m_point->write(writer, QStringLiteral("point"));
+ m_point->write(writer, u"point"_s);
break;
case Rect:
if (m_rect != nullptr)
- m_rect->write(writer, QStringLiteral("rect"));
+ m_rect->write(writer, u"rect"_s);
break;
case Set:
- writer.writeTextElement(QStringLiteral("set"), elementSet());
+ writer.writeTextElement(u"set"_s, elementSet());
break;
case Locale:
if (m_locale != nullptr)
- m_locale->write(writer, QStringLiteral("locale"));
+ m_locale->write(writer, u"locale"_s);
break;
case SizePolicy:
if (m_sizePolicy != nullptr)
- m_sizePolicy->write(writer, QStringLiteral("sizepolicy"));
+ m_sizePolicy->write(writer, u"sizepolicy"_s);
break;
case Size:
if (m_size != nullptr)
- m_size->write(writer, QStringLiteral("size"));
+ m_size->write(writer, u"size"_s);
break;
case String:
if (m_string != nullptr)
- m_string->write(writer, QStringLiteral("string"));
+ m_string->write(writer, u"string"_s);
break;
case StringList:
if (m_stringList != nullptr)
- m_stringList->write(writer, QStringLiteral("stringlist"));
+ m_stringList->write(writer, u"stringlist"_s);
break;
case Number:
- writer.writeTextElement(QStringLiteral("number"), QString::number(elementNumber()));
+ writer.writeTextElement(u"number"_s, QString::number(elementNumber()));
break;
case Float:
- writer.writeTextElement(QStringLiteral("float"), QString::number(elementFloat(), 'f', 8));
+ writer.writeTextElement(u"float"_s, QString::number(elementFloat(), 'f', 8));
break;
case Double:
- writer.writeTextElement(QStringLiteral("double"), QString::number(elementDouble(), 'f', 15));
+ writer.writeTextElement(u"double"_s, QString::number(elementDouble(), 'f', 15));
break;
case Date:
if (m_date != nullptr)
- m_date->write(writer, QStringLiteral("date"));
+ m_date->write(writer, u"date"_s);
break;
case Time:
if (m_time != nullptr)
- m_time->write(writer, QStringLiteral("time"));
+ m_time->write(writer, u"time"_s);
break;
case DateTime:
if (m_dateTime != nullptr)
- m_dateTime->write(writer, QStringLiteral("datetime"));
+ m_dateTime->write(writer, u"datetime"_s);
break;
case PointF:
if (m_pointF != nullptr)
- m_pointF->write(writer, QStringLiteral("pointf"));
+ m_pointF->write(writer, u"pointf"_s);
break;
case RectF:
if (m_rectF != nullptr)
- m_rectF->write(writer, QStringLiteral("rectf"));
+ m_rectF->write(writer, u"rectf"_s);
break;
case SizeF:
if (m_sizeF != nullptr)
- m_sizeF->write(writer, QStringLiteral("sizef"));
+ m_sizeF->write(writer, u"sizef"_s);
break;
case LongLong:
- writer.writeTextElement(QStringLiteral("longLong"), QString::number(elementLongLong()));
+ writer.writeTextElement(u"longLong"_s, QString::number(elementLongLong()));
break;
case Char:
if (m_char != nullptr)
- m_char->write(writer, QStringLiteral("char"));
+ m_char->write(writer, u"char"_s);
break;
case Url:
if (m_url != nullptr)
- m_url->write(writer, QStringLiteral("url"));
+ m_url->write(writer, u"url"_s);
break;
case UInt:
- writer.writeTextElement(QStringLiteral("UInt"), QString::number(elementUInt()));
+ writer.writeTextElement(u"UInt"_s, QString::number(elementUInt()));
break;
case ULongLong:
- writer.writeTextElement(QStringLiteral("uLongLong"), QString::number(elementULongLong()));
+ writer.writeTextElement(u"uLongLong"_s, QString::number(elementULongLong()));
break;
case Brush:
if (m_brush != nullptr)
- m_brush->write(writer, QStringLiteral("brush"));
+ m_brush->write(writer, u"brush"_s);
break;
default:
@@ -5660,13 +5674,13 @@ void DomConnections::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("connection"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"connection"_s, Qt::CaseInsensitive)) {
auto *v = new DomConnection();
v->read(reader);
m_connection.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -5682,7 +5696,7 @@ void DomConnections::write(QXmlStreamWriter &writer, const QString &tagName) con
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("connections") : tagName.toLower());
for (DomConnection *v : m_connection)
- v->write(writer, QStringLiteral("connection"));
+ v->write(writer, u"connection"_s);
writer.writeEndElement();
}
@@ -5704,29 +5718,29 @@ void DomConnection::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("sender"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"sender"_s, Qt::CaseInsensitive)) {
setElementSender(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("signal"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"signal"_s, Qt::CaseInsensitive)) {
setElementSignal(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("receiver"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"receiver"_s, Qt::CaseInsensitive)) {
setElementReceiver(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("slot"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"slot"_s, Qt::CaseInsensitive)) {
setElementSlot(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("hints"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"hints"_s, Qt::CaseInsensitive)) {
auto *v = new DomConnectionHints();
v->read(reader);
setElementHints(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -5742,19 +5756,19 @@ void DomConnection::write(QXmlStreamWriter &writer, const QString &tagName) cons
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("connection") : tagName.toLower());
if (m_children & Sender)
- writer.writeTextElement(QStringLiteral("sender"), m_sender);
+ writer.writeTextElement(u"sender"_s, m_sender);
if (m_children & Signal)
- writer.writeTextElement(QStringLiteral("signal"), m_signal);
+ writer.writeTextElement(u"signal"_s, m_signal);
if (m_children & Receiver)
- writer.writeTextElement(QStringLiteral("receiver"), m_receiver);
+ writer.writeTextElement(u"receiver"_s, m_receiver);
if (m_children & Slot)
- writer.writeTextElement(QStringLiteral("slot"), m_slot);
+ writer.writeTextElement(u"slot"_s, m_slot);
if (m_children & Hints)
- m_hints->write(writer, QStringLiteral("hints"));
+ m_hints->write(writer, u"hints"_s);
writer.writeEndElement();
}
@@ -5837,13 +5851,13 @@ void DomConnectionHints::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("hint"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"hint"_s, Qt::CaseInsensitive)) {
auto *v = new DomConnectionHint();
v->read(reader);
m_hint.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -5859,7 +5873,7 @@ void DomConnectionHints::write(QXmlStreamWriter &writer, const QString &tagName)
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("connectionhints") : tagName.toLower());
for (DomConnectionHint *v : m_hint)
- v->write(writer, QStringLiteral("hint"));
+ v->write(writer, u"hint"_s);
writer.writeEndElement();
}
@@ -5877,26 +5891,26 @@ void DomConnectionHint::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("type")) {
+ if (name == u"type"_s) {
setAttributeType(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("x"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"x"_s, Qt::CaseInsensitive)) {
setElementX(reader.readElementText().toInt());
continue;
}
- if (!tag.compare(QLatin1String("y"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"y"_s, Qt::CaseInsensitive)) {
setElementY(reader.readElementText().toInt());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -5912,13 +5926,13 @@ void DomConnectionHint::write(QXmlStreamWriter &writer, const QString &tagName)
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("connectionhint") : tagName.toLower());
if (hasAttributeType())
- writer.writeAttribute(QStringLiteral("type"), attributeType());
+ writer.writeAttribute(u"type"_s, attributeType());
if (m_children & X)
- writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x));
+ writer.writeTextElement(u"x"_s, QString::number(m_x));
if (m_children & Y)
- writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y));
+ writer.writeTextElement(u"y"_s, QString::number(m_y));
writer.writeEndElement();
}
@@ -5957,13 +5971,13 @@ void DomDesignerData::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("property"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"property"_s, Qt::CaseInsensitive)) {
auto *v = new DomProperty();
v->read(reader);
m_property.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -5979,7 +5993,7 @@ void DomDesignerData::write(QXmlStreamWriter &writer, const QString &tagName) co
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("designerdata") : tagName.toLower());
for (DomProperty *v : m_property)
- v->write(writer, QStringLiteral("property"));
+ v->write(writer, u"property"_s);
writer.writeEndElement();
}
@@ -6002,15 +6016,15 @@ void DomSlots::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("signal"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"signal"_s, Qt::CaseInsensitive)) {
m_signal.append(reader.readElementText());
continue;
}
- if (!tag.compare(QLatin1String("slot"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"slot"_s, Qt::CaseInsensitive)) {
m_slot.append(reader.readElementText());
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -6026,10 +6040,10 @@ void DomSlots::write(QXmlStreamWriter &writer, const QString &tagName) const
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("slots") : tagName.toLower());
for (const QString &v : m_signal)
- writer.writeTextElement(QStringLiteral("signal"), v);
+ writer.writeTextElement(u"signal"_s, v);
for (const QString &v : m_slot)
- writer.writeTextElement(QStringLiteral("slot"), v);
+ writer.writeTextElement(u"slot"_s, v);
writer.writeEndElement();
}
@@ -6060,19 +6074,19 @@ void DomPropertySpecifications::read(QXmlStreamReader &reader)
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- if (!tag.compare(QLatin1String("tooltip"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"tooltip"_s, Qt::CaseInsensitive)) {
auto *v = new DomPropertyToolTip();
v->read(reader);
m_tooltip.append(v);
continue;
}
- if (!tag.compare(QLatin1String("stringpropertyspecification"), Qt::CaseInsensitive)) {
+ if (!tag.compare(u"stringpropertyspecification"_s, Qt::CaseInsensitive)) {
auto *v = new DomStringPropertySpecification();
v->read(reader);
m_stringpropertyspecification.append(v);
continue;
}
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -6088,10 +6102,10 @@ void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &t
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("propertyspecifications") : tagName.toLower());
for (DomPropertyToolTip *v : m_tooltip)
- v->write(writer, QStringLiteral("tooltip"));
+ v->write(writer, u"tooltip"_s);
for (DomStringPropertySpecification *v : m_stringpropertyspecification)
- v->write(writer, QStringLiteral("stringpropertyspecification"));
+ v->write(writer, u"stringpropertyspecification"_s);
writer.writeEndElement();
}
@@ -6115,18 +6129,18 @@ void DomPropertyToolTip::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -6142,7 +6156,7 @@ void DomPropertyToolTip::write(QXmlStreamWriter &writer, const QString &tagName)
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("propertytooltip") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
writer.writeEndElement();
}
@@ -6154,26 +6168,26 @@ void DomStringPropertySpecification::read(QXmlStreamReader &reader)
const QXmlStreamAttributes &attributes = reader.attributes();
for (const QXmlStreamAttribute &attribute : attributes) {
const auto name = attribute.name();
- if (name == QLatin1String("name")) {
+ if (name == u"name"_s) {
setAttributeName(attribute.value().toString());
continue;
}
- if (name == QLatin1String("type")) {
+ if (name == u"type"_s) {
setAttributeType(attribute.value().toString());
continue;
}
- if (name == QLatin1String("notr")) {
+ if (name == u"notr"_s) {
setAttributeNotr(attribute.value().toString());
continue;
}
- reader.raiseError(QLatin1String("Unexpected attribute ") + name);
+ reader.raiseError("Unexpected attribute "_L1 + name);
}
while (!reader.hasError()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement : {
const auto tag = reader.name();
- reader.raiseError(QLatin1String("Unexpected element ") + tag);
+ reader.raiseError("Unexpected element "_L1 + tag);
}
break;
case QXmlStreamReader::EndElement :
@@ -6189,13 +6203,13 @@ void DomStringPropertySpecification::write(QXmlStreamWriter &writer, const QStri
writer.writeStartElement(tagName.isEmpty() ? QStringLiteral("stringpropertyspecification") : tagName.toLower());
if (hasAttributeName())
- writer.writeAttribute(QStringLiteral("name"), attributeName());
+ writer.writeAttribute(u"name"_s, attributeName());
if (hasAttributeType())
- writer.writeAttribute(QStringLiteral("type"), attributeType());
+ writer.writeAttribute(u"type"_s, attributeType());
if (hasAttributeNotr())
- writer.writeAttribute(QStringLiteral("notr"), attributeNotr());
+ writer.writeAttribute(u"notr"_s, attributeNotr());
writer.writeEndElement();
}
diff --git a/src/tools/uic/ui4.h b/src/tools/uic/ui4.h
index a5ac51f521..333f7f4e6a 100644
--- a/src/tools/uic/ui4.h
+++ b/src/tools/uic/ui4.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
//
// W A R N I N G
@@ -1670,6 +1645,16 @@ public:
inline bool hasElementKerning() const { return m_children & Kerning; }
void clearElementKerning();
+ inline QString elementHintingPreference() const { return m_hintingPreference; }
+ void setElementHintingPreference(const QString &a);
+ inline bool hasElementHintingPreference() const { return m_children & HintingPreference; }
+ void clearElementHintingPreference();
+
+ inline QString elementFontWeight() const { return m_fontWeight; }
+ void setElementFontWeight(const QString &a);
+ inline bool hasElementFontWeight() const { return m_children & FontWeight; }
+ void clearElementFontWeight();
+
private:
// child element data
@@ -1684,6 +1669,8 @@ private:
bool m_antialiasing = false;
QString m_styleStrategy;
bool m_kerning = false;
+ QString m_hintingPreference;
+ QString m_fontWeight;
enum Child {
Family = 1,
@@ -1695,7 +1682,9 @@ private:
StrikeOut = 64,
Antialiasing = 128,
StyleStrategy = 256,
- Kerning = 512
+ Kerning = 512,
+ HintingPreference = 1024,
+ FontWeight = 2048
};
};
diff --git a/src/tools/uic/uic.cpp b/src/tools/uic/uic.cpp
index 3601280144..fb0a37d21d 100644
--- a/src/tools/uic/uic.cpp
+++ b/src/tools/uic/uic.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "uic.h"
#include "ui4.h"
@@ -47,6 +22,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Uic::Uic(Driver *d)
: drv(d),
out(d->output()),
@@ -61,9 +38,10 @@ bool Uic::printDependencies()
QString fileName = opt.inputFile;
QFile f;
- if (fileName.isEmpty())
- f.open(stdin, QIODevice::ReadOnly);
- else {
+ if (fileName.isEmpty()) {
+ if (!f.open(stdin, QIODevice::ReadOnly))
+ return false;
+ } else {
f.setFileName(fileName);
if (!f.open(QIODevice::ReadOnly))
return false;
@@ -127,7 +105,7 @@ void Uic::writeCopyrightHeaderCpp(const DomUI *ui) const
static inline bool isCppCommentChar(QChar c)
{
- return c == QLatin1Char('/') || c == QLatin1Char('*');
+ return c == u'/' || c == u'*';
}
static int leadingCppCommentCharCount(QStringView s)
@@ -142,13 +120,13 @@ void Uic::writeCopyrightHeaderPython(const DomUI *ui) const
{
QString comment = ui->elementComment();
if (!comment.isEmpty()) {
- const auto lines = QStringView{comment}.split(QLatin1Char('\n'));
+ const auto lines = QStringView{comment}.split(u'\n');
for (const auto &line : lines) {
if (const int leadingCommentChars = leadingCppCommentCharCount(line)) {
out << language::repeat(leadingCommentChars, '#')
<< line.right(line.size() - leadingCommentChars);
} else {
- if (!line.startsWith(QLatin1Char('#')))
+ if (!line.startsWith(u'#'))
out << "# ";
out << line;
}
@@ -169,7 +147,7 @@ void Uic::writeCopyrightHeaderPython(const DomUI *ui) const
static double versionFromUiAttribute(QXmlStreamReader &reader)
{
const QXmlStreamAttributes attributes = reader.attributes();
- const QString versionAttribute = QLatin1String("version");
+ const auto versionAttribute = "version"_L1;
if (!attributes.hasAttribute(versionAttribute))
return 4.0;
const QStringView version = attributes.value(versionAttribute);
@@ -180,7 +158,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
{
DomUI *ui = nullptr;
- const QString uiElement = QLatin1String("ui");
+ const auto uiElement = "ui"_L1;
while (!reader.atEnd()) {
if (reader.readNext() == QXmlStreamReader::StartElement) {
if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0
@@ -195,7 +173,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
ui = new DomUI();
ui->read(reader);
} else {
- reader.raiseError(QLatin1String("Unexpected element ") + reader.name().toString());
+ reader.raiseError("Unexpected element "_L1 + reader.name().toString());
}
}
}
@@ -231,7 +209,7 @@ bool Uic::write(QIODevice *in)
const QString &language = ui->attributeLanguage();
driver()->setUseIdBasedTranslations(ui->attributeIdbasedtr());
- if (!language.isEmpty() && language.compare(QLatin1String("c++"), Qt::CaseInsensitive) != 0) {
+ if (!language.isEmpty() && language.compare("c++"_L1, Qt::CaseInsensitive) != 0) {
fprintf(stderr, "uic: File is not a \"c++\" ui file, language=%s\n", qPrintable(language));
return false;
}
@@ -266,8 +244,7 @@ bool Uic::write(DomUI *ui)
}
pixFunction = ui->elementPixmapFunction();
- if (pixFunction == QLatin1String("QPixmap::fromMimeSource")
- || pixFunction == QLatin1String("qPixmapFromMimeSource")) {
+ if (pixFunction == "QPixmap::fromMimeSource"_L1 || pixFunction == "qPixmapFromMimeSource"_L1) {
fprintf(stderr, "%s: Warning: Obsolete pixmap function '%s' specified in the UI file.\n",
qPrintable(opt.messagePrefix()), qPrintable(pixFunction));
pixFunction.clear();
@@ -315,9 +292,9 @@ void Uic::writeHeaderProtectionEnd()
bool Uic::isButton(const QString &className) const
{
static const QStringList buttons = {
- QLatin1String("QRadioButton"), QLatin1String("QToolButton"),
- QLatin1String("QCheckBox"), QLatin1String("QPushButton"),
- QLatin1String("QCommandLinkButton")
+ u"QRadioButton"_s, u"QToolButton"_s,
+ u"QCheckBox"_s, u"QPushButton"_s,
+ u"QCommandLinkButton"_s
};
return customWidgetsInfo()->extendsOneOf(className, buttons);
}
@@ -325,10 +302,10 @@ bool Uic::isButton(const QString &className) const
bool Uic::isContainer(const QString &className) const
{
static const QStringList containers = {
- QLatin1String("QStackedWidget"), QLatin1String("QToolBox"),
- QLatin1String("QTabWidget"), QLatin1String("QScrollArea"),
- QLatin1String("QMdiArea"), QLatin1String("QWizard"),
- QLatin1String("QDockWidget")
+ u"QStackedWidget"_s, u"QToolBox"_s,
+ u"QTabWidget"_s, u"QScrollArea"_s,
+ u"QMdiArea"_s, u"QWizard"_s,
+ u"QDockWidget"_s
};
return customWidgetsInfo()->extendsOneOf(className, containers);
@@ -337,7 +314,7 @@ bool Uic::isContainer(const QString &className) const
bool Uic::isMenu(const QString &className) const
{
static const QStringList menus = {
- QLatin1String("QMenu"), QLatin1String("QPopupMenu")
+ u"QMenu"_s, u"QPopupMenu"_s
};
return customWidgetsInfo()->extendsOneOf(className, menus);
}
diff --git a/src/tools/uic/uic.h b/src/tools/uic/uic.h
index f3dfd49149..ef5f0a90bd 100644
--- a/src/tools/uic/uic.h
+++ b/src/tools/uic/uic.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef UIC_H
#define UIC_H
diff --git a/src/tools/uic/utils.h b/src/tools/uic/utils.h
index 34c4ab23d4..37247cdea6 100644
--- a/src/tools/uic/utils.h
+++ b/src/tools/uic/utils.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef UTILS_H
#define UTILS_H
@@ -37,7 +12,7 @@
QT_BEGIN_NAMESPACE
inline bool toBool(const QString &str)
-{ return str.toLower() == QLatin1String("true"); }
+{ return QString::compare(str, QLatin1StringView("true"), Qt::CaseInsensitive) == 0; }
inline QString toString(const DomString *str)
{ return str ? str->text() : QString(); }
diff --git a/src/tools/uic/validator.cpp b/src/tools/uic/validator.cpp
index d3b1300ee7..c9f8c0a496 100644
--- a/src/tools/uic/validator.cpp
+++ b/src/tools/uic/validator.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "validator.h"
#include "driver.h"
diff --git a/src/tools/uic/validator.h b/src/tools/uic/validator.h
index f7f7401991..6f965b84d5 100644
--- a/src/tools/uic/validator.h
+++ b/src/tools/uic/validator.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef VALIDATOR_H
#define VALIDATOR_H
diff --git a/src/tools/windeployqt/CMakeLists.txt b/src/tools/windeployqt/CMakeLists.txt
new file mode 100644
index 0000000000..2e50116484
--- /dev/null
+++ b/src/tools/windeployqt/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## windeployqt Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name windeployqt)
+qt_internal_add_tool(${target_name}
+ TOOLS_TARGET Core
+ USER_FACING
+ INSTALL_VERSIONED_LINK
+ TARGET_DESCRIPTION "Qt Windows Deployment Tool"
+ SOURCES
+ qmlutils.cpp qmlutils.h
+ qtmoduleinfo.cpp qtmoduleinfo.h
+ qtplugininfo.cpp qtplugininfo.h
+ utils.cpp utils.h
+ main.cpp
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ QT_NO_FOREACH
+ QT_NO_QPAIR
+ LIBRARIES
+ Qt::CorePrivate
+)
+qt_internal_return_unless_building_tools()
+
+qt_internal_extend_target(${target_name} CONDITION WIN32
+ PUBLIC_LIBRARIES
+ shlwapi
+)
diff --git a/src/tools/windeployqt/main.cpp b/src/tools/windeployqt/main.cpp
new file mode 100644
index 0000000000..084345a4d8
--- /dev/null
+++ b/src/tools/windeployqt/main.cpp
@@ -0,0 +1,2007 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "utils.h"
+#include "qmlutils.h"
+#include "qtmoduleinfo.h"
+#include "qtplugininfo.h"
+
+#include <QtCore/QCommandLineOption>
+#include <QtCore/QCommandLineParser>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QList>
+#include <QtCore/QOperatingSystemVersion>
+#include <QtCore/QSharedPointer>
+
+#ifdef Q_OS_WIN
+#include <QtCore/qt_windows.h>
+#else
+#define IMAGE_FILE_MACHINE_ARM64 0xaa64
+#endif
+
+#include <QtCore/private/qconfig_p.h>
+
+#include <algorithm>
+#include <cstdio>
+#include <iostream>
+#include <iterator>
+#include <unordered_map>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static QtModuleInfoStore qtModuleEntries;
+
+#define DECLARE_KNOWN_MODULE(name) \
+ static size_t Qt##name ## ModuleId = QtModule::InvalidId
+
+DECLARE_KNOWN_MODULE(3DQuick);
+DECLARE_KNOWN_MODULE(Core);
+DECLARE_KNOWN_MODULE(Designer);
+DECLARE_KNOWN_MODULE(DesignerComponents);
+DECLARE_KNOWN_MODULE(Gui);
+DECLARE_KNOWN_MODULE(Qml);
+DECLARE_KNOWN_MODULE(QmlTooling);
+DECLARE_KNOWN_MODULE(Quick);
+DECLARE_KNOWN_MODULE(WebEngineCore);
+DECLARE_KNOWN_MODULE(Widgets);
+
+#define DEFINE_KNOWN_MODULE(name) \
+ m[QLatin1String("Qt6" #name)] = &Qt##name ## ModuleId
+
+static void assignKnownModuleIds()
+{
+ std::unordered_map<QString, size_t *> m;
+ DEFINE_KNOWN_MODULE(3DQuick);
+ DEFINE_KNOWN_MODULE(Core);
+ DEFINE_KNOWN_MODULE(Designer);
+ DEFINE_KNOWN_MODULE(DesignerComponents);
+ DEFINE_KNOWN_MODULE(Gui);
+ DEFINE_KNOWN_MODULE(Qml);
+ DEFINE_KNOWN_MODULE(QmlTooling);
+ DEFINE_KNOWN_MODULE(Quick);
+ DEFINE_KNOWN_MODULE(WebEngineCore);
+ DEFINE_KNOWN_MODULE(Widgets);
+ for (size_t i = 0; i < qtModuleEntries.size(); ++i) {
+ const QtModule &module = qtModuleEntries.moduleById(i);
+ auto it = m.find(module.name);
+ if (it == m.end())
+ continue;
+ *(it->second) = i;
+ }
+}
+
+#undef DECLARE_KNOWN_MODULE
+#undef DEFINE_KNOWN_MODULE
+
+static const char webEngineProcessC[] = "QtWebEngineProcess";
+
+static inline QString webProcessBinary(const char *binaryName, Platform p)
+{
+ const QString webProcess = QLatin1StringView(binaryName);
+ return (p & WindowsBased) ? webProcess + QStringLiteral(".exe") : webProcess;
+}
+
+static QString moduleNameToOptionName(const QString &moduleName)
+{
+ QString result = moduleName
+ .mid(3) // strip the "Qt6" prefix
+ .toLower();
+ if (result == u"help"_s)
+ result.prepend("qt"_L1);
+ return result;
+}
+
+static QByteArray formatQtModules(const ModuleBitset &mask, bool option = false)
+{
+ QByteArray result;
+ for (const auto &qtModule : qtModuleEntries) {
+ if (mask.test(qtModule.id)) {
+ if (!result.isEmpty())
+ result.append(' ');
+ result.append(option
+ ? moduleNameToOptionName(qtModule.name).toUtf8()
+ : qtModule.name.toUtf8());
+ if (qtModule.internal)
+ result.append("Internal");
+ }
+ }
+ return result;
+}
+
+static QString formatQtPlugins(const PluginInformation &pluginInfo)
+{
+ QString result(u'\n');
+ for (const auto &pair : pluginInfo.typeMap()) {
+ result += pair.first;
+ result += u": \n";
+ for (const QString &plugin : pair.second) {
+ result += u" ";
+ result += plugin;
+ result += u'\n';
+ }
+ }
+ return result;
+}
+
+static Platform platformFromMkSpec(const QString &xSpec)
+{
+ if (xSpec.startsWith("win32-"_L1)) {
+ if (xSpec.contains("clang-g++"_L1))
+ return WindowsDesktopClangMinGW;
+ if (xSpec.contains("clang-msvc++"_L1))
+ return WindowsDesktopClangMsvc;
+ if (xSpec.contains("arm"_L1))
+ return WindowsDesktopMsvcArm;
+
+ return xSpec.contains("g++"_L1) ? WindowsDesktopMinGW : WindowsDesktopMsvcIntel;
+ }
+ return UnknownPlatform;
+}
+
+// Helpers for exclusive options, "-foo", "--no-foo"
+enum ExlusiveOptionValue {
+ OptionAuto,
+ OptionEnabled,
+ OptionDisabled
+};
+
+static ExlusiveOptionValue parseExclusiveOptions(const QCommandLineParser *parser,
+ const QCommandLineOption &enableOption,
+ const QCommandLineOption &disableOption)
+{
+ const bool enabled = parser->isSet(enableOption);
+ const bool disabled = parser->isSet(disableOption);
+ if (enabled) {
+ if (disabled) {
+ std::wcerr << "Warning: both -" << enableOption.names().first()
+ << " and -" << disableOption.names().first() << " were specified, defaulting to -"
+ << enableOption.names().first() << ".\n";
+ }
+ return OptionEnabled;
+ }
+ return disabled ? OptionDisabled : OptionAuto;
+}
+
+struct Options {
+ enum DebugDetection {
+ DebugDetectionAuto,
+ DebugDetectionForceDebug,
+ DebugDetectionForceRelease
+ };
+
+ bool plugins = true;
+ bool libraries = true;
+ bool quickImports = true;
+ bool translations = true;
+ bool systemD3dCompiler = true;
+ bool systemDxc = true;
+ bool compilerRunTime = false;
+ bool softwareRasterizer = true;
+ bool ffmpeg = true;
+ PluginSelections pluginSelections;
+ Platform platform = WindowsDesktopMsvcIntel;
+ ModuleBitset additionalLibraries;
+ ModuleBitset disabledLibraries;
+ unsigned updateFileFlags = 0;
+ QStringList qmlDirectories; // Project's QML files.
+ QStringList qmlImportPaths; // Custom QML module locations.
+ QString directory;
+ QString qtpathsBinary;
+ QString translationsDirectory; // Translations target directory
+ QStringList languages;
+ QString libraryDirectory;
+ QString pluginDirectory;
+ QString openSslRootDirectory;
+ QString qmlDirectory;
+ QStringList binaries;
+ JsonOutput *json = nullptr;
+ ListOption list = ListNone;
+ DebugDetection debugDetection = DebugDetectionAuto;
+ bool deployPdb = false;
+ bool dryRun = false;
+ bool patchQt = true;
+ bool ignoreLibraryErrors = false;
+ bool deployInsightTrackerPlugin = false;
+ bool forceOpenSslPlugin = false;
+};
+
+// Return binary to be deployed from folder, ignore pre-existing web engine process.
+static inline QString findBinary(const QString &directory, Platform platform)
+{
+ const QStringList nameFilters = (platform & WindowsBased) ?
+ QStringList(QStringLiteral("*.exe")) : QStringList();
+ const QFileInfoList &binaries =
+ QDir(QDir::cleanPath(directory)).entryInfoList(nameFilters, QDir::Files | QDir::Executable);
+ for (const QFileInfo &binaryFi : binaries) {
+ const QString binary = binaryFi.fileName();
+ if (!binary.contains(QLatin1StringView(webEngineProcessC), Qt::CaseInsensitive)) {
+ return binaryFi.absoluteFilePath();
+ }
+ }
+ return QString();
+}
+
+static QString msgFileDoesNotExist(const QString & file)
+{
+ return u'"' + QDir::toNativeSeparators(file) + "\" does not exist."_L1;
+}
+
+enum CommandLineParseFlag {
+ CommandLineParseError = 0x1,
+ CommandLineParseHelpRequested = 0x2,
+ CommandLineVersionRequested = 0x4
+};
+
+static QCommandLineOption createQMakeOption()
+{
+ return {
+ u"qmake"_s,
+ u"Use specified qmake instead of qmake from PATH. Deprecated, use qtpaths instead."_s,
+ u"path"_s
+ };
+}
+
+static QCommandLineOption createQtPathsOption()
+{
+ return {
+ u"qtpaths"_s,
+ u"Use specified qtpaths.exe instead of qtpaths.exe from PATH."_s,
+ u"path"_s
+ };
+}
+
+static QCommandLineOption createVerboseOption()
+{
+ return {
+ u"verbose"_s,
+ u"Verbose level (0-2)."_s,
+ u"level"_s
+ };
+}
+
+static int parseEarlyArguments(const QStringList &arguments, Options *options,
+ QString *errorMessage)
+{
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+
+ QCommandLineOption qmakeOption = createQMakeOption();
+ parser.addOption(qmakeOption);
+
+ QCommandLineOption qtpathsOption = createQtPathsOption();
+ parser.addOption(qtpathsOption);
+
+ QCommandLineOption verboseOption = createVerboseOption();
+ parser.addOption(verboseOption);
+
+ // Deliberately don't check for errors. We want to ignore options we don't know about.
+ parser.parse(arguments);
+
+ if (parser.isSet(qmakeOption) && parser.isSet(qtpathsOption)) {
+ *errorMessage = QStringLiteral("-qmake and -qtpaths are mutually exclusive.");
+ return CommandLineParseError;
+ }
+
+ if (parser.isSet(qmakeOption) && optVerboseLevel >= 1)
+ std::wcerr << "Warning: -qmake option is deprecated. Use -qtpaths instead.\n";
+
+ if (parser.isSet(qtpathsOption) || parser.isSet(qmakeOption)) {
+ const QString qtpathsArg = parser.isSet(qtpathsOption) ? parser.value(qtpathsOption)
+ : parser.value(qmakeOption);
+
+ const QString qtpathsBinary = QDir::cleanPath(qtpathsArg);
+ const QFileInfo fi(qtpathsBinary);
+ if (!fi.exists()) {
+ *errorMessage = msgFileDoesNotExist(qtpathsBinary);
+ return CommandLineParseError;
+ }
+
+ if (!fi.isExecutable()) {
+ *errorMessage = u'"' + QDir::toNativeSeparators(qtpathsBinary)
+ + QStringLiteral("\" is not an executable.");
+ return CommandLineParseError;
+ }
+ options->qtpathsBinary = qtpathsBinary;
+ }
+
+ if (parser.isSet(verboseOption)) {
+ bool ok;
+ const QString value = parser.value(verboseOption);
+ optVerboseLevel = value.toInt(&ok);
+ if (!ok || optVerboseLevel < 0) {
+ *errorMessage = QStringLiteral("Invalid value \"%1\" passed for verbose level.")
+ .arg(value);
+ return CommandLineParseError;
+ }
+ }
+
+ return 0;
+}
+
+static inline int parseArguments(const QStringList &arguments, QCommandLineParser *parser,
+ Options *options, QString *errorMessage)
+{
+ using CommandLineOptionPtr = QSharedPointer<QCommandLineOption>;
+ using OptionPtrVector = QList<CommandLineOptionPtr>;
+
+ parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ parser->setApplicationDescription(u"Qt Deploy Tool " QT_VERSION_STR
+ "\n\nThe simplest way to use windeployqt is to add the bin directory of your Qt\n"
+ "installation (e.g. <QT_DIR\\bin>) to the PATH variable and then run:\n windeployqt <path-to-app-binary>\n\n"
+ "If your application uses Qt Quick, run:\n windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>"_s);
+ const QCommandLineOption helpOption = parser->addHelpOption();
+ const QCommandLineOption versionOption = parser->addVersionOption();
+
+ QCommandLineOption dirOption(QStringLiteral("dir"),
+ QStringLiteral("Use directory instead of binary directory."),
+ QStringLiteral("directory"));
+ parser->addOption(dirOption);
+
+ // Add early options to have them available in the help text.
+ parser->addOption(createQMakeOption());
+ parser->addOption(createQtPathsOption());
+
+ QCommandLineOption libDirOption(QStringLiteral("libdir"),
+ QStringLiteral("Copy libraries to path."),
+ QStringLiteral("path"));
+ parser->addOption(libDirOption);
+
+ QCommandLineOption pluginDirOption(QStringLiteral("plugindir"),
+ QStringLiteral("Copy plugins to path."),
+ QStringLiteral("path"));
+ parser->addOption(pluginDirOption);
+
+ const QCommandLineOption translationDirOption(
+ u"translationdir"_s,
+ u"Copy translations to path."_s,
+ u"path"_s);
+ parser->addOption(translationDirOption);
+
+ QCommandLineOption qmlDeployDirOption(QStringLiteral("qml-deploy-dir"),
+ QStringLiteral("Copy qml files to path."),
+ QStringLiteral("path"));
+ parser->addOption(qmlDeployDirOption);
+
+ QCommandLineOption debugOption(QStringLiteral("debug"),
+ QStringLiteral("Assume debug binaries."));
+ parser->addOption(debugOption);
+ QCommandLineOption releaseOption(QStringLiteral("release"),
+ QStringLiteral("Assume release binaries."));
+ parser->addOption(releaseOption);
+ QCommandLineOption releaseWithDebugInfoOption(QStringLiteral("release-with-debug-info"),
+ QStringLiteral("Assume release binaries with debug information."));
+ releaseWithDebugInfoOption.setFlags(QCommandLineOption::HiddenFromHelp); // Deprecated by improved debug detection.
+ parser->addOption(releaseWithDebugInfoOption);
+
+ QCommandLineOption deployPdbOption(QStringLiteral("pdb"),
+ QStringLiteral("Deploy .pdb files (MSVC)."));
+ parser->addOption(deployPdbOption);
+
+ QCommandLineOption forceOption(QStringLiteral("force"),
+ QStringLiteral("Force updating files."));
+ parser->addOption(forceOption);
+
+ QCommandLineOption dryRunOption(QStringLiteral("dry-run"),
+ QStringLiteral("Simulation mode. Behave normally, but do not copy/update any files."));
+ parser->addOption(dryRunOption);
+
+ QCommandLineOption noPatchQtOption(QStringLiteral("no-patchqt"),
+ QStringLiteral("Do not patch the Qt6Core library."));
+ parser->addOption(noPatchQtOption);
+
+ QCommandLineOption ignoreErrorOption(QStringLiteral("ignore-library-errors"),
+ QStringLiteral("Ignore errors when libraries cannot be found."));
+ parser->addOption(ignoreErrorOption);
+
+ QCommandLineOption noPluginsOption(QStringLiteral("no-plugins"),
+ QStringLiteral("Skip plugin deployment."));
+ parser->addOption(noPluginsOption);
+
+ QCommandLineOption includeSoftPluginsOption(QStringLiteral("include-soft-plugins"),
+ QStringLiteral("Include in the deployment all relevant plugins by taking into account all soft dependencies."));
+ parser->addOption(includeSoftPluginsOption);
+
+ QCommandLineOption skipPluginTypesOption(QStringLiteral("skip-plugin-types"),
+ QStringLiteral("A comma-separated list of plugin types that are not deployed (qmltooling,generic)."),
+ QStringLiteral("plugin types"));
+ parser->addOption(skipPluginTypesOption);
+
+ QCommandLineOption addPluginTypesOption(QStringLiteral("add-plugin-types"),
+ QStringLiteral("A comma-separated list of plugin types that will be added to deployment (imageformats,iconengines)"),
+ QStringLiteral("plugin types"));
+ parser->addOption(addPluginTypesOption);
+
+ QCommandLineOption includePluginsOption(QStringLiteral("include-plugins"),
+ QStringLiteral("A comma-separated list of individual plugins that will be added to deployment (scene2d,qjpeg)"),
+ QStringLiteral("plugins"));
+ parser->addOption(includePluginsOption);
+
+ QCommandLineOption excludePluginsOption(QStringLiteral("exclude-plugins"),
+ QStringLiteral("A comma-separated list of individual plugins that will not be deployed (qsvg,qpdf)"),
+ QStringLiteral("plugins"));
+ parser->addOption(excludePluginsOption);
+
+ QCommandLineOption noLibraryOption(QStringLiteral("no-libraries"),
+ QStringLiteral("Skip library deployment."));
+ parser->addOption(noLibraryOption);
+
+ QCommandLineOption qmlDirOption(QStringLiteral("qmldir"),
+ QStringLiteral("Scan for QML-imports starting from directory."),
+ QStringLiteral("directory"));
+ parser->addOption(qmlDirOption);
+
+ QCommandLineOption qmlImportOption(QStringLiteral("qmlimport"),
+ QStringLiteral("Add the given path to the QML module search locations."),
+ QStringLiteral("directory"));
+ parser->addOption(qmlImportOption);
+
+ QCommandLineOption noQuickImportOption(QStringLiteral("no-quick-import"),
+ QStringLiteral("Skip deployment of Qt Quick imports."));
+ parser->addOption(noQuickImportOption);
+
+
+ QCommandLineOption translationOption(QStringLiteral("translations"),
+ QStringLiteral("A comma-separated list of languages to deploy (de,fi)."),
+ QStringLiteral("languages"));
+ parser->addOption(translationOption);
+
+ QCommandLineOption noTranslationOption(QStringLiteral("no-translations"),
+ QStringLiteral("Skip deployment of translations."));
+ parser->addOption(noTranslationOption);
+
+ QCommandLineOption noSystemD3DCompilerOption(QStringLiteral("no-system-d3d-compiler"),
+ QStringLiteral("Skip deployment of the system D3D compiler."));
+ parser->addOption(noSystemD3DCompilerOption);
+
+ QCommandLineOption noSystemDxcOption(QStringLiteral("no-system-dxc-compiler"),
+ QStringLiteral("Skip deployment of the system DXC (dxcompiler.dll, dxil.dll)."));
+ parser->addOption(noSystemDxcOption);
+
+
+ QCommandLineOption compilerRunTimeOption(QStringLiteral("compiler-runtime"),
+ QStringLiteral("Deploy compiler runtime (Desktop only)."));
+ parser->addOption(compilerRunTimeOption);
+
+ QCommandLineOption noCompilerRunTimeOption(QStringLiteral("no-compiler-runtime"),
+ QStringLiteral("Do not deploy compiler runtime (Desktop only)."));
+ parser->addOption(noCompilerRunTimeOption);
+
+ QCommandLineOption jsonOption(QStringLiteral("json"),
+ QStringLiteral("Print to stdout in JSON format."));
+ parser->addOption(jsonOption);
+
+ QCommandLineOption suppressSoftwareRasterizerOption(QStringLiteral("no-opengl-sw"),
+ QStringLiteral("Do not deploy the software rasterizer library."));
+ parser->addOption(suppressSoftwareRasterizerOption);
+
+ QCommandLineOption noFFmpegOption(QStringLiteral("no-ffmpeg"),
+ QStringLiteral("Do not deploy the FFmpeg libraries."));
+ parser->addOption(noFFmpegOption);
+
+ QCommandLineOption forceOpenSslOption(QStringLiteral("force-openssl"),
+ QStringLiteral("Deploy openssl plugin but ignore openssl library dependency"));
+ parser->addOption(forceOpenSslOption);
+
+ QCommandLineOption openSslRootOption(QStringLiteral("openssl-root"),
+ QStringLiteral("Directory containing openSSL libraries."),
+ QStringLiteral("directory"));
+ parser->addOption(openSslRootOption);
+
+
+ QCommandLineOption listOption(QStringLiteral("list"),
+ "Print only the names of the files copied.\n"
+ "Available options:\n"
+ " source: absolute path of the source files\n"
+ " target: absolute path of the target files\n"
+ " relative: paths of the target files, relative\n"
+ " to the target directory\n"
+ " mapping: outputs the source and the relative\n"
+ " target, suitable for use within an\n"
+ " Appx mapping file"_L1,
+ QStringLiteral("option"));
+ parser->addOption(listOption);
+
+ // Add early option to have it available in the help text.
+ parser->addOption(createVerboseOption());
+
+ parser->addPositionalArgument(QStringLiteral("[files]"),
+ QStringLiteral("Binaries or directory containing the binary."));
+
+ QCommandLineOption deployInsightTrackerOption(QStringLiteral("deploy-insighttracker"),
+ QStringLiteral("Deploy insight tracker plugin."));
+ // The option will be added to the parser if the module is available (see block below)
+ bool insightTrackerModuleAvailable = false;
+
+ OptionPtrVector enabledModuleOptions;
+ OptionPtrVector disabledModuleOptions;
+ const size_t qtModulesCount = qtModuleEntries.size();
+ enabledModuleOptions.reserve(qtModulesCount);
+ disabledModuleOptions.reserve(qtModulesCount);
+ for (const QtModule &module : qtModuleEntries) {
+ const QString option = moduleNameToOptionName(module.name);
+ const QString name = module.name;
+ if (name == u"InsightTracker") {
+ parser->addOption(deployInsightTrackerOption);
+ insightTrackerModuleAvailable = true;
+ }
+ const QString enabledDescription = QStringLiteral("Add ") + name + QStringLiteral(" module.");
+ CommandLineOptionPtr enabledOption(new QCommandLineOption(option, enabledDescription));
+ parser->addOption(*enabledOption.data());
+ enabledModuleOptions.append(enabledOption);
+ const QString disabledDescription = QStringLiteral("Remove ") + name + QStringLiteral(" module.");
+ CommandLineOptionPtr disabledOption(new QCommandLineOption(QStringLiteral("no-") + option,
+ disabledDescription));
+ disabledModuleOptions.append(disabledOption);
+ parser->addOption(*disabledOption.data());
+ }
+
+ const bool success = parser->parse(arguments);
+ if (parser->isSet(helpOption))
+ return CommandLineParseHelpRequested;
+ if (parser->isSet(versionOption))
+ return CommandLineVersionRequested;
+ if (!success) {
+ *errorMessage = parser->errorText();
+ return CommandLineParseError;
+ }
+
+ options->libraryDirectory = parser->value(libDirOption);
+ options->pluginDirectory = parser->value(pluginDirOption);
+ options->translationsDirectory = parser->value(translationDirOption);
+ options->qmlDirectory = parser->value(qmlDeployDirOption);
+ options->plugins = !parser->isSet(noPluginsOption);
+ options->libraries = !parser->isSet(noLibraryOption);
+ options->translations = !parser->isSet(noTranslationOption);
+ if (parser->isSet(translationOption))
+ options->languages = parser->value(translationOption).split(u',');
+ options->systemD3dCompiler = !parser->isSet(noSystemD3DCompilerOption);
+ options->systemDxc = !parser->isSet(noSystemDxcOption);
+ options->quickImports = !parser->isSet(noQuickImportOption);
+
+ // default to deployment of compiler runtime for windows desktop configurations
+ if (options->platform == WindowsDesktopMinGW || options->platform.testFlags(WindowsDesktopMsvc)
+ || parser->isSet(compilerRunTimeOption))
+ options->compilerRunTime = true;
+ if (parser->isSet(noCompilerRunTimeOption))
+ options->compilerRunTime = false;
+
+ if (options->compilerRunTime && options->platform != WindowsDesktopMinGW
+ && !options->platform.testFlags(WindowsDesktopMsvc)) {
+ *errorMessage = QStringLiteral("Deployment of the compiler runtime is implemented for Desktop MSVC/g++ only.");
+ return CommandLineParseError;
+ }
+
+ options->pluginSelections.includeSoftPlugins = parser->isSet(includeSoftPluginsOption);
+
+ if (parser->isSet(skipPluginTypesOption))
+ options->pluginSelections.disabledPluginTypes = parser->value(skipPluginTypesOption).split(u',');
+
+ if (parser->isSet(addPluginTypesOption))
+ options->pluginSelections.enabledPluginTypes = parser->value(addPluginTypesOption).split(u',');
+
+ if (parser->isSet(includePluginsOption))
+ options->pluginSelections.includedPlugins = parser->value(includePluginsOption).split(u',');
+
+ if (parser->isSet(excludePluginsOption))
+ options->pluginSelections.excludedPlugins = parser->value(excludePluginsOption).split(u',');
+
+ if (parser->isSet(releaseWithDebugInfoOption))
+ std::wcerr << "Warning: " << releaseWithDebugInfoOption.names().first() << " is obsolete.";
+
+ switch (parseExclusiveOptions(parser, debugOption, releaseOption)) {
+ case OptionAuto:
+ break;
+ case OptionEnabled:
+ options->debugDetection = Options::DebugDetectionForceDebug;
+ break;
+ case OptionDisabled:
+ options->debugDetection = Options::DebugDetectionForceRelease;
+ break;
+ }
+
+ if (parser->isSet(deployPdbOption)) {
+ if (options->platform.testFlag(WindowsBased) && !options->platform.testFlag(MinGW))
+ options->deployPdb = true;
+ else
+ std::wcerr << "Warning: --" << deployPdbOption.names().first() << " is not supported on this platform.\n";
+ }
+
+ if (parser->isSet(suppressSoftwareRasterizerOption))
+ options->softwareRasterizer = false;
+
+ if (parser->isSet(noFFmpegOption))
+ options->ffmpeg = false;
+
+ if (parser->isSet(forceOpenSslOption))
+ options->forceOpenSslPlugin = true;
+
+ if (parser->isSet(openSslRootOption))
+ options->openSslRootDirectory = parser->value(openSslRootOption);
+
+ if (options->forceOpenSslPlugin && !options->openSslRootDirectory.isEmpty()) {
+ *errorMessage = QStringLiteral("force-openssl and openssl-root are mutually exclusive");
+ return CommandLineParseError;
+ }
+
+ if (parser->isSet(forceOption))
+ options->updateFileFlags |= ForceUpdateFile;
+ if (parser->isSet(dryRunOption)) {
+ options->dryRun = true;
+ options->updateFileFlags |= SkipUpdateFile;
+ }
+
+ options->patchQt = !parser->isSet(noPatchQtOption);
+ options->ignoreLibraryErrors = parser->isSet(ignoreErrorOption);
+ if (insightTrackerModuleAvailable)
+ options->deployInsightTrackerPlugin = parser->isSet(deployInsightTrackerOption);
+
+ for (const QtModule &module : qtModuleEntries) {
+ if (parser->isSet(*enabledModuleOptions.at(module.id)))
+ options->additionalLibraries[module.id] = 1;
+ if (parser->isSet(*disabledModuleOptions.at(module.id)))
+ options->disabledLibraries[module.id] = 1;
+ }
+
+ // Add some dependencies
+ if (options->additionalLibraries.test(QtQuickModuleId))
+ options->additionalLibraries[QtQmlModuleId] = 1;
+ if (options->additionalLibraries.test(QtDesignerComponentsModuleId))
+ options->additionalLibraries[QtDesignerModuleId] = 1;
+
+ if (parser->isSet(listOption)) {
+ const QString value = parser->value(listOption);
+ if (value == QStringLiteral("source")) {
+ options->list = ListSource;
+ } else if (value == QStringLiteral("target")) {
+ options->list = ListTarget;
+ } else if (value == QStringLiteral("relative")) {
+ options->list = ListRelative;
+ } else if (value == QStringLiteral("mapping")) {
+ options->list = ListMapping;
+ } else {
+ *errorMessage = QStringLiteral("Please specify a valid option for -list (source, target, relative, mapping).");
+ return CommandLineParseError;
+ }
+ }
+
+ if (parser->isSet(jsonOption) || options->list) {
+ optVerboseLevel = 0;
+ options->json = new JsonOutput;
+ }
+
+ const QStringList posArgs = parser->positionalArguments();
+ if (posArgs.isEmpty()) {
+ *errorMessage = QStringLiteral("Please specify the binary or folder.");
+ return CommandLineParseError | CommandLineParseHelpRequested;
+ }
+
+ if (parser->isSet(dirOption))
+ options->directory = parser->value(dirOption);
+
+ if (parser->isSet(qmlDirOption))
+ options->qmlDirectories = parser->values(qmlDirOption);
+
+ if (parser->isSet(qmlImportOption))
+ options->qmlImportPaths = parser->values(qmlImportOption);
+
+ const QString &file = posArgs.front();
+ const QFileInfo fi(QDir::cleanPath(file));
+ if (!fi.exists()) {
+ *errorMessage = msgFileDoesNotExist(file);
+ return CommandLineParseError;
+ }
+
+ if (!options->directory.isEmpty() && !fi.isFile()) { // -dir was specified - expecting file.
+ *errorMessage = u'"' + file + QStringLiteral("\" is not an executable file.");
+ return CommandLineParseError;
+ }
+
+ if (fi.isFile()) {
+ options->binaries.append(fi.absoluteFilePath());
+ if (options->directory.isEmpty())
+ options->directory = fi.absolutePath();
+ } else {
+ const QString binary = findBinary(fi.absoluteFilePath(), options->platform);
+ if (binary.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to find binary in \"") + file + u'"';
+ return CommandLineParseError;
+ }
+ options->directory = fi.absoluteFilePath();
+ options->binaries.append(binary);
+ } // directory.
+
+ // Remaining files or plugin directories
+ bool multipleDirs = false;
+ for (int i = 1; i < posArgs.size(); ++i) {
+ const QFileInfo fi(QDir::cleanPath(posArgs.at(i)));
+ const QString path = fi.absoluteFilePath();
+ if (!fi.exists()) {
+ *errorMessage = msgFileDoesNotExist(path);
+ return CommandLineParseError;
+ }
+ if (fi.isDir()) {
+ const QStringList libraries =
+ findSharedLibraries(QDir(path), options->platform, MatchDebugOrRelease, QString());
+ for (const QString &library : libraries)
+ options->binaries.append(path + u'/' + library);
+ } else {
+ if (!parser->isSet(dirOption) && fi.absolutePath() != options->directory)
+ multipleDirs = true;
+ options->binaries.append(path);
+ }
+ }
+ if (multipleDirs) {
+ std::wcerr << "Warning: using binaries from different directories, deploying to following path: "
+ << options->directory << '\n' << "To disable this warning, use the --dir option\n";
+ }
+ if (options->translationsDirectory.isEmpty())
+ options->translationsDirectory = options->directory + "/translations"_L1;
+ return 0;
+}
+
+// Simple line wrapping at 80 character boundaries.
+static inline QString lineBreak(QString s)
+{
+ for (qsizetype i = 80; i < s.size(); i += 80) {
+ const qsizetype lastBlank = s.lastIndexOf(u' ', i);
+ if (lastBlank >= 0) {
+ s[lastBlank] = u'\n';
+ i = lastBlank + 1;
+ }
+ }
+ return s;
+}
+
+static inline QString helpText(const QCommandLineParser &p, const PluginInformation &pluginInfo)
+{
+ QString result = p.helpText();
+ // Replace the default-generated text which is too long by a short summary
+ // explaining how to enable single libraries.
+ if (qtModuleEntries.size() == 0)
+ return result;
+ const QtModule &firstModule = qtModuleEntries.moduleById(0);
+ const QString firstModuleOption = moduleNameToOptionName(firstModule.name);
+ const qsizetype moduleStart = result.indexOf("\n --"_L1 + firstModuleOption);
+ const qsizetype argumentsStart = result.lastIndexOf("\nArguments:"_L1);
+ if (moduleStart >= argumentsStart)
+ return result;
+ QString moduleHelp;
+ moduleHelp +=
+ "\n\nQt libraries can be added by passing their name (-xml) or removed by passing\n"
+ "the name prepended by --no- (--no-xml). Available libraries:\n"_L1;
+ ModuleBitset mask;
+ moduleHelp += lineBreak(QString::fromLatin1(formatQtModules(mask.set(), true)));
+ moduleHelp += u"\n\n";
+ moduleHelp +=
+ u"Qt plugins can be included or excluded individually or by type.\n"
+ u"To deploy or block plugins individually, use the --include-plugins\n"
+ u"and --exclude-plugins options (--include-plugins qjpeg,qsvgicon)\n"
+ u"You can also use the --skip-plugin-types or --add-plugin-types to\n"
+ u"achieve similar results with entire plugin groups, like imageformats, e.g.\n"
+ u"(--add-plugin-types imageformats,iconengines). Exclusion always takes\n"
+ u"precedence over inclusion, and types take precedence over specific plugins.\n"
+ u"For example, including qjpeg, but skipping imageformats, will NOT deploy qjpeg.\n"
+ u"\nDetected available plugins:\n";
+ moduleHelp += formatQtPlugins(pluginInfo);
+ result.replace(moduleStart, argumentsStart - moduleStart, moduleHelp);
+ return result;
+}
+
+static inline bool isQtModule(const QString &libName)
+{
+ // Match Standard modules named Qt6XX.dll
+ if (libName.size() < 3 || !libName.startsWith("Qt"_L1, Qt::CaseInsensitive))
+ return false;
+ const QChar version = libName.at(2);
+ return version.isDigit() && (version.toLatin1() - '0') == QT_VERSION_MAJOR;
+}
+
+// Helper for recursively finding all dependent Qt libraries.
+static bool findDependentQtLibraries(const QString &qtBinDir, const QString &binary, Platform platform,
+ QString *errorMessage, QStringList *result,
+ unsigned *wordSize = nullptr, bool *isDebug = nullptr,
+ unsigned short *machineArch = nullptr,
+ int *directDependencyCount = nullptr, int recursionDepth = 0)
+{
+ QStringList dependentLibs;
+ if (directDependencyCount)
+ *directDependencyCount = 0;
+ if (!readPeExecutable(binary, errorMessage, &dependentLibs, wordSize, isDebug,
+ platform == WindowsDesktopMinGW, machineArch)) {
+ errorMessage->prepend("Unable to find dependent libraries of "_L1 +
+ QDir::toNativeSeparators(binary) + " :"_L1);
+ return false;
+ }
+ // Filter out the Qt libraries. Note that depends.exe finds libs from optDirectory if we
+ // are run the 2nd time (updating). We want to check against the Qt bin dir libraries
+ const int start = result->size();
+ for (const QString &lib : std::as_const(dependentLibs)) {
+ if (isQtModule(lib)) {
+ const QString path = normalizeFileName(qtBinDir + u'/' + QFileInfo(lib).fileName());
+ if (!result->contains(path))
+ result->append(path);
+ }
+ }
+ const int end = result->size();
+ if (directDependencyCount)
+ *directDependencyCount = end - start;
+ // Recurse
+ for (int i = start; i < end; ++i)
+ if (!findDependentQtLibraries(qtBinDir, result->at(i), platform, errorMessage, result,
+ nullptr, nullptr, nullptr, nullptr, recursionDepth + 1))
+ return false;
+ return true;
+}
+
+// Base class to filter debug/release Windows DLLs for functions to be passed to updateFile().
+// Tries to pre-filter by namefilter and does check via PE.
+class DllDirectoryFileEntryFunction {
+public:
+ explicit DllDirectoryFileEntryFunction(Platform platform,
+ DebugMatchMode debugMatchMode, const QString &prefix = QString()) :
+ m_platform(platform), m_debugMatchMode(debugMatchMode), m_prefix(prefix) {}
+
+ QStringList operator()(const QDir &dir) const
+ { return findSharedLibraries(dir, m_platform, m_debugMatchMode, m_prefix); }
+
+private:
+ const Platform m_platform;
+ const DebugMatchMode m_debugMatchMode;
+ const QString m_prefix;
+};
+
+static QString pdbFileName(QString libraryFileName)
+{
+ const qsizetype lastDot = libraryFileName.lastIndexOf(u'.') + 1;
+ if (lastDot <= 0)
+ return QString();
+ libraryFileName.replace(lastDot, libraryFileName.size() - lastDot, "pdb"_L1);
+ return libraryFileName;
+}
+static inline QStringList qmlCacheFileFilters()
+{
+ return QStringList() << QStringLiteral("*.jsc") << QStringLiteral("*.qmlc");
+}
+
+// File entry filter function for updateFile() that returns a list of files for
+// QML import trees: DLLs (matching debug) and .qml/,js, etc.
+class QmlDirectoryFileEntryFunction {
+public:
+ enum Flags {
+ DeployPdb = 0x1,
+ SkipSources = 0x2
+ };
+
+ explicit QmlDirectoryFileEntryFunction(
+ const QString &moduleSourcePath, Platform platform, DebugMatchMode debugMatchMode, unsigned flags)
+ : m_flags(flags), m_qmlNameFilter(QmlDirectoryFileEntryFunction::qmlNameFilters(flags))
+ , m_dllFilter(platform, debugMatchMode), m_moduleSourcePath(moduleSourcePath)
+ {}
+
+ QStringList operator()(const QDir &dir) const
+ {
+ if (moduleSourceDir(dir).canonicalPath() != m_moduleSourcePath) {
+ // If we're in a different module, return nothing.
+ return {};
+ }
+
+ QStringList result;
+ const QStringList &libraries = m_dllFilter(dir);
+ for (const QString &library : libraries) {
+ result.append(library);
+ if (m_flags & DeployPdb) {
+ const QString pdb = pdbFileName(library);
+ if (QFileInfo(dir.absoluteFilePath(pdb)).isFile())
+ result.append(pdb);
+ }
+ }
+ result.append(m_qmlNameFilter(dir));
+ return result;
+ }
+
+private:
+ static QDir moduleSourceDir(const QDir &dir)
+ {
+ QDir moduleSourceDir = dir;
+ while (!moduleSourceDir.exists(QStringLiteral("qmldir"))) {
+ if (!moduleSourceDir.cdUp()) {
+ return {};
+ }
+ }
+ return moduleSourceDir;
+ }
+
+ static inline QStringList qmlNameFilters(unsigned flags)
+ {
+ QStringList result;
+ result << QStringLiteral("qmldir") << QStringLiteral("*.qmltypes")
+ << QStringLiteral("*.frag") << QStringLiteral("*.vert") // Shaders
+ << QStringLiteral("*.ttf");
+ if (!(flags & SkipSources)) {
+ result << QStringLiteral("*.js") << QStringLiteral("*.qml") << QStringLiteral("*.png");
+ result.append(qmlCacheFileFilters());
+ }
+ return result;
+ }
+
+ const unsigned m_flags;
+ NameFilterFileEntryFunction m_qmlNameFilter;
+ DllDirectoryFileEntryFunction m_dllFilter;
+ QString m_moduleSourcePath;
+};
+
+static qint64 qtModule(QString module, const QString &infix)
+{
+ // Match needle 'path/Qt6Core<infix><d>.dll' or 'path/libQt6Core<infix>.so.5.0'
+ const qsizetype lastSlashPos = module.lastIndexOf(u'/');
+ if (lastSlashPos > 0)
+ module.remove(0, lastSlashPos + 1);
+ if (module.startsWith("lib"_L1))
+ module.remove(0, 3);
+ int endPos = infix.isEmpty() ? -1 : module.lastIndexOf(infix);
+ if (endPos == -1)
+ endPos = module.indexOf(u'.'); // strip suffixes '.so.5.0'.
+ if (endPos > 0)
+ module.truncate(endPos);
+ // That should leave us with 'Qt6Core<d>'.
+ for (const auto &qtModule : qtModuleEntries) {
+ const QString &libraryName = qtModule.name;
+ if (module == libraryName
+ || (module.size() == libraryName.size() + 1 && module.startsWith(libraryName))) {
+ return qtModule.id;
+ }
+ }
+ std::wcerr << "Warning: module " << qPrintable(module) << " could not be found\n";
+ return -1;
+}
+
+// Return the path if a plugin is to be deployed
+static QString deployPlugin(const QString &plugin, const QDir &subDir, const bool dueToModule,
+ const DebugMatchMode &debugMatchMode, ModuleBitset *pluginNeededQtModules,
+ const ModuleBitset &disabledQtModules,
+ const PluginSelections &pluginSelections, const QString &libraryLocation,
+ const QString &infix, Platform platform,
+ bool deployInsightTrackerPlugin, bool deployOpenSslPlugin)
+{
+ const QString subDirName = subDir.dirName();
+ // Filter out disabled plugins
+ if (optVerboseLevel && pluginSelections.disabledPluginTypes.contains(subDirName)) {
+ std::wcout << "Skipping plugin " << plugin << " due to skipped plugin type " << subDirName << '\n';
+ return {};
+ }
+ if (optVerboseLevel && subDirName == u"generic" && plugin.contains(u"qinsighttracker")
+ && !deployInsightTrackerPlugin) {
+ std::wcout << "Skipping plugin " << plugin
+ << ". Use -deploy-insighttracker if you want to use it.\n";
+ return {};
+ }
+ if (optVerboseLevel && subDirName == u"tls" && plugin.contains(u"qopensslbackend")
+ && !deployOpenSslPlugin) {
+ std::wcout << "Skipping plugin " << plugin
+ << ". Use -force_openssl or specify -openssl-root if you want to use it.\n";
+ return {};
+ }
+
+ const int dotIndex = plugin.lastIndexOf(u'.');
+ // Strip the .dll from the name, and an additional 'd' if it's a debug library with the 'd'
+ // suffix
+ const int stripIndex = debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)
+ ? dotIndex - 1
+ : dotIndex;
+ const QString pluginName = plugin.first(stripIndex);
+
+ if (optVerboseLevel && pluginSelections.excludedPlugins.contains(pluginName)) {
+ std::wcout << "Skipping plugin " << plugin << " due to exclusion option" << '\n';
+ return {};
+ }
+
+ // By default, only deploy qwindows.dll
+ if (subDirName == u"platforms"
+ && !(pluginSelections.includedPlugins.contains(pluginName)
+ || (pluginSelections.enabledPluginTypes.contains(subDirName)))
+ && !pluginName.startsWith(u"qwindows")) {
+ return {};
+ }
+
+ const QString pluginPath = subDir.absoluteFilePath(plugin);
+
+ // If dueToModule is false, check if the user included the plugin or the entire type. In the
+ // former's case, only deploy said plugin and not all plugins of that type.
+ const bool requiresPlugin = pluginSelections.includedPlugins.contains(pluginName)
+ || pluginSelections.enabledPluginTypes.contains(subDirName);
+ if (!dueToModule && !requiresPlugin)
+ return {};
+
+ // Deploy QUiTools plugins as is without further dependency checking.
+ // The user needs to ensure all required libraries are present (would
+ // otherwise pull QtWebEngine for its plugin).
+ if (subDirName == u"designer")
+ return pluginPath;
+
+ QStringList dependentQtLibs;
+ QString errorMessage;
+ if (findDependentQtLibraries(libraryLocation, pluginPath, platform,
+ &errorMessage, &dependentQtLibs)) {
+ for (int d = 0; d < dependentQtLibs.size(); ++d) {
+ const qint64 module = qtModule(dependentQtLibs.at(d), infix);
+ if (module >= 0)
+ (*pluginNeededQtModules)[module] = 1;
+ }
+ } else {
+ std::wcerr << "Warning: Cannot determine dependencies of "
+ << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n';
+ }
+
+ ModuleBitset disabledNeededQtModules;
+ disabledNeededQtModules = *pluginNeededQtModules & disabledQtModules;
+ if (disabledNeededQtModules.any()) {
+ if (optVerboseLevel) {
+ std::wcout << "Skipping plugin " << plugin
+ << " due to disabled dependencies ("
+ << formatQtModules(disabledNeededQtModules).constData() << ").\n";
+ }
+ return {};
+ }
+
+ return pluginPath;
+}
+
+static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo,
+ const PluginSelections &pluginSelections)
+{
+ bool needsTypeForPlugin = false;
+ for (const QString &plugin: pluginSelections.includedPlugins) {
+ if (pluginInfo.isTypeForPlugin(subDirName, plugin))
+ needsTypeForPlugin = true;
+ }
+ return (pluginSelections.enabledPluginTypes.contains(subDirName) || needsTypeForPlugin);
+}
+
+QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disabledQtModules,
+ const PluginInformation &pluginInfo, const PluginSelections &pluginSelections,
+ const QString &qtPluginsDirName, const QString &libraryLocation,
+ const QString &infix, DebugMatchMode debugMatchModeIn, Platform platform,
+ QString *platformPlugin, bool deployInsightTrackerPlugin,
+ bool deployOpenSslPlugin)
+{
+ if (qtPluginsDirName.isEmpty())
+ return QStringList();
+ QDir pluginsDir(qtPluginsDirName);
+ QStringList result;
+ bool missingQtModulesAdded = false;
+ const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QFileInfo &subDirFi : pluginDirs) {
+ const QString subDirName = subDirFi.fileName();
+ const size_t module = qtModuleEntries.moduleIdForPluginType(subDirName);
+ if (module == QtModule::InvalidId) {
+ if (optVerboseLevel > 1) {
+ std::wcerr << "No Qt module found for plugin type \"" << subDirName << "\".\n";
+ }
+ continue;
+ }
+ const bool dueToModule = usedQtModules->test(module);
+ if (dueToModule || needsPluginType(subDirName, pluginInfo, pluginSelections)) {
+ const DebugMatchMode debugMatchMode = (module == QtWebEngineCoreModuleId)
+ ? MatchDebugOrRelease // QTBUG-44331: Debug detection does not work for webengine, deploy all.
+ : debugMatchModeIn;
+ QDir subDir(subDirFi.absoluteFilePath());
+ if (optVerboseLevel)
+ std::wcout << "Adding in plugin type " << subDirFi.baseName() << " for module: " << qtModuleEntries.moduleById(module).name << '\n';
+
+ const bool isPlatformPlugin = subDirName == "platforms"_L1;
+ const QStringList plugins =
+ findSharedLibraries(subDir, platform, debugMatchMode, QString());
+ for (const QString &plugin : plugins) {
+ ModuleBitset pluginNeededQtModules;
+ const QString pluginPath =
+ deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules,
+ disabledQtModules, pluginSelections, libraryLocation, infix,
+ platform, deployInsightTrackerPlugin, deployOpenSslPlugin);
+ if (!pluginPath.isEmpty()) {
+ if (isPlatformPlugin && plugin.startsWith(u"qwindows"))
+ *platformPlugin = subDir.absoluteFilePath(plugin);
+ result.append(pluginPath);
+
+ const ModuleBitset missingModules = (pluginNeededQtModules & ~*usedQtModules);
+ if (missingModules.any()) {
+ *usedQtModules |= missingModules;
+ missingQtModulesAdded = true;
+ if (optVerboseLevel) {
+ std::wcout << "Adding " << formatQtModules(missingModules).constData()
+ << " for " << plugin << " from plugin type: " << subDirName << '\n';
+ }
+ }
+ }
+ } // for filter
+ } // type matches
+ } // for plugin folder
+
+ // If missing Qt modules were added during plugin deployment make additional pass, because we may need
+ // additional plugins.
+ if (pluginSelections.includeSoftPlugins && missingQtModulesAdded) {
+ if (optVerboseLevel) {
+ std::wcout << "Performing additional pass of finding Qt plugins due to updated Qt module list: "
+ << formatQtModules(*usedQtModules).constData() << "\n";
+ }
+ return findQtPlugins(usedQtModules, disabledQtModules, pluginInfo, pluginSelections, qtPluginsDirName,
+ libraryLocation, infix, debugMatchModeIn, platform, platformPlugin,
+ deployInsightTrackerPlugin, deployOpenSslPlugin);
+ }
+
+ return result;
+}
+
+static QStringList translationNameFilters(const ModuleBitset &modules, const QString &prefix)
+{
+ QStringList result;
+ for (const auto &qtModule : qtModuleEntries) {
+ if (modules.test(qtModule.id) && !qtModule.translationCatalog.isEmpty()) {
+ const QString name = qtModule.translationCatalog + u'_' + prefix + ".qm"_L1;
+ if (!result.contains(name))
+ result.push_back(name);
+ }
+ }
+ return result;
+}
+
+static bool deployTranslations(const QString &sourcePath, const ModuleBitset &usedQtModules,
+ const QString &target, const Options &options,
+ QString *errorMessage)
+{
+ // Find available languages prefixes by checking on qtbase.
+ QStringList prefixes;
+ QDir sourceDir(sourcePath);
+ const QStringList qmFilter = QStringList(QStringLiteral("qtbase_*.qm"));
+ const QFileInfoList &qmFiles = sourceDir.entryInfoList(qmFilter);
+ for (const QFileInfo &qmFi : qmFiles) {
+ const QString prefix = qmFi.baseName().mid(7);
+ if (options.languages.isEmpty() || options.languages.contains(prefix))
+ prefixes.append(prefix);
+ }
+ if (prefixes.isEmpty()) {
+ std::wcerr << "Warning: Could not find any translations in "
+ << QDir::toNativeSeparators(sourcePath) << " (developer build?)\n.";
+ return true;
+ }
+ // Run lconvert to concatenate all files into a single named "qt_<prefix>.qm" in the application folder
+ // Use QT_INSTALL_TRANSLATIONS as working directory to keep the command line short.
+ const QString absoluteTarget = QFileInfo(target).absoluteFilePath();
+ const QString binary = QStringLiteral("lconvert");
+ QStringList arguments;
+ for (const QString &prefix : std::as_const(prefixes)) {
+ arguments.clear();
+ const QString targetFile = QStringLiteral("qt_") + prefix + QStringLiteral(".qm");
+ arguments.append(QStringLiteral("-o"));
+ const QString targetFilePath = absoluteTarget + u'/' + targetFile;
+ if (options.json)
+ options.json->addFile(sourcePath + u'/' + targetFile, absoluteTarget);
+ arguments.append(QDir::toNativeSeparators(targetFilePath));
+ const QStringList translationFilters = translationNameFilters(usedQtModules, prefix);
+ if (translationFilters.isEmpty()){
+ std::wcerr << "Warning: translation catalogs are all empty, skipping translation deployment\n";
+ return true;
+ }
+ const QFileInfoList &langQmFiles = sourceDir.entryInfoList(translationFilters);
+ for (const QFileInfo &langQmFileFi : langQmFiles) {
+ if (options.json) {
+ options.json->addFile(langQmFileFi.absoluteFilePath(),
+ absoluteTarget);
+ }
+ arguments.append(langQmFileFi.fileName());
+ }
+ if (optVerboseLevel)
+ std::wcout << "Creating " << targetFile << "...\n";
+ unsigned long exitCode;
+ if ((options.updateFileFlags & SkipUpdateFile) == 0
+ && (!runProcess(binary, arguments, sourcePath, &exitCode, nullptr, nullptr, errorMessage)
+ || exitCode)) {
+ return false;
+ }
+ } // for prefixes.
+ return true;
+}
+
+static QStringList findFFmpegLibs(const QString &qtBinDir, Platform platform)
+{
+ const std::vector<QLatin1StringView> ffmpegHints = { "avcodec"_L1, "avformat"_L1, "avutil"_L1,
+ "swresample"_L1, "swscale"_L1 };
+ const QStringList bundledLibs =
+ findSharedLibraries(qtBinDir, platform, MatchDebugOrRelease, {});
+
+ QStringList ffmpegLibs;
+ for (const QLatin1StringView &libHint : ffmpegHints) {
+ const QStringList ffmpegLib = bundledLibs.filter(libHint, Qt::CaseInsensitive);
+
+ if (ffmpegLib.empty()) {
+ std::wcerr << "Warning: Cannot find FFmpeg libraries. Multimedia features will not work as expected.\n";
+ return {};
+ } else if (ffmpegLib.size() != 1u) {
+ std::wcerr << "Warning: Multiple versions of FFmpeg libraries found. Multimedia features will not work as expected.\n";
+ return {};
+ }
+
+ const QChar slash(u'/');
+ QFileInfo ffmpegLibPath{ qtBinDir + slash + ffmpegLib.front() };
+ ffmpegLibs.append(ffmpegLibPath.absoluteFilePath());
+ }
+
+ return ffmpegLibs;
+}
+
+// Find the openssl libraries Qt executables depend on.
+static QStringList findOpenSslLibraries(const QString &openSslRootDir, Platform platform)
+{
+ const std::vector<QLatin1StringView> libHints = { "libcrypto"_L1, "libssl"_L1 };
+ const QChar slash(u'/');
+ const QString openSslBinDir = openSslRootDir + slash + "bin"_L1;
+ const QStringList openSslRootLibs =
+ findSharedLibraries(openSslBinDir, platform, MatchDebugOrRelease, {});
+
+ QStringList result;
+ for (const QLatin1StringView &libHint : libHints) {
+ const QStringList lib = openSslRootLibs.filter(libHint, Qt::CaseInsensitive);
+
+ if (lib.empty()) {
+ std::wcerr << "Warning: Cannot find openssl libraries.\n";
+ return {};
+ } else if (lib.size() != 1u) {
+ std::wcerr << "Warning: Multiple versions of openssl libraries found.\n";
+ return {};
+ }
+
+ QFileInfo libPath{ openSslBinDir + slash + lib.front() };
+ result.append(libPath.absoluteFilePath());
+ }
+
+ return result;
+}
+
+
+struct DeployResult
+{
+ operator bool() const { return success; }
+
+ bool success = false;
+ bool isDebug = false;
+ ModuleBitset directlyUsedQtLibraries;
+ ModuleBitset usedQtLibraries;
+ ModuleBitset deployedQtLibraries;
+};
+
+static QString libraryPath(const QString &libraryLocation, const char *name,
+ const QString &infix, Platform platform, bool debug)
+{
+ QString result = libraryLocation + u'/';
+ result += QLatin1StringView(name);
+ result += infix;
+ if (debug && platformHasDebugSuffix(platform))
+ result += u'd';
+ result += sharedLibrarySuffix();
+ return result;
+}
+
+static QString vcDebugRedistDir() { return QStringLiteral("Debug_NonRedist"); }
+
+static QString vcRedistDir()
+{
+ const char vcDirVar[] = "VCINSTALLDIR";
+ const QChar slash(u'/');
+ QString vcRedistDirName = QDir::cleanPath(QFile::decodeName(qgetenv(vcDirVar)));
+ if (vcRedistDirName.isEmpty()) {
+ std::wcerr << "Warning: Cannot find Visual Studio installation directory, " << vcDirVar
+ << " is not set.\n";
+ return QString();
+ }
+ if (!vcRedistDirName.endsWith(slash))
+ vcRedistDirName.append(slash);
+ vcRedistDirName.append(QStringLiteral("redist/MSVC"));
+ if (!QFileInfo(vcRedistDirName).isDir()) {
+ std::wcerr << "Warning: Cannot find Visual Studio redist directory, "
+ << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n";
+ return QString();
+ }
+ // Look in reverse order for folder containing the debug redist folder
+ const QFileInfoList subDirs =
+ QDir(vcRedistDirName)
+ .entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
+ for (const QFileInfo &f : subDirs) {
+ QString path = f.absoluteFilePath();
+ if (QFileInfo(path + slash + vcDebugRedistDir()).isDir())
+ return path;
+ path += QStringLiteral("/onecore");
+ if (QFileInfo(path + slash + vcDebugRedistDir()).isDir())
+ return path;
+ }
+ std::wcerr << "Warning: Cannot find Visual Studio redist directory under "
+ << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n";
+ return QString();
+}
+
+static QStringList findMinGWRuntimePaths(const QString &qtBinDir, Platform platform, const QStringList &runtimeFilters)
+{
+ //MinGW: Add runtime libraries. Check first for the Qt binary directory, and default to path if nothing is found.
+ QStringList result;
+ const bool isClang = platform == WindowsDesktopClangMinGW;
+ QStringList filters;
+ const QString suffix = u'*' + sharedLibrarySuffix();
+ for (const auto &minGWRuntime : runtimeFilters)
+ filters.append(minGWRuntime + suffix);
+
+ QFileInfoList dlls = QDir(qtBinDir).entryInfoList(filters, QDir::Files);
+ if (dlls.isEmpty()) {
+ std::wcerr << "Warning: Runtime libraries not found in Qt binary folder, defaulting to looking in path\n";
+ const QString binaryPath = isClang ? findInPath("clang++.exe"_L1) : findInPath("g++.exe"_L1);
+ if (binaryPath.isEmpty()) {
+ std::wcerr << "Warning: Cannot find " << (isClang ? "Clang" : "GCC") << " installation directory, " << (isClang ? "clang++" : "g++") << ".exe must be in the path\n";
+ return {};
+ }
+ const QString binaryFolder = QFileInfo(binaryPath).absolutePath();
+ dlls = QDir(binaryFolder).entryInfoList(filters, QDir::Files);
+ }
+
+ for (const QFileInfo &dllFi : dlls)
+ result.append(dllFi.absoluteFilePath());
+
+ return result;
+}
+
+static QStringList compilerRunTimeLibs(const QString &qtBinDir, Platform platform, bool isDebug, unsigned short machineArch)
+{
+ QStringList result;
+ switch (platform) {
+ case WindowsDesktopMinGW: {
+ const QStringList minGWRuntimes = { "*gcc_"_L1, "*stdc++"_L1, "*winpthread"_L1 };
+ result.append(findMinGWRuntimePaths(qtBinDir, platform, minGWRuntimes));
+ break;
+ }
+ case WindowsDesktopClangMinGW: {
+ const QStringList clangMinGWRuntimes = { "*unwind"_L1, "*c++"_L1 };
+ result.append(findMinGWRuntimePaths(qtBinDir, platform, clangMinGWRuntimes));
+ break;
+ }
+#ifdef Q_OS_WIN
+ case WindowsDesktopMsvcIntel:
+ case WindowsDesktopMsvcArm: { // MSVC/Desktop: Add redistributable packages.
+ QString vcRedistDirName = vcRedistDir();
+ if (vcRedistDirName.isEmpty())
+ break;
+ QStringList redistFiles;
+ QDir vcRedistDir(vcRedistDirName);
+ const QString machineArchString = getArchString(machineArch);
+ if (isDebug) {
+ // Append DLLs from Debug_NonRedist\x??\Microsoft.VC<version>.DebugCRT.
+ if (vcRedistDir.cd(vcDebugRedistDir()) && vcRedistDir.cd(machineArchString)) {
+ const QStringList names = vcRedistDir.entryList(QStringList(QStringLiteral("Microsoft.VC*.DebugCRT")), QDir::Dirs);
+ if (!names.isEmpty() && vcRedistDir.cd(names.first())) {
+ const QFileInfoList &dlls = vcRedistDir.entryInfoList(QStringList("*.dll"_L1));
+ for (const QFileInfo &dll : dlls)
+ redistFiles.append(dll.absoluteFilePath());
+ }
+ }
+ } else { // release: Bundle vcredist<>.exe
+ QString releaseRedistDir = vcRedistDirName;
+ const QStringList countryCodes = vcRedistDir.entryList(QStringList(QStringLiteral("[0-9]*")), QDir::Dirs);
+ if (!countryCodes.isEmpty()) // Pre MSVC2017
+ releaseRedistDir += u'/' + countryCodes.constFirst();
+ QFileInfo fi(releaseRedistDir + "/vc_redist."_L1
+ + machineArchString + ".exe"_L1);
+ if (!fi.isFile()) { // Pre MSVC2017/15.5
+ fi.setFile(releaseRedistDir + "/vcredist_"_L1
+ + machineArchString + ".exe"_L1);
+ }
+ if (fi.isFile())
+ redistFiles.append(fi.absoluteFilePath());
+ }
+ if (redistFiles.isEmpty()) {
+ std::wcerr << "Warning: Cannot find Visual Studio " << (isDebug ? "debug" : "release")
+ << " redistributable files in " << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n";
+ break;
+ }
+ result.append(redistFiles);
+ }
+ break;
+#endif // Q_OS_WIN
+ default:
+ break;
+ }
+ return result;
+}
+
+static inline int qtVersion(const QMap<QString, QString> &qtpathsVariables)
+{
+ const QString versionString = qtpathsVariables.value(QStringLiteral("QT_VERSION"));
+ const QChar dot = u'.';
+ const int majorVersion = versionString.section(dot, 0, 0).toInt();
+ const int minorVersion = versionString.section(dot, 1, 1).toInt();
+ const int patchVersion = versionString.section(dot, 2, 2).toInt();
+ return (majorVersion << 16) | (minorVersion << 8) | patchVersion;
+}
+
+// Deploy a library along with its .pdb debug info file (MSVC) should it exist.
+static bool updateLibrary(const QString &sourceFileName, const QString &targetDirectory,
+ const Options &options, QString *errorMessage)
+{
+ if (!updateFile(sourceFileName, targetDirectory, options.updateFileFlags, options.json, errorMessage)) {
+ if (options.ignoreLibraryErrors) {
+ std::wcerr << "Warning: Could not update " << sourceFileName << " :" << *errorMessage << '\n';
+ errorMessage->clear();
+ return true;
+ }
+ return false;
+ }
+
+ if (options.deployPdb) {
+ const QFileInfo pdb(pdbFileName(sourceFileName));
+ if (pdb.isFile())
+ return updateFile(pdb.absoluteFilePath(), targetDirectory, options.updateFileFlags, nullptr, errorMessage);
+ }
+ return true;
+}
+
+// Find out the ICU version to add the data library icudtXX.dll, which does not
+// show as a dependency.
+static QString getIcuVersion(const QString &libName)
+{
+ QString version;
+ std::copy_if(libName.cbegin(), libName.cend(), std::back_inserter(version),
+ [](QChar c) { return c.isDigit(); });
+ return version;
+}
+
+static DeployResult deploy(const Options &options, const QMap<QString, QString> &qtpathsVariables,
+ const PluginInformation &pluginInfo, QString *errorMessage)
+{
+ DeployResult result;
+
+ const QChar slash = u'/';
+
+ const QString qtBinDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_BINS"));
+ const QString libraryLocation = qtBinDir;
+ const QString infix = qtpathsVariables.value(QLatin1StringView(qmakeInfixKey));
+ const int version = qtVersion(qtpathsVariables);
+ Q_UNUSED(version);
+
+ if (optVerboseLevel > 1)
+ std::wcout << "Qt binaries in " << QDir::toNativeSeparators(qtBinDir) << '\n';
+
+ QStringList dependentQtLibs;
+ bool detectedDebug;
+ unsigned wordSize;
+ unsigned short machineArch;
+ int directDependencyCount = 0;
+ if (!findDependentQtLibraries(libraryLocation, options.binaries.first(), options.platform, errorMessage, &dependentQtLibs, &wordSize,
+ &detectedDebug, &machineArch, &directDependencyCount)) {
+ return result;
+ }
+ for (int b = 1; b < options.binaries.size(); ++b) {
+ if (!findDependentQtLibraries(libraryLocation, options.binaries.at(b), options.platform, errorMessage, &dependentQtLibs,
+ nullptr, nullptr, nullptr)) {
+ return result;
+ }
+ }
+
+ DebugMatchMode debugMatchMode = MatchDebugOrRelease;
+ result.isDebug = false;
+ switch (options.debugDetection) {
+ case Options::DebugDetectionAuto:
+ // Debug detection is only relevant for Msvc/ClangMsvc which have distinct
+ // runtimes and binaries. For anything else, use MatchDebugOrRelease
+ // since also debug cannot be reliably detect for MinGW.
+ if (options.platform.testFlag(Msvc) || options.platform.testFlag(ClangMsvc)) {
+ result.isDebug = detectedDebug;
+ debugMatchMode = result.isDebug ? MatchDebug : MatchRelease;
+ }
+ break;
+ case Options::DebugDetectionForceDebug:
+ result.isDebug = true;
+ debugMatchMode = MatchDebug;
+ break;
+ case Options::DebugDetectionForceRelease:
+ debugMatchMode = MatchRelease;
+ break;
+ }
+
+ // Determine application type, check Quick2 is used by looking at the
+ // direct dependencies (do not be fooled by QtWebKit depending on it).
+ for (int m = 0; m < dependentQtLibs.size(); ++m) {
+ const qint64 module = qtModule(dependentQtLibs.at(m), infix);
+ if (module >= 0)
+ result.directlyUsedQtLibraries[module] = 1;
+ }
+
+ const bool usesQml = result.directlyUsedQtLibraries.test(QtQmlModuleId);
+ const bool usesQuick = result.directlyUsedQtLibraries.test(QtQuickModuleId);
+ const bool uses3DQuick = result.directlyUsedQtLibraries.test(Qt3DQuickModuleId);
+ const bool usesQml2 = !(options.disabledLibraries.test(QtQmlModuleId))
+ && (usesQml || usesQuick || uses3DQuick || (options.additionalLibraries.test(QtQmlModuleId)));
+
+ if (optVerboseLevel) {
+ std::wcout << QDir::toNativeSeparators(options.binaries.first()) << ' '
+ << wordSize << " bit, " << (result.isDebug ? "debug" : "release")
+ << " executable";
+ if (usesQml2)
+ std::wcout << " [QML]";
+ std::wcout << '\n';
+ }
+
+ if (dependentQtLibs.isEmpty()) {
+ *errorMessage = QDir::toNativeSeparators(options.binaries.first()) + QStringLiteral(" does not seem to be a Qt executable.");
+ return result;
+ }
+
+ // Some Windows-specific checks: Qt5Core depends on ICU when configured with "-icu". Other than
+ // that, Qt5WebKit has a hard dependency on ICU.
+ if (options.platform.testFlag(WindowsBased)) {
+ const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral("Qt6Core"), Qt::CaseInsensitive)
+ + dependentQtLibs.filter(QStringLiteral("Qt5WebKit"), Qt::CaseInsensitive);
+ for (const QString &qtLib : qtLibs) {
+ QStringList icuLibs = findDependentLibraries(qtLib, errorMessage).filter(QStringLiteral("ICU"), Qt::CaseInsensitive);
+ if (!icuLibs.isEmpty()) {
+ // Find out the ICU version to add the data library icudtXX.dll, which does not show
+ // as a dependency.
+ const QString icuVersion = getIcuVersion(icuLibs.constFirst());
+ if (!icuVersion.isEmpty()) {
+ if (optVerboseLevel > 1)
+ std::wcout << "Adding ICU version " << icuVersion << '\n';
+ QString icuLib = QStringLiteral("icudt") + icuVersion
+ + QLatin1StringView(windowsSharedLibrarySuffix);
+ // Some packages contain debug dlls of ICU libraries even though it's a C
+ // library and the official packages do not differentiate (QTBUG-87677)
+ if (result.isDebug) {
+ const QString icuLibCandidate = QStringLiteral("icudtd") + icuVersion
+ + QLatin1StringView(windowsSharedLibrarySuffix);
+ if (!findInPath(icuLibCandidate).isEmpty()) {
+ icuLib = icuLibCandidate;
+ }
+ }
+ icuLibs.push_back(icuLib);
+ }
+ for (const QString &icuLib : std::as_const(icuLibs)) {
+ const QString icuPath = findInPath(icuLib);
+ if (icuPath.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to locate ICU library ") + icuLib;
+ return result;
+ }
+ dependentQtLibs.push_back(icuPath);
+ } // for each icuLib
+ break;
+ } // !icuLibs.isEmpty()
+ } // Qt6Core/Qt6WebKit
+ } // Windows
+
+ // Scan Quick2 imports
+ QmlImportScanResult qmlScanResult;
+ if (options.quickImports && usesQml2) {
+ // Custom list of import paths provided by user
+ QStringList qmlImportPaths = options.qmlImportPaths;
+ // Qt's own QML modules
+ qmlImportPaths << qtpathsVariables.value(QStringLiteral("QT_INSTALL_QML"));
+ QStringList qmlDirectories = options.qmlDirectories;
+ if (qmlDirectories.isEmpty()) {
+ const QString qmlDirectory = findQmlDirectory(options.platform, options.directory);
+ if (!qmlDirectory.isEmpty())
+ qmlDirectories.append(qmlDirectory);
+ }
+ for (const QString &qmlDirectory : std::as_const(qmlDirectories)) {
+ if (optVerboseLevel >= 1)
+ std::wcout << "Scanning " << QDir::toNativeSeparators(qmlDirectory) << ":\n";
+ const QmlImportScanResult scanResult =
+ runQmlImportScanner(qmlDirectory, qmlImportPaths,
+ result.directlyUsedQtLibraries.test(QtWidgetsModuleId),
+ options.platform, debugMatchMode, errorMessage);
+ if (!scanResult.ok)
+ return result;
+ qmlScanResult.append(scanResult);
+ // Additional dependencies of QML plugins.
+ for (const QString &plugin : std::as_const(qmlScanResult.plugins)) {
+ if (!findDependentQtLibraries(libraryLocation, plugin, options.platform, errorMessage, &dependentQtLibs, &wordSize, &detectedDebug, &machineArch))
+ return result;
+ }
+ if (optVerboseLevel >= 1) {
+ std::wcout << "QML imports:\n";
+ for (const QmlImportScanResult::Module &mod : std::as_const(qmlScanResult.modules)) {
+ std::wcout << " '" << mod.name << "' "
+ << QDir::toNativeSeparators(mod.sourcePath) << '\n';
+ }
+ if (optVerboseLevel >= 2) {
+ std::wcout << "QML plugins:\n";
+ for (const QString &p : std::as_const(qmlScanResult.plugins))
+ std::wcout << " " << QDir::toNativeSeparators(p) << '\n';
+ }
+ }
+ }
+ }
+
+ QString platformPlugin;
+ // Sort apart Qt 5 libraries in the ones that are represented by the
+ // QtModule enumeration (and thus controlled by flags) and others.
+ QStringList deployedQtLibraries;
+ for (int i = 0 ; i < dependentQtLibs.size(); ++i) {
+ const qint64 module = qtModule(dependentQtLibs.at(i), infix);
+ if (module >= 0)
+ result.usedQtLibraries[module] = 1;
+ else
+ deployedQtLibraries.push_back(dependentQtLibs.at(i)); // Not represented by flag.
+ }
+ result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
+
+ ModuleBitset disabled = options.disabledLibraries;
+ if (!usesQml2) {
+ disabled[QtQmlModuleId] = 1;
+ disabled[QtQuickModuleId] = 1;
+ }
+
+ QStringList openSslLibs;
+ if (!options.openSslRootDirectory.isEmpty()) {
+ openSslLibs = findOpenSslLibraries(options.openSslRootDirectory, options.platform);
+ if (openSslLibs.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to find openSSL libraries in ")
+ + options.openSslRootDirectory;
+ return result;
+ }
+
+ deployedQtLibraries.append(openSslLibs);
+ }
+ const bool deployOpenSslPlugin = options.forceOpenSslPlugin || !openSslLibs.isEmpty();
+
+ const QStringList plugins = findQtPlugins(
+ &result.deployedQtLibraries,
+ // For non-QML applications, disable QML to prevent it from being pulled in by the
+ // qtaccessiblequick plugin.
+ disabled, pluginInfo,
+ options.pluginSelections, qtpathsVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")),
+ libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin,
+ options.deployInsightTrackerPlugin, deployOpenSslPlugin);
+
+ // Apply options flags and re-add library names.
+ QString qtGuiLibrary;
+ for (const auto &qtModule : qtModuleEntries) {
+ if (result.deployedQtLibraries.test(qtModule.id)) {
+ const QString library = libraryPath(libraryLocation, qtModule.name.toUtf8(), infix,
+ options.platform, result.isDebug);
+ deployedQtLibraries.append(library);
+ if (qtModule.id == QtGuiModuleId)
+ qtGuiLibrary = library;
+ }
+ }
+
+ if (optVerboseLevel >= 1) {
+ std::wcout << "Direct dependencies: " << formatQtModules(result.directlyUsedQtLibraries).constData()
+ << "\nAll dependencies : " << formatQtModules(result.usedQtLibraries).constData()
+ << "\nTo be deployed : " << formatQtModules(result.deployedQtLibraries).constData() << '\n';
+ }
+
+ if (optVerboseLevel > 1)
+ std::wcout << "Plugins: " << plugins.join(u',') << '\n';
+
+ if (result.deployedQtLibraries.test(QtGuiModuleId) && platformPlugin.isEmpty()) {
+ *errorMessage =QStringLiteral("Unable to find the platform plugin.");
+ return result;
+ }
+
+ if (options.platform.testFlag(WindowsBased) && !qtGuiLibrary.isEmpty()) {
+ const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, errorMessage);
+ const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral("opengl32"), Qt::CaseInsensitive).isEmpty();
+ if (options.softwareRasterizer && !dependsOnOpenGl) {
+ const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral("opengl32sw") + QLatin1StringView(windowsSharedLibrarySuffix));
+ if (softwareRasterizer.isFile())
+ deployedQtLibraries.append(softwareRasterizer.absoluteFilePath());
+ }
+ if (options.systemD3dCompiler && machineArch != IMAGE_FILE_MACHINE_ARM64) {
+ const QString d3dCompiler = findD3dCompiler(options.platform, qtBinDir, wordSize);
+ if (d3dCompiler.isEmpty()) {
+ std::wcerr << "Warning: Cannot find any version of the d3dcompiler DLL.\n";
+ } else {
+ deployedQtLibraries.push_back(d3dCompiler);
+ }
+ }
+ if (options.systemDxc) {
+ const QStringList dxcLibs = findDxc(options.platform, qtBinDir, wordSize);
+ if (!dxcLibs.isEmpty())
+ deployedQtLibraries.append(dxcLibs);
+ else
+ std::wcerr << "Warning: Cannot find any version of the dxcompiler.dll and dxil.dll.\n";
+ }
+ } // Windows
+
+ // Add FFmpeg if we deploy the FFmpeg backend
+ if (options.ffmpeg
+ && !plugins.filter(QStringLiteral("ffmpegmediaplugin"), Qt::CaseInsensitive).empty()) {
+ deployedQtLibraries.append(findFFmpegLibs(qtBinDir, options.platform));
+ }
+
+ // Update libraries
+ if (options.libraries) {
+ const QString targetPath = options.libraryDirectory.isEmpty() ?
+ options.directory : options.libraryDirectory;
+ QStringList libraries = deployedQtLibraries;
+ if (options.compilerRunTime)
+ libraries.append(compilerRunTimeLibs(qtBinDir, options.platform, result.isDebug, machineArch));
+ for (const QString &qtLib : std::as_const(libraries)) {
+ if (!updateLibrary(qtLib, targetPath, options, errorMessage))
+ return result;
+ }
+
+#if !QT_CONFIG(relocatable)
+ if (options.patchQt && !options.dryRun) {
+ const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation, "Qt6Core", infix,
+ options.platform, result.isDebug)).fileName();
+ if (!patchQtCore(targetPath + u'/' + qt6CoreName, errorMessage)) {
+ std::wcerr << "Warning: " << *errorMessage << '\n';
+ errorMessage->clear();
+ }
+ }
+#endif // QT_CONFIG(relocatable)
+ } // optLibraries
+
+ // Update plugins
+ if (options.plugins) {
+ const QString targetPath = options.pluginDirectory.isEmpty() ?
+ options.directory : options.pluginDirectory;
+ QDir dir(targetPath);
+ if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
+ *errorMessage = "Cannot create "_L1 +
+ QDir::toNativeSeparators(dir.absolutePath()) + u'.';
+ return result;
+ }
+ for (const QString &plugin : plugins) {
+ const QString targetDirName = plugin.section(slash, -2, -2);
+ const QString targetPath = dir.absoluteFilePath(targetDirName);
+ if (!dir.exists(targetDirName)) {
+ if (optVerboseLevel)
+ std::wcout << "Creating directory " << targetPath << ".\n";
+ if (!(options.updateFileFlags & SkipUpdateFile) && !dir.mkdir(targetDirName)) {
+ *errorMessage = QStringLiteral("Cannot create ") + targetDirName + u'.';
+ return result;
+ }
+ }
+ if (!updateLibrary(plugin, targetPath, options, errorMessage))
+ return result;
+ }
+ } // optPlugins
+
+ // Update Quick imports
+ // Do not be fooled by QtWebKit.dll depending on Quick into always installing Quick imports
+ // for WebKit1-applications. Check direct dependency only.
+ if (options.quickImports && usesQml2) {
+ const QString targetPath = options.qmlDirectory.isEmpty()
+ ? options.directory + QStringLiteral("/qml")
+ : options.qmlDirectory;
+ if (!createDirectory(targetPath, errorMessage, options.dryRun))
+ return result;
+ for (const QmlImportScanResult::Module &module : std::as_const(qmlScanResult.modules)) {
+ const QString installPath = module.installPath(targetPath);
+ if (optVerboseLevel > 1)
+ std::wcout << "Installing: '" << module.name
+ << "' from " << module.sourcePath << " to "
+ << QDir::toNativeSeparators(installPath) << '\n';
+ if (installPath != targetPath && !createDirectory(installPath, errorMessage, options.dryRun))
+ return result;
+ unsigned updateFileFlags = options.updateFileFlags
+ | SkipQmlDesignerSpecificsDirectories;
+ unsigned qmlDirectoryFileFlags = 0;
+ if (options.deployPdb)
+ qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
+ if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(module.sourcePath,
+ options.platform,
+ debugMatchMode,
+ qmlDirectoryFileFlags),
+ installPath, updateFileFlags, options.json, errorMessage)) {
+ return result;
+ }
+ }
+ } // optQuickImports
+
+ if (options.translations) {
+ if (!createDirectory(options.translationsDirectory, errorMessage, options.dryRun))
+ return result;
+ if (!deployTranslations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS")),
+ result.deployedQtLibraries, options.translationsDirectory, options,
+ errorMessage)) {
+ return result;
+ }
+ }
+
+ result.success = true;
+ return result;
+}
+
+static bool deployWebProcess(const QMap<QString, QString> &qtpathsVariables, const char *binaryName,
+ const PluginInformation &pluginInfo, const Options &sourceOptions,
+ QString *errorMessage)
+{
+ // Copy the web process and its dependencies
+ const QString webProcess = webProcessBinary(binaryName, sourceOptions.platform);
+ const QString webProcessSource = qtpathsVariables.value(QStringLiteral("QT_INSTALL_LIBEXECS"))
+ + u'/' + webProcess;
+ if (!updateFile(webProcessSource, sourceOptions.directory, sourceOptions.updateFileFlags, sourceOptions.json, errorMessage))
+ return false;
+ Options options(sourceOptions);
+ options.binaries.append(options.directory + u'/' + webProcess);
+ options.quickImports = false;
+ options.translations = false;
+ return deploy(options, qtpathsVariables, pluginInfo, errorMessage);
+}
+
+static bool deployWebEngineCore(const QMap<QString, QString> &qtpathsVariables,
+ const PluginInformation &pluginInfo, const Options &options,
+ bool isDebug, QString *errorMessage)
+{
+ static const char *installDataFiles[] = { "icudtl.dat",
+ "qtwebengine_devtools_resources.pak",
+ "qtwebengine_resources.pak",
+ "qtwebengine_resources_100p.pak",
+ "qtwebengine_resources_200p.pak",
+ isDebug ? "v8_context_snapshot.debug.bin"
+ : "v8_context_snapshot.bin" };
+ QByteArray webEngineProcessName(webEngineProcessC);
+ if (isDebug && platformHasDebugSuffix(options.platform))
+ webEngineProcessName.append('d');
+ if (optVerboseLevel)
+ std::wcout << "Deploying: " << webEngineProcessName.constData() << "...\n";
+ if (!deployWebProcess(qtpathsVariables, webEngineProcessName, pluginInfo, options, errorMessage))
+ return false;
+ const QString resourcesSubDir = QStringLiteral("/resources");
+ const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_DATA"))
+ + resourcesSubDir + u'/';
+ const QString resourcesTargetDir(options.directory + resourcesSubDir);
+ if (!createDirectory(resourcesTargetDir, errorMessage, options.dryRun))
+ return false;
+ for (auto installDataFile : installDataFiles) {
+ if (!updateFile(resourcesSourceDir + QLatin1StringView(installDataFile),
+ resourcesTargetDir, options.updateFileFlags, options.json, errorMessage)) {
+ return false;
+ }
+ }
+ const QFileInfo translations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS"))
+ + QStringLiteral("/qtwebengine_locales"));
+ if (!translations.isDir()) {
+ std::wcerr << "Warning: Cannot find the translation files of the QtWebEngine module at "
+ << QDir::toNativeSeparators(translations.absoluteFilePath()) << ".\n";
+ return true;
+ }
+ if (options.translations) {
+ // Copy the whole translations directory.
+ return createDirectory(options.translationsDirectory, errorMessage, options.dryRun)
+ && updateFile(translations.absoluteFilePath(), options.translationsDirectory,
+ options.updateFileFlags, options.json, errorMessage);
+ }
+ // Translations have been turned off, but QtWebEngine needs at least one.
+ const QFileInfo enUSpak(translations.filePath() + QStringLiteral("/en-US.pak"));
+ if (!enUSpak.exists()) {
+ std::wcerr << "Warning: Cannot find "
+ << QDir::toNativeSeparators(enUSpak.absoluteFilePath()) << ".\n";
+ return true;
+ }
+ const QString webEngineTranslationsDir = options.translationsDirectory + u'/'
+ + translations.fileName();
+ if (!createDirectory(webEngineTranslationsDir, errorMessage, options.dryRun))
+ return false;
+ return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir,
+ options.updateFileFlags, options.json, errorMessage);
+}
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+int main(int argc, char **argv)
+{
+ QCoreApplication a(argc, argv);
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR ""_L1);
+
+ const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
+ QByteArray path = qgetenv("PATH");
+ if (!path.contains(qtBinPath)) { // QTBUG-39177, ensure Qt is in the path so that qt.conf is taken into account.
+ path.prepend(QDir::listSeparator().toLatin1());
+ path.prepend(qtBinPath);
+ qputenv("PATH", path);
+ }
+
+ Options options;
+ QString errorMessage;
+
+ // Early parse the --qmake and --qtpaths options, because they are needed to determine the
+ // options that select/deselect Qt modules.
+ {
+ int result = parseEarlyArguments(QCoreApplication::arguments(), &options, &errorMessage);
+ if (result & CommandLineParseError) {
+ std::wcerr << "Error: " << errorMessage << "\n";
+ return 1;
+ }
+ }
+
+ const QMap<QString, QString> qtpathsVariables =
+ queryQtPaths(options.qtpathsBinary, &errorMessage);
+ const QString xSpec = qtpathsVariables.value(QStringLiteral("QMAKE_XSPEC"));
+ if (qtpathsVariables.isEmpty() || xSpec.isEmpty()
+ || !qtpathsVariables.contains(QStringLiteral("QT_INSTALL_BINS"))) {
+ std::wcerr << "Unable to query qtpaths: " << errorMessage << '\n';
+ return 1;
+ }
+
+ options.platform = platformFromMkSpec(xSpec);
+ if (options.platform == UnknownPlatform) {
+ std::wcerr << "Unsupported platform " << xSpec << '\n';
+ return 1;
+ }
+
+ // Read the Qt module information from the Qt installation directory.
+ const QString modulesDir
+ = qtpathsVariables.value(QLatin1String("QT_INSTALL_ARCHDATA"))
+ + QLatin1String("/modules");
+ const QString translationsDir
+ = qtpathsVariables.value(QLatin1String("QT_INSTALL_TRANSLATIONS"));
+ if (!qtModuleEntries.populate(modulesDir, translationsDir, optVerboseLevel > 1,
+ &errorMessage)) {
+ std::wcerr << "Error: " << errorMessage << "\n";
+ return 1;
+ }
+ assignKnownModuleIds();
+
+ // Read the Qt plugin types information from the Qt installation directory.
+ PluginInformation pluginInfo{};
+ pluginInfo.generateAvailablePlugins(qtpathsVariables, options.platform);
+
+ // Parse the full command line.
+ {
+ QCommandLineParser parser;
+ QString errorMessage;
+ const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
+ if (result & CommandLineParseError)
+ std::wcerr << errorMessage << "\n\n";
+ if (result & CommandLineVersionRequested) {
+ std::fputs(QT_VERSION_STR "\n", stdout);
+ return 0;
+ }
+ if (result & CommandLineParseHelpRequested)
+ std::fputs(qPrintable(helpText(parser, pluginInfo)), stdout);
+ if (result & CommandLineParseError)
+ return 1;
+ if (result & CommandLineParseHelpRequested)
+ return 0;
+ }
+
+ // Create directories
+ if (!createDirectory(options.directory, &errorMessage, options.dryRun)) {
+ std::wcerr << errorMessage << '\n';
+ return 1;
+ }
+ if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
+ && !createDirectory(options.libraryDirectory, &errorMessage, options.dryRun)) {
+ std::wcerr << errorMessage << '\n';
+ return 1;
+ }
+
+ const DeployResult result = deploy(options, qtpathsVariables, pluginInfo, &errorMessage);
+ if (!result) {
+ std::wcerr << errorMessage << '\n';
+ return 1;
+ }
+
+ if (result.deployedQtLibraries.test(QtWebEngineCoreModuleId)) {
+ if (!deployWebEngineCore(qtpathsVariables, pluginInfo, options, result.isDebug,
+ &errorMessage)) {
+ std::wcerr << errorMessage << '\n';
+ return 1;
+ }
+ }
+
+ if (options.json) {
+ if (options.list)
+ std::fputs(options.json->toList(options.list, options.directory).constData(), stdout);
+ else
+ std::fputs(options.json->toJson().constData(), stdout);
+ delete options.json;
+ options.json = nullptr;
+ }
+
+ return 0;
+}
diff --git a/src/tools/windeployqt/qmlutils.cpp b/src/tools/windeployqt/qmlutils.cpp
new file mode 100644
index 0000000000..a7e63e7470
--- /dev/null
+++ b/src/tools/windeployqt/qmlutils.cpp
@@ -0,0 +1,138 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmlutils.h"
+#include "utils.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonParseError>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2)
+{
+ return m1.className.isEmpty() ? m1.name == m2.name : m1.className == m2.className;
+}
+
+// Return install path (cp -r semantics)
+QString QmlImportScanResult::Module::installPath(const QString &root) const
+{
+ QString result = root;
+ const qsizetype lastSlashPos = relativePath.lastIndexOf(u'/');
+ if (lastSlashPos != -1) {
+ result += u'/';
+ result += QStringView{relativePath}.left(lastSlashPos);
+ }
+ return result;
+}
+
+static QString qmlDirectoryRecursion(Platform platform, const QString &path)
+{
+ QDir dir(path);
+ if (!dir.entryList(QStringList(QStringLiteral("*.qml")), QDir::Files, QDir::NoSort).isEmpty())
+ return dir.path();
+ const QFileInfoList &subDirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort);
+ for (const QFileInfo &subDirFi : subDirs) {
+ if (!isBuildDirectory(platform, subDirFi.fileName())) {
+ const QString subPath = qmlDirectoryRecursion(platform, subDirFi.absoluteFilePath());
+ if (!subPath.isEmpty())
+ return subPath;
+ }
+ }
+ return QString();
+}
+
+// Find a directory containing QML files in the project
+QString findQmlDirectory(Platform platform, const QString &startDirectoryName)
+{
+ QDir startDirectory(startDirectoryName);
+ if (isBuildDirectory(platform, startDirectory.dirName()))
+ startDirectory.cdUp();
+ return qmlDirectoryRecursion(platform, startDirectory.path());
+}
+
+static void findFileRecursion(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode, QStringList *matches)
+{
+ const QStringList &dlls = findSharedLibraries(directory, platform, debugMatchMode);
+ for (const QString &dll : dlls)
+ matches->append(directory.filePath(dll));
+ const QFileInfoList &subDirs = directory.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for (const QFileInfo &subDirFi : subDirs) {
+ QDir subDirectory(subDirFi.absoluteFilePath());
+ // Don't enter other QML modules when recursing!
+ if (subDirectory.isReadable() && !subDirectory.exists(QStringLiteral("qmldir")))
+ findFileRecursion(subDirectory, platform, debugMatchMode, matches);
+ }
+}
+
+QmlImportScanResult runQmlImportScanner(const QString &directory, const QStringList &qmlImportPaths,
+ bool usesWidgets, int platform, DebugMatchMode debugMatchMode,
+ QString *errorMessage)
+{
+ Q_UNUSED(usesWidgets);
+ QmlImportScanResult result;
+ QStringList arguments;
+ for (const QString &importPath : qmlImportPaths)
+ arguments << QStringLiteral("-importPath") << importPath;
+ arguments << QStringLiteral("-rootPath") << directory;
+ unsigned long exitCode;
+ QByteArray stdOut;
+ QByteArray stdErr;
+ const QString binary = QStringLiteral("qmlimportscanner");
+ if (!runProcess(binary, arguments, QDir::currentPath(), &exitCode, &stdOut, &stdErr, errorMessage))
+ return result;
+ if (exitCode) {
+ *errorMessage = binary + QStringLiteral(" returned ") + QString::number(exitCode)
+ + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
+ return result;
+ }
+ QJsonParseError jsonParseError{};
+ const QJsonDocument data = QJsonDocument::fromJson(stdOut, &jsonParseError);
+ if (data.isNull() ) {
+ *errorMessage = binary + QStringLiteral(" returned invalid JSON output: ")
+ + jsonParseError.errorString() + QStringLiteral(" :\"")
+ + QString::fromLocal8Bit(stdOut) + u'"';
+ return result;
+ }
+ const QJsonArray array = data.array();
+ const int childCount = array.count();
+ for (int c = 0; c < childCount; ++c) {
+ const QJsonObject object = array.at(c).toObject();
+ if (object.value(QStringLiteral("type")).toString() == "module"_L1) {
+ const QString path = object.value(QStringLiteral("path")).toString();
+ if (!path.isEmpty()) {
+ QmlImportScanResult::Module module;
+ module.name = object.value(QStringLiteral("name")).toString();
+ module.className = object.value(QStringLiteral("classname")).toString();
+ module.sourcePath = path;
+ module.relativePath = object.value(QStringLiteral("relativePath")).toString();
+ result.modules.append(module);
+ findFileRecursion(QDir(path), Platform(platform), debugMatchMode, &result.plugins);
+ }
+ }
+ }
+ result.ok = true;
+ return result;
+}
+
+void QmlImportScanResult::append(const QmlImportScanResult &other)
+{
+ for (const QmlImportScanResult::Module &module : other.modules) {
+ if (std::find(modules.cbegin(), modules.cend(), module) == modules.cend())
+ modules.append(module);
+ }
+ for (const QString &plugin : other.plugins) {
+ if (!plugins.contains(plugin))
+ plugins.append(plugin);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/windeployqt/qmlutils.h b/src/tools/windeployqt/qmlutils.h
new file mode 100644
index 0000000000..bff1fb3a9b
--- /dev/null
+++ b/src/tools/windeployqt/qmlutils.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLUTILS_H
+#define QMLUTILS_H
+
+#include "utils.h"
+
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+
+QString findQmlDirectory(Platform platform, const QString &startDirectoryName);
+
+struct QmlImportScanResult {
+ struct Module {
+ QString installPath(const QString &root) const;
+
+ QString name;
+ QString className;
+ QString sourcePath;
+ QString relativePath;
+ };
+
+ void append(const QmlImportScanResult &other);
+
+ bool ok = false;
+ QList<Module> modules;
+ QStringList plugins;
+};
+
+bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2);
+
+QmlImportScanResult runQmlImportScanner(const QString &directory, const QStringList &qmlImportPaths,
+ bool usesWidgets, int platform, DebugMatchMode debugMatchMode,
+ QString *errorMessage);
+
+QT_END_NAMESPACE
+
+#endif // QMLUTILS_H
diff --git a/src/tools/windeployqt/qtmoduleinfo.cpp b/src/tools/windeployqt/qtmoduleinfo.cpp
new file mode 100644
index 0000000000..b928a64478
--- /dev/null
+++ b/src/tools/windeployqt/qtmoduleinfo.cpp
@@ -0,0 +1,183 @@
+// 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 "qtmoduleinfo.h"
+#include "utils.h"
+
+#include <QDirListing>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QDebug>
+
+#include <iostream>
+#include <algorithm>
+#include <unordered_map>
+
+using namespace Qt::StringLiterals;
+
+static QStringList toStringList(const QJsonArray &jsonArray)
+{
+ QStringList result;
+ for (const auto &item : jsonArray) {
+ if (item.isString())
+ result.append(item.toString());
+ }
+ return result;
+}
+
+struct TranslationCatalog
+{
+ QString name;
+ QStringList repositories;
+ QStringList modules;
+};
+
+using TranslationCatalogs = std::vector<TranslationCatalog>;
+
+static TranslationCatalogs readTranslationsCatalogs(const QString &translationsDir,
+ bool verbose,
+ QString *errorString)
+{
+ QFile file(translationsDir + QLatin1String("/catalogs.json"));
+ if (verbose) {
+ std::wcerr << "Trying to read translation catalogs from \""
+ << qUtf8Printable(file.fileName()) << "\".\n";
+ }
+ if (!file.open(QIODevice::ReadOnly)) {
+ *errorString = QLatin1String("Cannot open ") + file.fileName();
+ return {};
+ }
+
+ QJsonParseError jsonParseError;
+ QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &jsonParseError);
+ if (jsonParseError.error != QJsonParseError::NoError) {
+ *errorString = jsonParseError.errorString();
+ return {};
+ }
+
+ if (!document.isArray()) {
+ *errorString = QLatin1String("Expected an array as root element of ") + file.fileName();
+ return {};
+ }
+
+ TranslationCatalogs catalogs;
+ for (const QJsonValueRef &item : document.array()) {
+ TranslationCatalog catalog;
+ catalog.name = item[QLatin1String("name")].toString();
+ catalog.repositories = toStringList(item[QLatin1String("repositories")].toArray());
+ catalog.modules = toStringList(item[QLatin1String("modules")].toArray());
+ if (verbose)
+ std::wcerr << "Found catalog \"" << qUtf8Printable(catalog.name) << "\".\n";
+ catalogs.emplace_back(std::move(catalog));
+ }
+
+ return catalogs;
+}
+
+static QtModule moduleFromJsonFile(const QString &filePath, QString *errorString)
+{
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ *errorString = QLatin1String("Cannot open ") + file.fileName();
+ return {};
+ }
+
+ QJsonParseError jsonParseError;
+ QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &jsonParseError);
+ if (jsonParseError.error != QJsonParseError::NoError) {
+ *errorString = jsonParseError.errorString();
+ return {};
+ }
+
+ if (!document.isObject()) {
+ *errorString = QLatin1String("Expected an object as root element of ") + file.fileName();
+ return {};
+ }
+
+ const QJsonObject obj = document.object();
+ QtModule module;
+ module.name = "Qt6"_L1 + obj[QLatin1String("name")].toString();
+ module.repository = obj[QLatin1String("repository")].toString();
+ module.internal = obj[QLatin1String("internal")].toBool();
+ module.pluginTypes = toStringList(obj[QLatin1String("plugin_types")].toArray());
+ return module;
+}
+
+static void dump(const QtModule &module)
+{
+ std::wcerr << "Found module \"" << qUtf8Printable(module.name) << "\".\n";
+ if (!module.pluginTypes.isEmpty())
+ qDebug().nospace() << " plugin types: " << module.pluginTypes;
+ if (!module.translationCatalog.isEmpty())
+ qDebug().nospace() << " translation catalog: "<< module.translationCatalog;
+}
+
+bool QtModuleInfoStore::populate(const QString &modulesDir, const QString &translationsDir,
+ bool verbose, QString *errorString)
+{
+ const TranslationCatalogs catalogs = readTranslationsCatalogs(translationsDir, verbose,
+ errorString);
+ if (!errorString->isEmpty()) {
+ std::wcerr << "Warning: Translations will not be available due to the following error."
+ << std::endl << *errorString << std::endl;
+ errorString->clear();
+ }
+ std::unordered_map<QString, QString> moduleToCatalogMap;
+ std::unordered_map<QString, QString> repositoryToCatalogMap;
+ for (const TranslationCatalog &catalog : catalogs) {
+ for (const QString &module : catalog.modules) {
+ moduleToCatalogMap.insert(std::make_pair(module, catalog.name));
+ }
+ for (const QString &repository : catalog.repositories) {
+ repositoryToCatalogMap.insert(std::make_pair(repository, catalog.name));
+ }
+ }
+
+ // Read modules, and assign a bit as ID.
+ for (const auto &dirEntry : QDirListing(modulesDir, {u"*.json"_s}, QDir::Files)) {
+ QtModule module = moduleFromJsonFile(dirEntry.filePath(), errorString);
+ if (!errorString->isEmpty())
+ return false;
+ if (module.internal && module.name.endsWith(QStringLiteral("Private")))
+ module.name.chop(7);
+ module.id = modules.size();
+ if (module.id == QtModule::InvalidId) {
+ *errorString = "Internal Error: too many modules for ModuleBitset to hold."_L1;
+ return false;
+ }
+
+ {
+ auto it = moduleToCatalogMap.find(module.name);
+ if (it != moduleToCatalogMap.end())
+ module.translationCatalog = it->second;
+ }
+ if (module.translationCatalog.isEmpty()) {
+ auto it = repositoryToCatalogMap.find(module.repository);
+ if (it != repositoryToCatalogMap.end())
+ module.translationCatalog = it->second;
+ }
+ if (verbose)
+ dump(module);
+ modules.emplace_back(std::move(module));
+ }
+
+ return true;
+}
+
+const QtModule &QtModuleInfoStore::moduleById(size_t id) const
+{
+ return modules.at(id);
+}
+
+size_t QtModuleInfoStore::moduleIdForPluginType(const QString &pluginType) const
+{
+ auto moduleHasPluginType = [&pluginType] (const QtModule &module) {
+ return module.pluginTypes.contains(pluginType);
+ };
+
+ auto it = std::find_if(modules.begin(), modules.end(), moduleHasPluginType);
+ if (it != modules.end())
+ return it->id ;
+
+ return QtModule::InvalidId;
+}
diff --git a/src/tools/windeployqt/qtmoduleinfo.h b/src/tools/windeployqt/qtmoduleinfo.h
new file mode 100644
index 0000000000..b35403a090
--- /dev/null
+++ b/src/tools/windeployqt/qtmoduleinfo.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTMODULEINFO_H
+#define QTMODULEINFO_H
+
+#include <QString>
+#include <QStringList>
+
+#include <bitset>
+#include <vector>
+
+constexpr size_t ModuleBitsetSize = 1024;
+using ModuleBitset = std::bitset<ModuleBitsetSize>;
+
+struct QtModule
+{
+ static constexpr size_t InvalidId = ModuleBitsetSize - 1;
+ size_t id = InvalidId;
+ bool internal = false;
+ QString name;
+ QString repository;
+ QStringList pluginTypes;
+ QString translationCatalog;
+};
+
+inline bool contains(const ModuleBitset &modules, const QtModule &module)
+{
+ return modules.test(module.id);
+}
+
+class QtModuleInfoStore
+{
+public:
+ QtModuleInfoStore() = default;
+
+ bool populate(const QString &modulesDir, const QString &translationsDir, bool verbose,
+ QString *errorString);
+
+ size_t size() const { return modules.size(); }
+ std::vector<QtModule>::const_iterator begin() const { return modules.begin(); }
+ std::vector<QtModule>::const_iterator end() const { return modules.end(); }
+
+ const QtModule &moduleById(size_t id) const;
+ size_t moduleIdForPluginType(const QString &pluginType) const;
+
+private:
+ std::vector<QtModule> modules;
+};
+
+#endif
diff --git a/src/tools/windeployqt/qtplugininfo.cpp b/src/tools/windeployqt/qtplugininfo.cpp
new file mode 100644
index 0000000000..1deaa35f35
--- /dev/null
+++ b/src/tools/windeployqt/qtplugininfo.cpp
@@ -0,0 +1,100 @@
+// 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 "qtplugininfo.h"
+
+#include <QDir>
+
+static PluginDetection determinePluginLibrary(const QDir &platformPluginDir, const QString &infix)
+{
+ // Use the platform plugin to determine which dlls are there (release/debug/both)
+ QString platformReleaseFilter(QStringLiteral("qwindows"));
+ if (!infix.isEmpty())
+ platformReleaseFilter += infix;
+ QString platformFilter = platformReleaseFilter + u'*';
+ platformFilter += sharedLibrarySuffix();
+
+ const QFileInfoList &dlls =
+ platformPluginDir.entryInfoList(QStringList(platformFilter), QDir::Files);
+ if (dlls.size() == 1) {
+ const QFileInfo dllFi = dlls.first();
+ const bool hasDebugDlls =
+ dllFi.fileName() == QString(platformReleaseFilter + sharedLibrarySuffix()) ? false
+ : true;
+ return (hasDebugDlls ? PluginDetection::DebugOnly : PluginDetection::ReleaseOnly);
+ } else {
+ return PluginDetection::DebugAndRelease;
+ }
+}
+
+static QStringList findPluginNames(const QDir &pluginDir, const PluginDetection libraryType,
+ const Platform &platform)
+{
+ QString errorMessage{};
+ QStringList result{};
+ QString filter{};
+ filter += u"*";
+ filter += sharedLibrarySuffix();
+
+ const QFileInfoList &dlls =
+ pluginDir.entryInfoList(QStringList(filter), QDir::Files, QDir::Name);
+
+ for (const QFileInfo &dllFi : dlls) {
+ QString plugin = dllFi.fileName();
+ const int dotIndex = plugin.lastIndexOf(u'.');
+ // We don't need the .dll for the name
+ plugin = plugin.first(dotIndex);
+
+ if (libraryType == PluginDetection::DebugAndRelease) {
+ bool isDebugDll{};
+ if (!readPeExecutable(dllFi.absoluteFilePath(), &errorMessage, 0, 0, &isDebugDll,
+ (platform == WindowsDesktopMinGW))) {
+ std::wcerr << "Warning: Unable to read "
+ << QDir::toNativeSeparators(dllFi.absoluteFilePath()) << ": "
+ << errorMessage;
+ }
+ if (isDebugDll && platformHasDebugSuffix(platform))
+ plugin.removeLast();
+ }
+ else if (libraryType == PluginDetection::DebugOnly)
+ plugin.removeLast();
+
+ if (!result.contains(plugin))
+ result.append(plugin);
+ }
+ return result;
+}
+
+bool PluginInformation::isTypeForPlugin(const QString &type, const QString &plugin) const
+{
+ return m_pluginMap.at(plugin) == type;
+}
+
+void PluginInformation::populatePluginToType(const QDir &pluginDir, const QStringList &plugins)
+{
+ for (const QString &plugin : plugins)
+ m_pluginMap.insert({ plugin, pluginDir.dirName() });
+}
+
+void PluginInformation::generateAvailablePlugins(const QMap<QString, QString> &qtPathsVariables,
+ const Platform &platform)
+{
+ const QDir pluginTypesDir(qtPathsVariables.value(QLatin1String("QT_INSTALL_PLUGINS")));
+ const QDir platformPluginDir(pluginTypesDir.absolutePath() + QStringLiteral("/platforms"));
+ const QString infix(qtPathsVariables.value(QLatin1String(qmakeInfixKey)));
+ const PluginDetection debugDetection = determinePluginLibrary(platformPluginDir, infix);
+
+ const QFileInfoList &pluginTypes =
+ pluginTypesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QFileInfo &pluginType : pluginTypes) {
+ const QString pluginTypeName = pluginType.baseName();
+ m_typeMap.insert({ pluginTypeName, QStringList{} });
+ const QStringList plugins =
+ findPluginNames(pluginType.absoluteFilePath(), debugDetection, platform);
+ m_typeMap.at(pluginTypeName) = plugins;
+ populatePluginToType(pluginTypeName, plugins);
+ }
+ if (!m_typeMap.size() || !m_pluginMap.size())
+ std::wcerr << "Warning: could not parse available plugins properly, plugin "
+ "inclusion/exclusion options will not work\n";
+}
diff --git a/src/tools/windeployqt/qtplugininfo.h b/src/tools/windeployqt/qtplugininfo.h
new file mode 100644
index 0000000000..420b2b5e1a
--- /dev/null
+++ b/src/tools/windeployqt/qtplugininfo.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTPLUGININFO_H
+#define QTPLUGININFO_H
+
+#include "utils.h"
+
+#include <QString>
+#include <QStringList>
+
+#include <unordered_map>
+
+enum class PluginDetection
+{
+ DebugOnly,
+ ReleaseOnly,
+ DebugAndRelease
+};
+
+struct PluginSelections
+{
+ QStringList disabledPluginTypes;
+ QStringList enabledPluginTypes;
+ QStringList excludedPlugins;
+ QStringList includedPlugins;
+ bool includeSoftPlugins = false;
+};
+
+class PluginInformation
+{
+public:
+ PluginInformation() = default;
+
+ bool isTypeForPlugin(const QString &type, const QString &plugin) const;
+
+ void generateAvailablePlugins(const QMap<QString, QString> &qtPathsVariables,
+ const Platform &platform);
+ void populatePluginToType(const QDir &pluginDir, const QStringList &plugins);
+
+ const std::unordered_map<QString, QStringList> &typeMap() const { return m_typeMap; }
+
+private:
+ std::unordered_map<QString, QStringList> m_typeMap;
+ std::unordered_map<QString, QString> m_pluginMap;
+};
+
+#endif
diff --git a/src/tools/windeployqt/utils.cpp b/src/tools/windeployqt/utils.cpp
new file mode 100644
index 0000000000..5141119254
--- /dev/null
+++ b/src/tools/windeployqt/utils.cpp
@@ -0,0 +1,1022 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "utils.h"
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QScopedArrayPointer>
+#include <QtCore/QStandardPaths>
+#if defined(Q_OS_WIN)
+# include <QtCore/qt_windows.h>
+# include <QtCore/private/qsystemerror_p.h>
+# include <shlwapi.h>
+# include <delayimp.h>
+#else // Q_OS_WIN
+# include <sys/wait.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <stdlib.h>
+# include <string.h>
+# include <errno.h>
+# include <fcntl.h>
+#endif // !Q_OS_WIN
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+int optVerboseLevel = 1;
+
+bool isBuildDirectory(Platform platform, const QString &dirName)
+{
+ return (platform.testFlag(Msvc) || platform.testFlag(ClangMsvc))
+ && (dirName == "debug"_L1 || dirName == "release"_L1);
+}
+
+// Create a symbolic link by changing to the source directory to make sure the
+// link uses relative paths only (QFile::link() otherwise uses the absolute path).
+bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage)
+{
+ const QString oldDirectory = QDir::currentPath();
+ if (!QDir::setCurrent(source.absolutePath())) {
+ *errorMessage = QStringLiteral("Unable to change to directory %1.").arg(QDir::toNativeSeparators(source.absolutePath()));
+ return false;
+ }
+ QFile file(source.fileName());
+ const bool success = file.link(target);
+ QDir::setCurrent(oldDirectory);
+ if (!success) {
+ *errorMessage = QString::fromLatin1("Failed to create symbolic link %1 -> %2: %3")
+ .arg(QDir::toNativeSeparators(source.absoluteFilePath()),
+ QDir::toNativeSeparators(target), file.errorString());
+ return false;
+ }
+ return true;
+}
+
+bool createDirectory(const QString &directory, QString *errorMessage, bool dryRun)
+{
+ const QFileInfo fi(directory);
+ if (fi.isDir())
+ return true;
+ if (fi.exists()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.").
+ arg(QDir::toNativeSeparators(directory));
+ return false;
+ }
+ if (optVerboseLevel)
+ std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n";
+ if (!dryRun) {
+ QDir dir;
+ if (!dir.mkpath(directory)) {
+ *errorMessage = QString::fromLatin1("Could not create directory %1.")
+ .arg(QDir::toNativeSeparators(directory));
+ return false;
+ }
+ }
+ return true;
+}
+
+// Find shared libraries matching debug/Platform in a directory, return relative names.
+QStringList findSharedLibraries(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode,
+ const QString &prefix)
+{
+ QString nameFilter = prefix;
+ if (nameFilter.isEmpty())
+ nameFilter += u'*';
+ if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform))
+ nameFilter += u'd';
+ nameFilter += sharedLibrarySuffix();
+ QStringList result;
+ QString errorMessage;
+ const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files);
+ for (const QFileInfo &dllFi : dlls) {
+ const QString dllPath = dllFi.absoluteFilePath();
+ bool matches = true;
+ if (debugMatchMode != MatchDebugOrRelease && (platform & WindowsBased)) {
+ bool debugDll;
+ if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll,
+ (platform == WindowsDesktopMinGW))) {
+ matches = debugDll == (debugMatchMode == MatchDebug);
+ } else {
+ std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(dllPath)
+ << ": " << errorMessage;
+ }
+ } // Windows
+ if (matches)
+ result += dllFi.fileName();
+ } // for
+ return result;
+}
+
+#ifdef Q_OS_WIN
+
+// Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW()
+QString normalizeFileName(const QString &name)
+{
+ wchar_t shortBuffer[MAX_PATH];
+ const QString nativeFileName = QDir::toNativeSeparators(name);
+ if (!GetShortPathNameW(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), shortBuffer, MAX_PATH))
+ return name;
+ wchar_t result[MAX_PATH];
+ if (!GetLongPathNameW(shortBuffer, result, MAX_PATH))
+ return name;
+ return QDir::fromNativeSeparators(QString::fromWCharArray(result));
+}
+
+// Find a tool binary in the Windows SDK 8
+QString findSdkTool(const QString &tool)
+{
+ QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(u';');
+ const QByteArray sdkDir = qgetenv("WindowsSdkDir");
+ if (!sdkDir.isEmpty())
+ paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + "/Tools/x64"_L1);
+ return QStandardPaths::findExecutable(tool, paths);
+}
+
+// runProcess helper: Create a temporary file for stdout/stderr redirection.
+static HANDLE createInheritableTemporaryFile()
+{
+ wchar_t path[MAX_PATH];
+ if (!GetTempPath(MAX_PATH, path))
+ return INVALID_HANDLE_VALUE;
+ wchar_t name[MAX_PATH];
+ if (!GetTempFileName(path, L"temp", 0, name)) // Creates file.
+ return INVALID_HANDLE_VALUE;
+ SECURITY_ATTRIBUTES securityAttributes;
+ ZeroMemory(&securityAttributes, sizeof(securityAttributes));
+ securityAttributes.nLength = sizeof(securityAttributes);
+ securityAttributes.bInheritHandle = TRUE;
+ return CreateFile(name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
+ TRUNCATE_EXISTING,
+ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+}
+
+// runProcess helper: Rewind and read out a temporary file for stdout/stderr.
+static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result)
+{
+ if (SetFilePointer(handle, 0, 0, FILE_BEGIN) == 0xFFFFFFFF)
+ return;
+ char buf[1024];
+ DWORD bytesRead;
+ while (ReadFile(handle, buf, sizeof(buf), &bytesRead, NULL) && bytesRead)
+ result->append(buf, int(bytesRead));
+ CloseHandle(handle);
+}
+
+static inline void appendToCommandLine(const QString &argument, QString *commandLine)
+{
+ const bool needsQuote = argument.contains(u' ');
+ if (!commandLine->isEmpty())
+ commandLine->append(u' ');
+ if (needsQuote)
+ commandLine->append(u'"');
+ commandLine->append(argument);
+ if (needsQuote)
+ commandLine->append(u'"');
+}
+
+// runProcess: Run a command line process (replacement for QProcess which
+// does not exist in the bootstrap library).
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory,
+ unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
+ QString *errorMessage)
+{
+ if (exitCode)
+ *exitCode = 0;
+
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ STARTUPINFO myInfo;
+ GetStartupInfo(&myInfo);
+ si.hStdInput = myInfo.hStdInput;
+ si.hStdOutput = myInfo.hStdOutput;
+ si.hStdError = myInfo.hStdError;
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+ const QChar backSlash = u'\\';
+ QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory);
+ if (!nativeWorkingDir.endsWith(backSlash))
+ nativeWorkingDir += backSlash;
+
+ if (stdOut) {
+ si.hStdOutput = createInheritableTemporaryFile();
+ if (si.hStdOutput == INVALID_HANDLE_VALUE) {
+ if (errorMessage)
+ *errorMessage = QStringLiteral("Error creating stdout temporary file");
+ return false;
+ }
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ if (stdErr) {
+ si.hStdError = createInheritableTemporaryFile();
+ if (si.hStdError == INVALID_HANDLE_VALUE) {
+ if (errorMessage)
+ *errorMessage = QStringLiteral("Error creating stderr temporary file");
+ return false;
+ }
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ // Create a copy of the command line which CreateProcessW can modify.
+ QString commandLine;
+ appendToCommandLine(binary, &commandLine);
+ for (const QString &a : args)
+ appendToCommandLine(a, &commandLine);
+ if (optVerboseLevel > 1)
+ std::wcout << "Running: " << commandLine << '\n';
+
+ QScopedArrayPointer<wchar_t> commandLineW(new wchar_t[commandLine.size() + 1]);
+ commandLine.toWCharArray(commandLineW.data());
+ commandLineW[commandLine.size()] = 0;
+ if (!CreateProcessW(0, commandLineW.data(), 0, 0, /* InheritHandles */ TRUE, 0, 0,
+ reinterpret_cast<LPCWSTR>(nativeWorkingDir.utf16()), &si, &pi)) {
+ if (stdOut)
+ CloseHandle(si.hStdOutput);
+ if (stdErr)
+ CloseHandle(si.hStdError);
+ if (errorMessage) {
+ *errorMessage = QStringLiteral("CreateProcessW failed: ")
+ + QSystemError::windowsString();
+ }
+ return false;
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hThread);
+ if (exitCode)
+ GetExitCodeProcess(pi.hProcess, exitCode);
+ CloseHandle(pi.hProcess);
+
+ if (stdOut)
+ readTemporaryProcessFile(si.hStdOutput, stdOut);
+ if (stdErr)
+ readTemporaryProcessFile(si.hStdError, stdErr);
+ return true;
+}
+
+#else // Q_OS_WIN
+
+static inline char *encodeFileName(const QString &f)
+{
+ const QByteArray encoded = QFile::encodeName(f);
+ char *result = new char[encoded.size() + 1];
+ strcpy(result, encoded.constData());
+ return result;
+}
+
+static inline char *tempFilePattern()
+{
+ QString path = QDir::tempPath();
+ if (!path.endsWith(u'/'))
+ path += u'/';
+ path += QStringLiteral("tmpXXXXXX");
+ return encodeFileName(path);
+}
+
+static inline QByteArray readOutRedirectFile(int fd)
+{
+ enum { bufSize = 256 };
+
+ QByteArray result;
+ if (!lseek(fd, 0, 0)) {
+ char buf[bufSize];
+ while (true) {
+ const ssize_t rs = read(fd, buf, bufSize);
+ if (rs <= 0)
+ break;
+ result.append(buf, int(rs));
+ }
+ }
+ close(fd);
+ return result;
+}
+
+// runProcess: Run a command line process (replacement for QProcess which
+// does not exist in the bootstrap library).
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory,
+ unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
+ QString *errorMessage)
+{
+ QScopedArrayPointer<char> stdOutFileName;
+ QScopedArrayPointer<char> stdErrFileName;
+
+ int stdOutFile = 0;
+ if (stdOut) {
+ stdOutFileName.reset(tempFilePattern());
+ stdOutFile = mkstemp(stdOutFileName.data());
+ if (stdOutFile < 0) {
+ *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+ }
+
+ int stdErrFile = 0;
+ if (stdErr) {
+ stdErrFileName.reset(tempFilePattern());
+ stdErrFile = mkstemp(stdErrFileName.data());
+ if (stdErrFile < 0) {
+ *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+ }
+
+ const pid_t pID = fork();
+
+ if (pID < 0) {
+ *errorMessage = QStringLiteral("Fork failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+
+ if (!pID) { // Child
+ if (stdOut) {
+ dup2(stdOutFile, STDOUT_FILENO);
+ close(stdOutFile);
+ }
+ if (stdErr) {
+ dup2(stdErrFile, STDERR_FILENO);
+ close(stdErrFile);
+ }
+
+ if (!workingDirectory.isEmpty() && !QDir::setCurrent(workingDirectory)) {
+ std::wcerr << "Failed to change working directory to " << workingDirectory << ".\n";
+ ::_exit(-1);
+ }
+
+ char **argv = new char *[args.size() + 2]; // Create argv.
+ char **ap = argv;
+ *ap++ = encodeFileName(binary);
+ for (const QString &a : std::as_const(args))
+ *ap++ = encodeFileName(a);
+ *ap = 0;
+
+ execvp(argv[0], argv);
+ ::_exit(-1);
+ }
+
+ int status;
+ pid_t waitResult;
+
+ do {
+ waitResult = waitpid(pID, &status, 0);
+ } while (waitResult == -1 && errno == EINTR);
+
+ if (stdOut) {
+ *stdOut = readOutRedirectFile(stdOutFile);
+ unlink(stdOutFileName.data());
+ }
+ if (stdErr) {
+ *stdErr = readOutRedirectFile(stdErrFile);
+ unlink(stdErrFileName.data());
+ }
+
+ if (waitResult < 0) {
+ *errorMessage = QStringLiteral("Wait failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status)) {
+ *errorMessage = binary + QStringLiteral(" did not exit cleanly.");
+ return false;
+ }
+ if (exitCode)
+ *exitCode = WEXITSTATUS(status);
+ return true;
+}
+
+#endif // !Q_OS_WIN
+
+// Find a file in the path using ShellAPI. This can be used to locate DLLs which
+// QStandardPaths cannot do.
+QString findInPath(const QString &file)
+{
+#if defined(Q_OS_WIN)
+ if (file.size() < MAX_PATH - 1) {
+ wchar_t buffer[MAX_PATH];
+ file.toWCharArray(buffer);
+ buffer[file.size()] = 0;
+ if (PathFindOnPath(buffer, NULL))
+ return QDir::cleanPath(QString::fromWCharArray(buffer));
+ }
+ return QString();
+#else // Q_OS_WIN
+ return QStandardPaths::findExecutable(file);
+#endif // !Q_OS_WIN
+}
+
+const char *qmakeInfixKey = "QT_INFIX";
+
+QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *errorMessage)
+{
+ const QString binary = !qtpathsBinary.isEmpty() ? qtpathsBinary : QStringLiteral("qtpaths");
+ const QString colonSpace = QStringLiteral(": ");
+ QByteArray stdOut;
+ QByteArray stdErr;
+ unsigned long exitCode = 0;
+ if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut,
+ &stdErr, errorMessage)) {
+ *errorMessage = QStringLiteral("Error running binary ") + binary + colonSpace + *errorMessage;
+ return QMap<QString, QString>();
+ }
+ if (exitCode) {
+ *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
+ + colonSpace + QString::fromLocal8Bit(stdErr);
+ return QMap<QString, QString>();
+ }
+ const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(u'\r');
+ QMap<QString, QString> result;
+ const qsizetype size = output.size();
+ for (qsizetype pos = 0; pos < size; ) {
+ const qsizetype colonPos = output.indexOf(u':', pos);
+ if (colonPos < 0)
+ break;
+ qsizetype endPos = output.indexOf(u'\n', colonPos + 1);
+ if (endPos < 0)
+ endPos = size;
+ const QString key = output.mid(pos, colonPos - pos);
+ const QString value = output.mid(colonPos + 1, endPos - colonPos - 1);
+ result.insert(key, value);
+ pos = endPos + 1;
+ }
+ QFile qconfigPriFile(result.value(QStringLiteral("QT_HOST_DATA")) + QStringLiteral("/mkspecs/qconfig.pri"));
+ if (qconfigPriFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ while (true) {
+ const QByteArray line = qconfigPriFile.readLine();
+ if (line.isEmpty())
+ break;
+ if (line.startsWith("QT_LIBINFIX")) {
+ const int pos = line.indexOf('=');
+ if (pos >= 0) {
+ const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed());
+ if (!infix.isEmpty())
+ result.insert(QLatin1StringView(qmakeInfixKey), infix);
+ }
+ break;
+ }
+ }
+ } else {
+ std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName())
+ << colonSpace << qconfigPriFile.errorString()<< '\n';
+ }
+ return result;
+}
+
+// Update a file or directory.
+bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
+ const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + u'/' + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::wcout << "Checking " << sourceFileName << ", " << targetFileName<< '\n';
+
+ if (!sourceFileInfo.exists()) {
+ *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ if (sourceFileInfo.isSymLink()) {
+ *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).")
+ .arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ const QFileInfo targetFileInfo(targetFileName);
+
+ if (sourceFileInfo.isDir()) {
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isDir()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a directory.
+ } else { // exists.
+ QDir d(targetDirectory);
+ if (optVerboseLevel)
+ std::wcout << "Creating " << QDir::toNativeSeparators(targetFileName) << ".\n";
+ if (!(flags & SkipUpdateFile) && !d.mkdir(sourceFileInfo.fileName())) {
+ *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
+ .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
+ return false;
+ }
+ }
+ // Recurse into directory
+ QDir dir(sourceFileName);
+ const QFileInfoList allEntries = dir.entryInfoList(nameFilters, QDir::Files)
+ + dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QFileInfo &entryFi : allEntries) {
+ if (!updateFile(entryFi.absoluteFilePath(), nameFilters, targetFileName, flags, json, errorMessage))
+ return false;
+ }
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (!(flags & ForceUpdateFile)
+ && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::wcout << sourceFileInfo.fileName() << " is up to date.\n";
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !targetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
+ return false;
+ }
+ } // target exists
+ QFile file(sourceFileName);
+ if (optVerboseLevel)
+ std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n";
+ if (!(flags & SkipUpdateFile) && !file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+}
+
+#ifdef Q_OS_WIN
+
+static inline QString stringFromRvaPtr(const void *rvaPtr)
+{
+ return QString::fromLocal8Bit(static_cast<const char *>(rvaPtr));
+}
+
+// Helper for reading out PE executable files: Find a section header for an RVA
+// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
+template <class ImageNtHeader>
+const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva, const ImageNtHeader *nTHeader)
+{
+ const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
+ const IMAGE_SECTION_HEADER *sectionEnd = section + nTHeader->FileHeader.NumberOfSections;
+ for ( ; section < sectionEnd; ++section)
+ if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize))
+ return section;
+ return 0;
+}
+
+// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
+template <class ImageNtHeader>
+inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader, const void *imageBase)
+{
+ const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
+ if (!sectionHdr)
+ return 0;
+ const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
+ return static_cast<const char *>(imageBase) + rva - delta;
+}
+
+// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32
+template <class ImageNtHeader>
+inline unsigned ntHeaderWordSize(const ImageNtHeader *header)
+{
+ // defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC
+ enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
+ if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
+ return 32;
+ if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
+ return 64;
+ return 0;
+}
+
+// Helper for reading out PE executable files: Retrieve the NT image header of an
+// executable via the legacy DOS header.
+static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
+{
+ IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
+ // Check DOS header consistency
+ if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
+ || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
+ *errorMessage = QString::fromLatin1("DOS header check failed.");
+ return 0;
+ }
+ // Retrieve NT header
+ char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew;
+ IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
+ // check NT header consistency
+ if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
+ || ntHeaders->Signature != IMAGE_NT_SIGNATURE
+ || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
+ *errorMessage = QString::fromLatin1("NT header check failed.");
+ return 0;
+ }
+ // Check magic
+ if (!ntHeaderWordSize(ntHeaders)) {
+ *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is invalid.").
+ arg(ntHeaders->OptionalHeader.Magic);
+ return 0;
+ }
+ // Check section headers
+ IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
+ if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
+ *errorMessage = QString::fromLatin1("NT header section header check failed.");
+ return 0;
+ }
+ return ntHeaders;
+}
+
+// Helper for reading out PE executable files: Read out import sections from
+// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32.
+template <class ImageNtHeader>
+inline QStringList readImportSections(const ImageNtHeader *ntHeaders, const void *base, QString *errorMessage)
+{
+ // Get import directory entry RVA and read out
+ const DWORD importsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ if (!importsStartRVA) {
+ *errorMessage = QString::fromLatin1("Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.");
+ return QStringList();
+ }
+ const IMAGE_IMPORT_DESCRIPTOR *importDesc = static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, base));
+ if (!importDesc) {
+ *errorMessage = QString::fromLatin1("Failed to find IMAGE_IMPORT_DESCRIPTOR entry.");
+ return QStringList();
+ }
+ QStringList result;
+ for ( ; importDesc->Name; ++importDesc)
+ result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base)));
+
+ // Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx .
+ // Check on grAttr bit 1 whether this is the format using RVA's > VS 6
+ if (const DWORD delayedImportsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) {
+ const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, base));
+ for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc)
+ result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base)));
+ }
+
+ return result;
+}
+
+// Check for MSCV runtime (MSVCP90D.dll/MSVCP90.dll, MSVCP120D.dll/MSVCP120.dll,
+// VCRUNTIME140D.DLL/VCRUNTIME140.DLL (VS2015) or msvcp120d_app.dll/msvcp120_app.dll).
+enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntime };
+
+static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &dependentLibraries)
+{
+ for (const QString &lib : dependentLibraries) {
+ qsizetype pos = 0;
+ if (lib.startsWith("MSVCR"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("MSVCP"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("VCRUNTIME"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("VCCORLIB"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("CONCRT"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("UCRTBASE"_L1, Qt::CaseInsensitive)) {
+ qsizetype lastDotPos = lib.lastIndexOf(u'.');
+ pos = -1 == lastDotPos ? 0 : lastDotPos - 1;
+ }
+
+ if (pos > 0) {
+ const auto removeExtraSuffix = [&lib, &pos](const QString &suffix) -> void {
+ if (lib.contains(suffix, Qt::CaseInsensitive))
+ pos -= suffix.size();
+ };
+ removeExtraSuffix("_app"_L1);
+ removeExtraSuffix("_atomic_wait"_L1);
+ removeExtraSuffix("_codecvt_ids"_L1);
+ }
+
+ if (pos)
+ return lib.at(pos).toLower() == u'd' ? MsvcDebugRuntime : MsvcReleaseRuntime;
+ }
+ return NoMsvcRuntime;
+}
+
+template <class ImageNtHeader>
+inline QStringList determineDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
+ QString *errorMessage)
+{
+ return readImportSections(nth, fileMemory, errorMessage);
+}
+
+template <class ImageNtHeader>
+inline bool determineDebug(const ImageNtHeader *nth, const void *fileMemory,
+ QStringList *dependentLibrariesIn, QString *errorMessage)
+{
+ if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
+ return false;
+
+ const QStringList dependentLibraries = dependentLibrariesIn != nullptr ?
+ *dependentLibrariesIn :
+ determineDependentLibs(nth, fileMemory, errorMessage);
+
+ const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ // When an MSVC debug entry is present, check whether the debug runtime
+ // is actually used to detect -release / -force-debug-info builds.
+ const MsvcDebugRuntimeResult msvcrt = checkMsvcDebugRuntime(dependentLibraries);
+ if (msvcrt == NoMsvcRuntime)
+ return hasDebugEntry;
+ else
+ return hasDebugEntry && msvcrt == MsvcDebugRuntime;
+}
+
+template <class ImageNtHeader>
+inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
+ QStringList *dependentLibrariesIn,
+ bool *isDebugIn, QString *errorMessage)
+{
+ if (dependentLibrariesIn)
+ *dependentLibrariesIn = determineDependentLibs(nth, fileMemory, errorMessage);
+
+ if (isDebugIn)
+ *isDebugIn = determineDebug(nth, fileMemory, dependentLibrariesIn, errorMessage);
+}
+
+// Read a PE executable and determine dependent libraries, word size
+// and debug flags.
+bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibrariesIn, unsigned *wordSizeIn,
+ bool *isDebugIn, bool isMinGW, unsigned short *machineArchIn)
+{
+ bool result = false;
+ HANDLE hFile = NULL;
+ HANDLE hFileMap = NULL;
+ void *fileMemory = 0;
+
+ if (dependentLibrariesIn)
+ dependentLibrariesIn->clear();
+ if (wordSizeIn)
+ *wordSizeIn = 0;
+ if (isDebugIn)
+ *isDebugIn = false;
+
+ do {
+ // Create a memory mapping of the file
+ hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
+ *errorMessage = QString::fromLatin1("Cannot open '%1': %2")
+ .arg(peExecutableFileName, QSystemError::windowsString());
+ break;
+ }
+
+ hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (hFileMap == NULL) {
+ *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2")
+ .arg(peExecutableFileName, QSystemError::windowsString());
+ break;
+ }
+
+ fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
+ if (!fileMemory) {
+ *errorMessage = QString::fromLatin1("Cannot map '%1': %2")
+ .arg(peExecutableFileName, QSystemError::windowsString());
+ break;
+ }
+
+ const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
+ if (!ntHeaders)
+ break;
+
+ const unsigned wordSize = ntHeaderWordSize(ntHeaders);
+ if (wordSizeIn)
+ *wordSizeIn = wordSize;
+ if (wordSize == 32) {
+ determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders),
+ fileMemory, dependentLibrariesIn, isDebugIn, errorMessage);
+ } else {
+ determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders),
+ fileMemory, dependentLibrariesIn, isDebugIn, errorMessage);
+ }
+
+ if (machineArchIn)
+ *machineArchIn = ntHeaders->FileHeader.Machine;
+
+ result = true;
+ if (optVerboseLevel > 1) {
+ std::wcout << __FUNCTION__ << ": " << QDir::toNativeSeparators(peExecutableFileName)
+ << ' ' << wordSize << " bit";
+ if (isMinGW)
+ std::wcout << ", MinGW";
+ if (dependentLibrariesIn) {
+ std::wcout << ", dependent libraries: ";
+ if (optVerboseLevel > 2)
+ std::wcout << dependentLibrariesIn->join(u' ');
+ else
+ std::wcout << dependentLibrariesIn->size();
+ }
+ if (isDebugIn)
+ std::wcout << (*isDebugIn ? ", debug" : ", release");
+ std::wcout << '\n';
+ }
+ } while (false);
+
+ if (fileMemory)
+ UnmapViewOfFile(fileMemory);
+
+ if (hFileMap != NULL)
+ CloseHandle(hFileMap);
+
+ if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+
+ return result;
+}
+
+QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize)
+{
+ const QString prefix = QStringLiteral("D3Dcompiler_");
+ const QString suffix = QLatin1StringView(windowsSharedLibrarySuffix);
+ // Get the DLL from Kit 8.0 onwards
+ const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir"));
+ if (!kitDir.isEmpty()) {
+ QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/");
+ if (platform.testFlag(ArmBased)) {
+ redistDirPath += QStringLiteral("arm");
+ } else {
+ redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64");
+ }
+ QDir redistDir(redistDirPath);
+ if (redistDir.exists()) {
+ const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + u'*' + suffix), QDir::Files);
+ if (!files.isEmpty())
+ return files.front().absoluteFilePath();
+ }
+ }
+ QStringList candidateVersions;
+ for (int i = 47 ; i >= 40 ; --i)
+ candidateVersions.append(prefix + QString::number(i) + suffix);
+ // Check the bin directory of the Qt SDK (in case it is shadowed by the
+ // Windows system directory in PATH).
+ for (const QString &candidate : std::as_const(candidateVersions)) {
+ const QFileInfo fi(qtBinDir + u'/' + candidate);
+ if (fi.isFile())
+ return fi.absoluteFilePath();
+ }
+ // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47).
+ if (platform.testFlag(IntelBased)) {
+ QString errorMessage;
+ unsigned detectedWordSize;
+ for (const QString &candidate : std::as_const(candidateVersions)) {
+ const QString dll = findInPath(candidate);
+ if (!dll.isEmpty()
+ && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
+ && detectedWordSize == wordSize) {
+ return dll;
+ }
+ }
+ }
+ return QString();
+}
+
+QStringList findDxc(Platform platform, const QString &qtBinDir, unsigned wordSize)
+{
+ QStringList results;
+ const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir"));
+ const QString suffix = QLatin1StringView(windowsSharedLibrarySuffix);
+ for (QString prefix : { QStringLiteral("dxcompiler"), QStringLiteral("dxil") }) {
+ QString name = prefix + suffix;
+ if (!kitDir.isEmpty()) {
+ QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/");
+ if (platform.testFlag(ArmBased)) {
+ redistDirPath += wordSize == 32 ? QStringLiteral("arm") : QStringLiteral("arm64");
+ } else {
+ redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64");
+ }
+ QDir redistDir(redistDirPath);
+ if (redistDir.exists()) {
+ const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + u'*' + suffix), QDir::Files);
+ if (!files.isEmpty()) {
+ results.append(files.front().absoluteFilePath());
+ continue;
+ }
+ }
+ }
+ // Check the bin directory of the Qt SDK (in case it is shadowed by the
+ // Windows system directory in PATH).
+ const QFileInfo fi(qtBinDir + u'/' + name);
+ if (fi.isFile()) {
+ results.append(fi.absoluteFilePath());
+ continue;
+ }
+ // Try to find it in the PATH (e.g. the Vulkan SDK ships these, even if Windows itself doesn't).
+ if (platform.testFlag(IntelBased)) {
+ QString errorMessage;
+ unsigned detectedWordSize;
+ const QString dll = findInPath(name);
+ if (!dll.isEmpty()
+ && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
+ && detectedWordSize == wordSize)
+ {
+ results.append(dll);
+ continue;
+ }
+ }
+ }
+ return results;
+}
+
+#else // Q_OS_WIN
+
+bool readPeExecutable(const QString &, QString *errorMessage,
+ QStringList *, unsigned *, bool *, bool, unsigned short *)
+{
+ *errorMessage = QStringLiteral("Not implemented.");
+ return false;
+}
+
+QString findD3dCompiler(Platform, const QString &, unsigned)
+{
+ return QString();
+}
+
+QStringList findDxc(Platform, const QString &, unsigned)
+{
+ return QStringList();
+}
+
+#endif // !Q_OS_WIN
+
+// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=."
+bool patchQtCore(const QString &path, QString *errorMessage)
+{
+ if (optVerboseLevel)
+ std::wcout << "Patching " << QFileInfo(path).fileName() << "...\n";
+
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: %2").arg(
+ QDir::toNativeSeparators(path), file.errorString());
+ return false;
+ }
+ const QByteArray oldContent = file.readAll();
+
+ if (oldContent.isEmpty()) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: Could not read file content").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+ file.close();
+
+ QByteArray content = oldContent;
+
+ QByteArray prfxpath("qt_prfxpath=");
+ int startPos = content.indexOf(prfxpath);
+ if (startPos == -1) {
+ *errorMessage = QString::fromLatin1(
+ "Unable to patch %1: Could not locate pattern \"qt_prfxpath=\"").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+ startPos += prfxpath.length();
+ int endPos = content.indexOf(char(0), startPos);
+ if (endPos == -1) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: Internal error").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+
+ QByteArray replacement = QByteArray(endPos - startPos, char(0));
+ replacement[0] = '.';
+ content.replace(startPos, endPos - startPos, replacement);
+ if (content == oldContent)
+ return true;
+
+ if (!file.open(QIODevice::WriteOnly)
+ || (file.write(content) != content.size())) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: Could not write to file: %2").arg(
+ QDir::toNativeSeparators(path), file.errorString());
+ return false;
+ }
+ return true;
+}
+
+#ifdef Q_OS_WIN
+QString getArchString(unsigned short machineArch)
+{
+ switch (machineArch) {
+ case IMAGE_FILE_MACHINE_I386:
+ return QStringLiteral("x86");
+ case IMAGE_FILE_MACHINE_ARM:
+ return QStringLiteral("arm");
+ case IMAGE_FILE_MACHINE_AMD64:
+ return QStringLiteral("x64");
+ case IMAGE_FILE_MACHINE_ARM64:
+ return QStringLiteral("arm64");
+ default:
+ break;
+ }
+ return QString();
+}
+#endif // Q_OS_WIN
+
+QT_END_NAMESPACE
diff --git a/src/tools/windeployqt/utils.h b/src/tools/windeployqt/utils.h
new file mode 100644
index 0000000000..fb3ba0b40b
--- /dev/null
+++ b/src/tools/windeployqt/utils.h
@@ -0,0 +1,366 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QStringList>
+#include <QMap>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#include <QtCore/QDateTime>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonDocument>
+
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+enum PlatformFlag {
+ // OS
+ WindowsBased = 0x00001,
+ // CPU
+ IntelBased = 0x00010,
+ ArmBased = 0x00020,
+ // Compiler
+ Msvc = 0x00100,
+ MinGW = 0x00200,
+ ClangMsvc = 0x00400,
+ ClangMinGW = 0x00800,
+ // Platforms
+ WindowsDesktopMsvc = WindowsBased + Msvc,
+ WindowsDesktopMsvcIntel = WindowsDesktopMsvc + IntelBased,
+ WindowsDesktopMsvcArm = WindowsDesktopMsvc + ArmBased,
+ WindowsDesktopMinGW = WindowsBased + IntelBased + MinGW,
+ WindowsDesktopClangMsvc = WindowsBased + IntelBased + ClangMsvc,
+ WindowsDesktopClangMinGW = WindowsBased + IntelBased + ClangMinGW,
+ UnknownPlatform
+};
+
+Q_DECLARE_FLAGS(Platform, PlatformFlag)
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Platform)
+
+inline bool platformHasDebugSuffix(Platform p) // Uses 'd' debug suffix
+{
+ return p.testFlag(Msvc) || p.testFlag(ClangMsvc);
+}
+
+enum ListOption {
+ ListNone = 0,
+ ListSource,
+ ListTarget,
+ ListRelative,
+ ListMapping
+};
+
+inline std::wostream &operator<<(std::wostream &str, const QString &s)
+{
+#ifdef Q_OS_WIN
+ str << reinterpret_cast<const wchar_t *>(s.utf16());
+#else
+ str << s.toStdWString();
+#endif
+ return str;
+}
+
+// Container class for JSON output
+class JsonOutput
+{
+ using SourceTargetMapping = std::pair<QString, QString>;
+ using SourceTargetMappings = QList<SourceTargetMapping>;
+
+public:
+ void addFile(const QString &source, const QString &target)
+ {
+ m_files.append(SourceTargetMapping(source, target));
+ }
+
+ void removeTargetDirectory(const QString &targetDirectory)
+ {
+ for (int i = m_files.size() - 1; i >= 0; --i) {
+ if (m_files.at(i).second == targetDirectory)
+ m_files.removeAt(i);
+ }
+ }
+
+ QByteArray toJson() const
+ {
+ QJsonObject document;
+ QJsonArray files;
+ for (const SourceTargetMapping &mapping : m_files) {
+ QJsonObject object;
+ object.insert(QStringLiteral("source"), QDir::toNativeSeparators(mapping.first));
+ object.insert(QStringLiteral("target"), QDir::toNativeSeparators(mapping.second));
+ files.append(object);
+ }
+ document.insert(QStringLiteral("files"), files);
+ return QJsonDocument(document).toJson();
+ }
+ QByteArray toList(ListOption option, const QDir &base) const
+ {
+ QByteArray list;
+ for (const SourceTargetMapping &mapping : m_files) {
+ const QString source = QDir::toNativeSeparators(mapping.first);
+ const QString fileName = QFileInfo(mapping.first).fileName();
+ const QString target = QDir::toNativeSeparators(mapping.second) + QDir::separator() + fileName;
+ switch (option) {
+ case ListNone:
+ break;
+ case ListSource:
+ list += source.toUtf8() + '\n';
+ break;
+ case ListTarget:
+ list += target.toUtf8() + '\n';
+ break;
+ case ListRelative:
+ list += QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + '\n';
+ break;
+ case ListMapping:
+ list += '"' + source.toUtf8() + "\" \"" + QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + "\"\n";
+ break;
+ }
+ }
+ return list;
+ }
+private:
+ SourceTargetMappings m_files;
+};
+
+#ifdef Q_OS_WIN
+QString normalizeFileName(const QString &name);
+QString winErrorMessage(unsigned long error);
+QString findSdkTool(const QString &tool);
+#else // !Q_OS_WIN
+inline QString normalizeFileName(const QString &name) { return name; }
+#endif // !Q_OS_WIN
+
+static const char windowsSharedLibrarySuffix[] = ".dll";
+
+inline QString sharedLibrarySuffix() { return QLatin1StringView(windowsSharedLibrarySuffix); }
+bool isBuildDirectory(Platform platform, const QString &dirName);
+
+bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage);
+bool createDirectory(const QString &directory, QString *errorMessage, bool dryRun);
+QString findInPath(const QString &file);
+
+extern const char *qmakeInfixKey; // Fake key containing the libinfix
+
+QMap<QString, QString> queryQtPaths(const QString &qmakeBinary, QString *errorMessage);
+
+enum DebugMatchMode {
+ MatchDebug,
+ MatchRelease,
+ MatchDebugOrRelease
+};
+
+QStringList findSharedLibraries(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode,
+ const QString &prefix = QString());
+
+bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
+ const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage);
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory = QString(),
+ unsigned long *exitCode = 0, QByteArray *stdOut = 0, QByteArray *stdErr = 0,
+ QString *errorMessage = 0);
+
+bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibraries = 0, unsigned *wordSize = 0,
+ bool *isDebug = 0, bool isMinGW = false, unsigned short *machineArch = nullptr);
+
+#ifdef Q_OS_WIN
+# if !defined(IMAGE_FILE_MACHINE_ARM64)
+# define IMAGE_FILE_MACHINE_ARM64 0xAA64
+# endif
+QString getArchString (unsigned short machineArch);
+#endif // Q_OS_WIN
+
+// Return dependent modules of executable files.
+
+inline QStringList findDependentLibraries(const QString &executableFileName, QString *errorMessage)
+{
+ QStringList result;
+ readPeExecutable(executableFileName, errorMessage, &result);
+ return result;
+}
+
+QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize);
+QStringList findDxc(Platform platform, const QString &qtBinDir, unsigned wordSize);
+
+bool patchQtCore(const QString &path, QString *errorMessage);
+
+extern int optVerboseLevel;
+
+// Recursively update a file or directory, matching DirectoryFileEntryFunction against the QDir
+// to obtain the files.
+enum UpdateFileFlag {
+ ForceUpdateFile = 0x1,
+ SkipUpdateFile = 0x2,
+ RemoveEmptyQmlDirectories = 0x4,
+ SkipQmlDesignerSpecificsDirectories = 0x8
+};
+
+template <class DirectoryFileEntryFunction>
+bool updateFile(const QString &sourceFileName,
+ DirectoryFileEntryFunction directoryFileEntryFunction,
+ const QString &targetDirectory,
+ unsigned flags,
+ JsonOutput *json,
+ QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + u'/' + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::wcout << "Checking " << sourceFileName << ", " << targetFileName << '\n';
+
+ if (!sourceFileInfo.exists()) {
+ *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ const QFileInfo targetFileInfo(targetFileName);
+
+ if (sourceFileInfo.isSymLink()) {
+ const QString sourcePath = sourceFileInfo.symLinkTarget();
+ const QString relativeSource = QDir(sourceFileInfo.absolutePath()).relativeFilePath(sourcePath);
+ if (relativeSource.contains(u'/')) {
+ *errorMessage = QString::fromLatin1("Symbolic links across directories are not supported (%1).")
+ .arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ // Update the linked-to file
+ if (!updateFile(sourcePath, directoryFileEntryFunction, targetDirectory, flags, json, errorMessage))
+ return false;
+
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isSymLink()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a symbolic link.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a symlink
+ const QString relativeTarget = QDir(targetFileInfo.absolutePath()).relativeFilePath(targetFileInfo.symLinkTarget());
+ if (relativeSource == relativeTarget) // Exists and points to same entry: happy.
+ return true;
+ QFile existingTargetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !existingTargetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing symbolic link %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), existingTargetFile.errorString());
+ return false;
+ }
+ } // target symbolic link exists
+ return createSymbolicLink(QFileInfo(targetDirectory + u'/' + relativeSource), sourceFileInfo.fileName(), errorMessage);
+ } // Source is symbolic link
+
+ if (sourceFileInfo.isDir()) {
+ if ((flags & SkipQmlDesignerSpecificsDirectories) && sourceFileInfo.fileName() == QLatin1StringView("designer")) {
+ if (optVerboseLevel)
+ std::wcout << "Skipping " << QDir::toNativeSeparators(sourceFileName) << ".\n";
+ return true;
+ }
+ bool created = false;
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isDir()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a directory.
+ } else { // exists.
+ QDir d(targetDirectory);
+ if (optVerboseLevel)
+ std::wcout << "Creating " << targetFileName << ".\n";
+ if (!(flags & SkipUpdateFile)) {
+ created = d.mkdir(sourceFileInfo.fileName());
+ if (!created) {
+ *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
+ .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
+ return false;
+ }
+ }
+ }
+ // Recurse into directory
+ QDir dir(sourceFileName);
+
+ const QStringList allEntries = directoryFileEntryFunction(dir) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &entry : allEntries)
+ if (!updateFile(sourceFileName + u'/' + entry, directoryFileEntryFunction, targetFileName, flags, json, errorMessage))
+ return false;
+ // Remove empty directories, for example QML import folders for which the filter did not match.
+ if (created && (flags & RemoveEmptyQmlDirectories)) {
+ QDir d(targetFileName);
+ const QStringList entries = d.entryList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ if (entries.isEmpty() || (entries.size() == 1 && entries.first() == QLatin1StringView("qmldir"))) {
+ if (!d.removeRecursively()) {
+ *errorMessage = QString::fromLatin1("Cannot remove empty directory %1.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ }
+ if (json)
+ json->removeTargetDirectory(targetFileName);
+ }
+ }
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (!(flags & ForceUpdateFile)
+ && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::wcout << sourceFileInfo.fileName() << " is up to date.\n";
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !targetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
+ return false;
+ }
+ } // target exists
+ QFile file(sourceFileName);
+ if (optVerboseLevel)
+ std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n";
+ if (!(flags & SkipUpdateFile)) {
+ if (!file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ if (!(file.permissions() & QFile::WriteUser)) { // QTBUG-40152, clear inherited read-only attribute
+ QFile targetFile(targetFileName);
+ if (!targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser)) {
+ *errorMessage = QString::fromLatin1("Cannot set write permission on %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), file.errorString());
+ return false;
+ }
+ } // Check permissions
+ } // !SkipUpdateFile
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+}
+
+// Base class to filter files by name filters functions to be passed to updateFile().
+class NameFilterFileEntryFunction {
+public:
+ explicit NameFilterFileEntryFunction(const QStringList &nameFilters) : m_nameFilters(nameFilters) {}
+ QStringList operator()(const QDir &dir) const { return dir.entryList(m_nameFilters, QDir::Files); }
+
+private:
+ const QStringList m_nameFilters;
+};
+
+// Convenience for all files.
+inline bool updateFile(const QString &sourceFileName, const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage)
+{
+ return updateFile(sourceFileName, NameFilterFileEntryFunction(QStringList()), targetDirectory, flags, json, errorMessage);
+}
+
+QT_END_NAMESPACE
+
+#endif // UTILS_H