From 35bdeffa20a211011c60638aff519792e9758433 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 18 Oct 2024 13:29:29 +0200 Subject: [PATCH 01/47] VC: fix version control for directory caching Amends 42377c21e4132427b294c6c5711d3c413e67a983 Change-Id: Ie38e0e823e9654aaebcddd85946cc6f2f6d4e67b Reviewed-by: hjk --- src/plugins/coreplugin/vcsmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp index 5a189f39471..3a419d507c7 100644 --- a/src/plugins/coreplugin/vcsmanager.cpp +++ b/src/plugins/coreplugin/vcsmanager.cpp @@ -223,7 +223,7 @@ IVersionControl* VcsManager::findVersionControlForDirectory(const FilePath &inpu } // Register Vcs(s) with the cache - FilePath tmpDir = directory.absolutePath(); + FilePath tmpDir = directory.absoluteFilePath(); #if defined WITH_TESTS // Force caching of test directories (even though they do not exist): if (directory.startsWith(TEST_PREFIX)) From 4fd716ff09389d0c82e2b3a3f5a7335a7f9db12f Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 18 Oct 2024 15:32:07 +0200 Subject: [PATCH 02/47] Bump version to 15 beta2 Change-Id: I293d173b377ff91d8f0889e11662d262cfa2cefa Reviewed-by: Eike Ziller --- cmake/QtCreatorIDEBranding.cmake | 6 +++--- qbs/modules/qtc/qtc.qbs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake index 6af8acc0995..c4c6e6df2bc 100644 --- a/cmake/QtCreatorIDEBranding.cmake +++ b/cmake/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "14.0.82") # The IDE version. -set(IDE_VERSION_COMPAT "14.0.82") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "15.0.0-beta1") # The IDE display version. +set(IDE_VERSION "14.0.83") # The IDE version. +set(IDE_VERSION_COMPAT "14.0.83") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "15.0.0-beta2") # The IDE display version. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. set(IDE_DISPLAY_NAME "Qt Creator") # The IDE display name. diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index 6b323c3037a..4c56789b0c0 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -4,16 +4,16 @@ import qbs.FileInfo import qbs.Utilities Module { - property string qtcreator_display_version: '15.0.0-beta1' + property string qtcreator_display_version: '15.0.0-beta2' property string ide_version_major: '14' property string ide_version_minor: '0' - property string ide_version_release: '82' + property string ide_version_release: '83' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release property string ide_compat_version_major: '14' property string ide_compat_version_minor: '0' - property string ide_compat_version_release: '82' + property string ide_compat_version_release: '83' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release From 708c2d4f36fe14b19657dcc1c9588eb52cb12ffb Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 18 Oct 2024 13:32:13 +0200 Subject: [PATCH 03/47] IncrediBuild: Use newest plugin setup pattern Change-Id: I520d3bfbfa1bac2672b92a8e4876aeacb9d67392 Reviewed-by: Christian Stenger --- .../incredibuild/buildconsolebuildstep.cpp | 19 ++++++++++++++----- .../incredibuild/buildconsolebuildstep.h | 8 +------- .../incredibuild/ibconsolebuildstep.cpp | 19 ++++++++++++++----- src/plugins/incredibuild/ibconsolebuildstep.h | 8 +------- .../incredibuild/incredibuildplugin.cpp | 14 +++----------- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/plugins/incredibuild/buildconsolebuildstep.cpp b/src/plugins/incredibuild/buildconsolebuildstep.cpp index 2b8bcd2185a..280382b1422 100644 --- a/src/plugins/incredibuild/buildconsolebuildstep.cpp +++ b/src/plugins/incredibuild/buildconsolebuildstep.cpp @@ -298,12 +298,21 @@ void BuildConsoleBuildStep::setupOutputFormatter(OutputFormatter *formatter) // BuildConsoleStepFactory -BuildConsoleStepFactory::BuildConsoleStepFactory() +class BuildConsoleStepFactory final : public BuildStepFactory { - registerStep(IncrediBuild::Constants::BUILDCONSOLE_BUILDSTEP_ID); - setDisplayName(Tr::tr("IncrediBuild for Windows")); - setSupportedStepLists({ProjectExplorer::Constants::BUILDSTEPS_BUILD, - ProjectExplorer::Constants::BUILDSTEPS_CLEAN}); +public: + BuildConsoleStepFactory() + { + registerStep(IncrediBuild::Constants::BUILDCONSOLE_BUILDSTEP_ID); + setDisplayName(Tr::tr("IncrediBuild for Windows")); + setSupportedStepLists({ProjectExplorer::Constants::BUILDSTEPS_BUILD, + ProjectExplorer::Constants::BUILDSTEPS_CLEAN}); + } +}; + +void setupBuildConsoleStep() +{ + static BuildConsoleStepFactory theBuildConsoleStepFactory; } } // IncrediBuild::Internal diff --git a/src/plugins/incredibuild/buildconsolebuildstep.h b/src/plugins/incredibuild/buildconsolebuildstep.h index 813e8dcb325..b73f0d4fec1 100644 --- a/src/plugins/incredibuild/buildconsolebuildstep.h +++ b/src/plugins/incredibuild/buildconsolebuildstep.h @@ -3,14 +3,8 @@ #pragma once -#include - namespace IncrediBuild::Internal { -class BuildConsoleStepFactory final : public ProjectExplorer::BuildStepFactory -{ -public: - BuildConsoleStepFactory(); -}; +void setupBuildConsoleStep(); } // IncrediBuild::Internal diff --git a/src/plugins/incredibuild/ibconsolebuildstep.cpp b/src/plugins/incredibuild/ibconsolebuildstep.cpp index 8dd68baa601..0fad6cbe74a 100644 --- a/src/plugins/incredibuild/ibconsolebuildstep.cpp +++ b/src/plugins/incredibuild/ibconsolebuildstep.cpp @@ -100,12 +100,21 @@ void IBConsoleBuildStep::setupOutputFormatter(OutputFormatter *formatter) // IBConsoleStepFactory -IBConsoleStepFactory::IBConsoleStepFactory() +class IBConsoleStepFactory final : public BuildStepFactory { - registerStep(IncrediBuild::Constants::IBCONSOLE_BUILDSTEP_ID); - setDisplayName(Tr::tr("IncrediBuild for Linux")); - setSupportedStepLists({ProjectExplorer::Constants::BUILDSTEPS_BUILD, - ProjectExplorer::Constants::BUILDSTEPS_CLEAN}); +public: + IBConsoleStepFactory() + { + registerStep(IncrediBuild::Constants::IBCONSOLE_BUILDSTEP_ID); + setDisplayName(Tr::tr("IncrediBuild for Linux")); + setSupportedStepLists({ProjectExplorer::Constants::BUILDSTEPS_BUILD, + ProjectExplorer::Constants::BUILDSTEPS_CLEAN}); + } +}; + +void setupIBConsoleStep() +{ + static IBConsoleStepFactory theIBConsoleStepFactory; } } // IncrediBuild::Internal diff --git a/src/plugins/incredibuild/ibconsolebuildstep.h b/src/plugins/incredibuild/ibconsolebuildstep.h index e44d27e5570..a8fb7259891 100644 --- a/src/plugins/incredibuild/ibconsolebuildstep.h +++ b/src/plugins/incredibuild/ibconsolebuildstep.h @@ -3,14 +3,8 @@ #pragma once -#include - namespace IncrediBuild::Internal { -class IBConsoleStepFactory : public ProjectExplorer::BuildStepFactory -{ -public: - IBConsoleStepFactory(); -}; +void setupIBConsoleStep(); } // IncrediBuild::Internal diff --git a/src/plugins/incredibuild/incredibuildplugin.cpp b/src/plugins/incredibuild/incredibuildplugin.cpp index 384ee0a760b..dba7c5bd610 100644 --- a/src/plugins/incredibuild/incredibuildplugin.cpp +++ b/src/plugins/incredibuild/incredibuildplugin.cpp @@ -8,25 +8,17 @@ namespace IncrediBuild::Internal { -class IncrediBuildPluginPrivate -{ -public: - BuildConsoleStepFactory buildConsoleStepFactory; - IBConsoleStepFactory ibConsolStepFactory; -}; - class IncrediBuildPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "IncrediBuild.json") public: - void initialize() override + void initialize() final { - d = std::make_unique(); + setupBuildConsoleStep(); + setupIBConsoleStep(); } - - std::unique_ptr d; }; } // IncrediBuild::Internal From 0f90482970572973aab53588f9d64a882b701349 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Fri, 18 Oct 2024 16:57:01 +0200 Subject: [PATCH 04/47] Theme: Fix warning about missing theme color Fixes "flat.creatortheme" misses color setting for key "PaletteAccent" Amends: 5ced0731f0126277f0c73d385d5c359c85e2ee6c Change-Id: I8f7997ad71c4e720caf3115d329e3495a1a757e7 Reviewed-by: Christian Stenger --- src/libs/utils/theme/theme.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp index 4b41db86b02..0654dfc6ff5 100644 --- a/src/libs/utils/theme/theme.cpp +++ b/src/libs/utils/theme/theme.cpp @@ -316,7 +316,7 @@ void Theme::readSettings(QSettings &settings) for (int i = 0, total = e.keyCount(); i < total; ++i) { const QString key = QLatin1String(e.key(i)); if (!d->unresolvedPalette.contains(key)) { - if (i < PaletteWindow || i > PalettePlaceholderTextDisabled) + if (i < PaletteWindow || i > PaletteAccentDisabled) qWarning("Theme \"%s\" misses color setting for key \"%s\".", qPrintable(d->fileName), qPrintable(key)); From 01a3d636dbc38b55198f845c335d83b329dbac05 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 18 Oct 2024 15:21:09 +0200 Subject: [PATCH 05/47] Spinner: Fix destructor Delete the accociated overlay widget on destruction. Secure calls to overlay with QPointer. Change-Id: I25a76498c39f16b89ddcea8fef1ac934d3c8652b Reviewed-by: Marcus Tillmanns --- src/libs/solutions/spinner/spinner.cpp | 23 +++++++++++++++++------ src/libs/solutions/spinner/spinner.h | 6 +++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/libs/solutions/spinner/spinner.cpp b/src/libs/solutions/spinner/spinner.cpp index 479467ae58d..179608597ad 100644 --- a/src/libs/solutions/spinner/spinner.cpp +++ b/src/libs/solutions/spinner/spinner.cpp @@ -238,12 +238,19 @@ Spinner::Spinner(SpinnerSize size, QWidget *parent) : QObject(parent) , m_widget(new SpinnerOverlay(size, parent)) {} +Spinner::~Spinner() +{ + if (m_widget) + delete m_widget; +} + /*! Sets the size of the spinner to the given \a size. */ void Spinner::setSize(SpinnerSize size) { - m_widget->setSize(size); + if (m_widget) + m_widget->setSize(size); } /*! @@ -251,7 +258,8 @@ void Spinner::setSize(SpinnerSize size) */ void Spinner::setColor(const QColor &color) { - m_widget->setColor(color); + if (m_widget) + m_widget->setColor(color); } /*! @@ -260,7 +268,8 @@ void Spinner::setColor(const QColor &color) */ void Spinner::show() { - m_widget->show(); + if (m_widget) + m_widget->show(); } /*! @@ -268,7 +277,8 @@ void Spinner::show() */ void Spinner::hide() { - m_widget->hide(); + if (m_widget) + m_widget->hide(); } /*! @@ -276,7 +286,7 @@ void Spinner::hide() */ bool Spinner::isVisible() const { - return m_widget->isVisible(); + return m_widget ? m_widget->isVisible() : false; } /*! @@ -285,7 +295,8 @@ bool Spinner::isVisible() const */ void Spinner::setVisible(bool visible) { - m_widget->setVisible(visible); + if (m_widget) + m_widget->setVisible(visible); } static QString colorButtonStyleSheet(const QColor &bgColor) diff --git a/src/libs/solutions/spinner/spinner.h b/src/libs/solutions/spinner/spinner.h index 8dd166a8640..da67cb1de30 100644 --- a/src/libs/solutions/spinner/spinner.h +++ b/src/libs/solutions/spinner/spinner.h @@ -6,6 +6,7 @@ #include "spinner_global.h" +#include #include namespace SpinnerSolution { @@ -20,12 +21,15 @@ Q_ENUM_NS(SpinnerState) // TODO: SpinnerOverlay and SpinnerWidget? +class SpinnerOverlay; + class SPINNER_EXPORT Spinner : public QObject { Q_OBJECT public: explicit Spinner(SpinnerSize size, QWidget *parent = nullptr); + ~Spinner() override; void setSize(SpinnerSize size); void setColor(const QColor &color); void show(); @@ -34,7 +38,7 @@ public: void setVisible(bool visible); private: - class SpinnerOverlay *m_widget = nullptr; + QPointer m_widget; }; class SPINNER_EXPORT SpinnerWidget : public QWidget From 4b89c18e2373e4d7b0107d14dd11c024600d8e30 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Sun, 13 Oct 2024 03:26:56 +0300 Subject: [PATCH 06/47] Android: print crash logs even if they are not from same pid Crash logs can be printed by the system from a process other than the currently running app, in cases of crash, it's good to have such logs immediately available in the IDE. So, always print Fatal logs when the app is running. There are other potentially related errors or warnings that might be printed from different pids, but filtering those while keeping the logs uncluttered might need some more work, that could be done some other time. Ideally, all this work would be done using: ``` adb logcat -v color -v tag --uid ``` which prints logs of the app uid, which includes the process itself and crash/fatal logs, and doesn't print the pid which we're filtering out. However, that works only for Android 7+, so two log processing ways would have to be maintained. Change-Id: I90bff561a70b78f3c184ebd609df194cd7ef7984 Reviewed-by: Jarek Kobus --- src/plugins/android/androidrunnerworker.cpp | 22 +++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index c19ca7abba7..eaed72b3d23 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -415,7 +415,10 @@ static ExecutableItem logcatRecipe(const Storage &storage) const QString pidString = QString::number(storagePtr->m_processPID); for (const QByteArray &msg : std::as_const(lines)) { const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n'); - if (!line.contains(pidString)) + // Get type excluding the initial color characters + const QString msgType = line.mid(5, 2); + const bool isFatal = msgType == "F/"; + if (!line.contains(pidString) && !isFatal) continue; if (storagePtr->m_useCppDebugger) { @@ -427,32 +430,31 @@ static ExecutableItem logcatRecipe(const Storage &storage) static const QRegularExpression regExpLogcat{ "^\\x1B\\[[0-9]+m" // color - "(\\w/)" // message type 1. capture + "\\w/" // message type ".*" // source - "(\\(\\s*\\d*\\)):" // pid 2. capture + "(\\(\\s*\\d*\\)):" // pid 1. capture "\\s*" ".*" // message "\\x1B\\[[0-9]+m" // color "[\\n\\r]*$" }; - static QStringList errorMsgTypes{"F/", "E/", "W/"}; + static QStringList errorMsgTypes{"W/", "E/", "F/"}; const bool onlyError = channel == QProcess::StandardError; const QRegularExpressionMatch match = regExpLogcat.match(line); if (match.hasMatch()) { - const QString pidMatch = match.captured(2); + const QString pidMatch = match.captured(1); const QString cleanPidMatch = pidMatch.mid(1, pidMatch.size() - 2).trimmed(); - if (cleanPidMatch == pidString) { - const QString msgType = match.captured(1); - const QString output = QString(line).remove(pidMatch); + const QString output = QString(line).remove(pidMatch); + if (isFatal) { + storagePtr->m_glue->addStdErr(output); + } else if (cleanPidMatch == pidString) { if (onlyError || errorMsgTypes.contains(msgType)) storagePtr->m_glue->addStdErr(output); else storagePtr->m_glue->addStdOut(output); } } else { - // Get type excluding the initial color characters - const QString msgType = line.mid(5, 7); if (onlyError || errorMsgTypes.contains(msgType)) storagePtr->m_glue->addStdErr(line); else From 99e2f0a65a84d3f52effb15cbe37829b63ef9fdb Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Mon, 21 Oct 2024 00:25:50 +0200 Subject: [PATCH 07/47] ExtensionManager: Implement latest plugin status UI specification The switch to enable or disable the selected plugin moves from the lower right corner to the top right of the middle column. The "Restart Now" button is replaced by an InfoBar entry. The "Loaded"/"Installed" on the extension cards change to "Active"/"Inactive". Fixes: QTCREATORBUG-31856 Change-Id: Iaf79b16255db0c9b55cb683e75eccc949512a4d0 Reviewed-by: Marcus Tillmanns --- .../extensionmanagerwidget.cpp | 29 ++++++++++++------- .../extensionmanager/extensionsbrowser.cpp | 4 +-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/plugins/extensionmanager/extensionmanagerwidget.cpp b/src/plugins/extensionmanager/extensionmanagerwidget.cpp index 0239907c5c8..84242e99021 100644 --- a/src/plugins/extensionmanager/extensionmanagerwidget.cpp +++ b/src/plugins/extensionmanager/extensionmanagerwidget.cpp @@ -9,6 +9,7 @@ #include "extensionsmodel.h" #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +241,8 @@ private: QString m_currentVendor; }; +const char kRestartSetting[] = "RestartAfterPluginEnabledChanged"; + class PluginStatusWidget : public QWidget { public: @@ -246,9 +250,7 @@ public: : QWidget(parent) { m_label = new InfoLabel; - m_switch = new Switch(Tr::tr("Load on start")); - m_restartButton = new Button(Tr::tr("Restart Now"), Button::MediumPrimary); - m_restartButton->setVisible(false); + m_switch = new Switch(Tr::tr("Active")); m_pluginView.hide(); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); @@ -256,7 +258,6 @@ public: Column { m_label, m_switch, - m_restartButton, }.attachTo(this); connect(m_switch, &QCheckBox::clicked, this, [this](bool checked) { @@ -265,7 +266,18 @@ public: return; const bool doIt = m_pluginView.data().setPluginsEnabled({spec}, checked); if (doIt) { - m_restartButton->show(); + if (!ICore::infoBar()->canInfoBeAdded(kRestartSetting)) + return; + + Utils::InfoBarEntry info( + kRestartSetting, + Core::Tr::tr("Plugin changes will take effect after restart.")); + info.addCustomButton(Tr::tr("Restart Now"), [] { + ICore::infoBar()->removeInfo(kRestartSetting); + QTimer::singleShot(0, ICore::instance(), &ICore::restart); + }); + ICore::infoBar()->addInfo(info); + ExtensionSystem::PluginManager::writeSettings(); } else { m_switch->setChecked(!checked); @@ -274,8 +286,6 @@ public: connect(ExtensionSystem::PluginManager::instance(), &ExtensionSystem::PluginManager::pluginsChanged, this, &PluginStatusWidget::update); - connect(m_restartButton, &QAbstractButton::clicked, - ICore::instance(), &ICore::restart, Qt::QueuedConnection); update(); } @@ -311,7 +321,6 @@ private: InfoLabel *m_label; Switch *m_switch; - QAbstractButton *m_restartButton; QString m_pluginId; ExtensionSystem::PluginView m_pluginView{this}; }; @@ -493,7 +502,6 @@ ExtensionManagerWidget::ExtensionManagerWidget() WelcomePageHelpers::createRule(Qt::Vertical), Column { m_secondaryContent, - m_pluginStatus, }, noMargin, spacing(0), }.attachTo(m_secondaryDescriptionWidget); @@ -501,8 +509,9 @@ ExtensionManagerWidget::ExtensionManagerWidget() Row { Row { Column { - Column { + Row { m_headingWidget, + m_pluginStatus, customMargins(SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl), }, diff --git a/src/plugins/extensionmanager/extensionsbrowser.cpp b/src/plugins/extensionmanager/extensionsbrowser.cpp index d69b355224f..2ba17d22036 100644 --- a/src/plugins/extensionmanager/extensionsbrowser.cpp +++ b/src/plugins/extensionmanager/extensionsbrowser.cpp @@ -157,9 +157,9 @@ static QString extensionStateDisplayString(ExtensionState state) { switch (state) { case InstalledEnabled: - return Tr::tr("Loaded"); + return Tr::tr("Active"); case InstalledDisabled: - return Tr::tr("Installed"); + return Tr::tr("Inactive"); default: return {}; } From 20f01707875b1e5b29dcef6c04cd69e33b61440a Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 15 Oct 2024 11:11:53 +0200 Subject: [PATCH 08/47] Utils: Create unittest for MacroExpander Change-Id: Iba56391622ee1b28c33554e8bd71e63c511cb908 Reviewed-by: Oswald Buddenhagen Reviewed-by: hjk --- tests/auto/utils/CMakeLists.txt | 1 + tests/auto/utils/expander/CMakeLists.txt | 4 + tests/auto/utils/expander/expander.qbs | 9 + tests/auto/utils/expander/tst_expander.cpp | 251 +++++++++++++++++++++ tests/auto/utils/utils.qbs | 1 + 5 files changed, 266 insertions(+) create mode 100644 tests/auto/utils/expander/CMakeLists.txt create mode 100644 tests/auto/utils/expander/expander.qbs create mode 100644 tests/auto/utils/expander/tst_expander.cpp diff --git a/tests/auto/utils/CMakeLists.txt b/tests/auto/utils/CMakeLists.txt index ea4ce6bf889..2ce149800dd 100644 --- a/tests/auto/utils/CMakeLists.txt +++ b/tests/auto/utils/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(ansiescapecodehandler) add_subdirectory(async) add_subdirectory(commandline) add_subdirectory(deviceshell) +add_subdirectory(expander) add_subdirectory(expected) add_subdirectory(filepath) add_subdirectory(fileutils) diff --git a/tests/auto/utils/expander/CMakeLists.txt b/tests/auto/utils/expander/CMakeLists.txt new file mode 100644 index 00000000000..34d714ab8c0 --- /dev/null +++ b/tests/auto/utils/expander/CMakeLists.txt @@ -0,0 +1,4 @@ +add_qtc_test(tst_utils_expander + DEPENDS Utils + SOURCES tst_expander.cpp +) diff --git a/tests/auto/utils/expander/expander.qbs b/tests/auto/utils/expander/expander.qbs new file mode 100644 index 00000000000..e8b005ce1cf --- /dev/null +++ b/tests/auto/utils/expander/expander.qbs @@ -0,0 +1,9 @@ +QtcAutotest { + name: "Macro Expander autotest" + + Depends { name: "Utils" } + + files: [ + "tst_expander.cpp", + ] +} diff --git a/tests/auto/utils/expander/tst_expander.cpp b/tests/auto/utils/expander/tst_expander.cpp new file mode 100644 index 00000000000..4bb8fc18892 --- /dev/null +++ b/tests/auto/utils/expander/tst_expander.cpp @@ -0,0 +1,251 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include +#include + +#include + +using namespace Utils; + +class tst_expander : public QObject +{ + Q_OBJECT + +private slots: + void expandFilePath_data() + { + QTest::addColumn("input"); + QTest::addColumn("expected"); + QTest::addColumn>("setup"); + + std::function empty; + std::function home = [](MacroExpander &expander) { + expander.registerVariable("Home", "", [] { return QDir::homePath(); }); + }; + std::function remotehome = [](MacroExpander &expander) { + expander.registerVariable("Home", "", [] { return "ssh://127.0.0.1/home"; }); + }; + std::function buildfolderwithspecialchars = + [](MacroExpander &expander) { + expander.registerVariable("BuildFolder", "", [] { + return "C:/Test/Some.Dots.In.File"; + }); + }; + + QTest::newRow("empty") << FilePath() << FilePath() << empty; + QTest::newRow("no expansion") << FilePath("foo") << FilePath("foo") << empty; + QTest::newRow("no expansion with slash") + << FilePath("foo/bar") << FilePath("foo/bar") << empty; + + QTest::newRow("home") << FilePath("%{Home}/foo") + << FilePath::fromString(QDir::homePath() + "/foo") << home; + + QTest::newRow("remote-home") + << FilePath("%{Home}/foo") + << FilePath::fromParts(QString("ssh"), QString("127.0.0.1"), QString("/home/foo")) + << remotehome; + + QTest::newRow("colon") << FilePath("foo:bar") + << FilePath::fromParts({}, {}, QString("foo:bar")) << empty; + + QTest::newRow("win-dots") + << FilePath("%{BuildFolder}/Debug/my.file.with.dots.elf") + << FilePath::fromParts( + {}, {}, QString("C:/Test/Some.Dots.In.File/Debug/my.file.with.dots.elf")) + << buildfolderwithspecialchars; + } + + void expandFilePath() + { + QFETCH(FilePath, input); + QFETCH(FilePath, expected); + QFETCH(std::function, setup); + + MacroExpander expander; + + if (setup) + setup(expander); + + QCOMPARE(expander.expand(input), expected); + } + + void expandString_data() + { + QTest::addColumn("input"); + QTest::addColumn("expected"); + QTest::addColumn>("setup"); + + std::function empty; + std::function user = [](MacroExpander &expander) { + expander.registerVariable("UserName", "", [] { return "JohnDoe"; }); + expander.registerVariable("FirstName", "", [] { return "John"; }); + expander.registerVariable("LastName", "", [] { return "Doe"; }); + expander.registerVariable("Email", "", [] { return "john.doe@example.com"; }); + }; + + QTest::newRow("empty") << QString() << QString() << empty; + QTest::newRow("no expansion") << QString("foo") << QString("foo") << empty; + + QTest::newRow("simple-expansion") + << QString("My name is: %{FirstName}") << QString("My name is: John") << user; + + QTest::newRow("multiple-expansions") + << QString("My name is: %{FirstName} %{LastName} (%{UserName})") + << QString("My name is: John Doe (JohnDoe)") << user; + + QTest::newRow("email") << QString("My email is: %{Email}") + << QString("My email is: john.doe@example.com") << user; + } + + void expandString() + { + QFETCH(QString, input); + QFETCH(QString, expected); + QFETCH(std::function, setup); + + MacroExpander expander; + + if (setup) + setup(expander); + + QCOMPARE(expander.expand(input), expected); + } + + void subProvider() + { + MacroExpander expander; + expander.registerVariable("MainVar", "", [] { return "MainValue"; }); + expander.registerSubProvider([] { + static MacroExpander *sub = new MacroExpander; + sub->registerVariable("SubVar", "", [] { return "SubValue"; }); + return sub; + }); + + QCOMPARE(expander.expand(QString("%{MainVar} %{SubVar}")), QString("MainValue SubValue")); + QString resolved; + expander.resolveMacro("MainVar", &resolved); + QCOMPARE(resolved, QString("MainValue")); + + expander.resolveMacro("SubVar", &resolved); + QCOMPARE(resolved, QString("SubValue")); + } + + void expandByteArray_data() + { + QTest::addColumn("input"); + QTest::addColumn("expected"); + QTest::addColumn>("setup"); + + std::function empty; + std::function user = [](MacroExpander &expander) { + expander.registerVariable("UserName", "", [] { return "JohnDoe"; }); + expander.registerVariable("FirstName", "", [] { return "John"; }); + expander.registerVariable("LastName", "", [] { return "Doe"; }); + }; + + QTest::newRow("empty") << QByteArray() << QByteArray() << empty; + QTest::newRow("no expansion") + << QByteArray("\0\x10\xff", 3) << QByteArray("\0\x10\xff", 3) << empty; + + QTest::newRow("simple-expansion") + << QByteArray("My name is: %{FirstName}") << QByteArray("My name is: John") << user; + + QTest::newRow("with-zeroes") << QByteArray("My name is: \0%{FirstName}", 25) + << QByteArray("My name is: \0John", 17) << user; + } + void expandByteArray() + { + QFETCH(QByteArray, input); + QFETCH(QByteArray, expected); + QFETCH(std::function, setup); + + MacroExpander expander; + + if (setup) + setup(expander); + + QCOMPARE(expander.expand(input), expected); + } + + void expandCommandArgs_data() + { + QTest::addColumn("input"); + QTest::addColumn("expected"); + QTest::addColumn>("setup"); + + std::function empty; + std::function file = [](MacroExpander &expander) { + expander.registerVariable("file", "", [] { return "foo.txt"; }); + }; + + std::function withspace = [](MacroExpander &expander) { + expander.registerVariable("WithSpace", "", [] { return "This has spaces"; }); + }; + + QTest::newRow("empty") << QString() << QString() << empty; + QTest::newRow("no expansion") << QString("foo") << QString("foo") << empty; + + QTest::newRow("simple-expansion") + << QString("cat %{file}") << QString("cat foo.txt") << file; + + QTest::newRow("with-ticks") + << QString("echo -n 'foo %{file}'") << QString("echo -n 'foo foo.txt'") << file; + + QTest::newRow("with-ticks-env") << QString("file=\%{file} echo -n 'foo \"$file\"'") + << QString("file=foo.txt echo -n 'foo \"$file\"'") << file; + + if (Utils::HostOsInfo::isWindowsHost()) { + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") << withspace; + + QTest::newRow("with-spaces-manual") + << QString("echo \"Some: %{WithSpace}\"") + << QString("echo \"Some: This has spaces\"") << withspace; + + QTest::newRow("with-spaces-nested") + << QString("cmd /k \"echo %{WithSpace}\"") + << QString("cmd /k \"echo This has spaces\"") << withspace; + } else { + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") << withspace; + + QTest::newRow("with-spaces-pre-quoted") + << QString("echo 'Some: %{WithSpace}'") << QString("echo 'Some: This has spaces'") + << withspace; + + QTest::newRow("with-spaces-nested") + << QString("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") + << withspace; + + // Due to security concerns, backslash-escaping an expando is treated as a quoting error + QTest::newRow("backslash-escaping") + << QString("echo \\%{file}") << QString("echo \\%{file}") << file; + + QTest::newRow("expando-within-shell-substitution") + << QString("${VAR:-%{file}}") << QString("${VAR:-foo.txt}") << file; + QTest::newRow("expando-within-shell-substitution-with-space") + << QString("echo \"Some: ${VAR:-%{WithSpace}}\"") + << QString("echo \"Some: ${VAR:-This has spaces}\"") << withspace; + } + } + + void expandCommandArgs() + { + QFETCH(QString, input); + QFETCH(QString, expected); + QFETCH(std::function, setup); + + MacroExpander expander; + + if (setup) + setup(expander); + + QCOMPARE(expander.expandProcessArgs(input), expected); + } +}; + +QTEST_GUILESS_MAIN(tst_expander) + +#include "tst_expander.moc" diff --git a/tests/auto/utils/utils.qbs b/tests/auto/utils/utils.qbs index d7534afa8cc..14d18d29d58 100644 --- a/tests/auto/utils/utils.qbs +++ b/tests/auto/utils/utils.qbs @@ -7,6 +7,7 @@ Project { "async/async.qbs", "commandline/commandline.qbs", "deviceshell/deviceshell.qbs", + "expander/expander.qbs", "expected/expected.qbs", "filepath/filepath.qbs", "fileutils/fileutils.qbs", From 7f94d491913b442e88a657b971bc491cdd52fb6d Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 21 Oct 2024 13:57:05 +0200 Subject: [PATCH 09/47] Tests: Fix test input Change-Id: I514dbfaf398f215cf6d096d2b9c06c57fe6e0b00 Reviewed-by: Marcus Tillmanns --- tests/auto/utils/expander/tst_expander.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/utils/expander/tst_expander.cpp b/tests/auto/utils/expander/tst_expander.cpp index 4bb8fc18892..a1dac554fb8 100644 --- a/tests/auto/utils/expander/tst_expander.cpp +++ b/tests/auto/utils/expander/tst_expander.cpp @@ -193,7 +193,7 @@ private slots: QTest::newRow("with-ticks") << QString("echo -n 'foo %{file}'") << QString("echo -n 'foo foo.txt'") << file; - QTest::newRow("with-ticks-env") << QString("file=\%{file} echo -n 'foo \"$file\"'") + QTest::newRow("with-ticks-env") << QString("file=%{file} echo -n 'foo \"$file\"'") << QString("file=foo.txt echo -n 'foo \"$file\"'") << file; if (Utils::HostOsInfo::isWindowsHost()) { From 8b78b9de1c145816ea478986a21e5204cdf75f78 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 21 Oct 2024 13:51:01 +0200 Subject: [PATCH 10/47] ProjectExplorer: Do not detect clang executable twice This fixes a soft assert in ToolchainManager. Change-Id: I8d0e4482a2eaff8b021f24558842fec557d7b4fe Reviewed-by: hjk --- src/plugins/projectexplorer/gcctoolchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 508a80736ec..90c0d81f4d9 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1626,7 +1626,7 @@ Toolchains GccToolchainFactory::autoDetectSdkClangToolchain(const Toolchains &kn for (Toolchain * const existingTc : known) { if (existingTc->compilerCommand() == *compilerPath) - return {existingTc}; + return {}; } return {autoDetectToolchain({*compilerPath, Constants::C_LANGUAGE_ID}, GccToolchain::Clang)}; From 67f4a720a891fc52b2a38629d73a249fe7ddc209 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 17 Oct 2024 12:33:09 +0200 Subject: [PATCH 11/47] Utils: Remove AbstractMacroExpander It was only used by its own tests. Instead move the functionality into MacroExpander(Private). The existing unittests are moved into tst_expander. Change-Id: Ia54f659efa7976b17f47a0084900f98acc834939 Reviewed-by: hjk --- src/libs/utils/commandline.cpp | 8 +- src/libs/utils/commandline.h | 7 +- src/libs/utils/macroexpander.cpp | 127 +++++- src/libs/utils/macroexpander.h | 5 +- src/libs/utils/stringutils.cpp | 120 ----- src/libs/utils/stringutils.h | 23 - tests/auto/utils/expander/tst_expander.cpp | 422 ++++++++++++++++-- tests/auto/utils/process/tst_process.cpp | 304 ------------- .../utils/stringutils/tst_stringutils.cpp | 119 ----- 9 files changed, 522 insertions(+), 613 deletions(-) diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index b4fa60481ed..d5ad99a4178 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -766,7 +766,7 @@ static int quoteArgInternalWin(QString &ret, int bslashes) * \return false if the string could not be parsed and therefore no safe * substitution was possible */ -bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType osType) +bool ProcessArgs::expandMacros(QString *cmd, const FindMacro &findMacro, OsType osType) { QString str = *cmd; if (str.isEmpty()) @@ -775,7 +775,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o QString rsts; int varLen; int varPos = 0; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) + if (!(varLen = findMacro(str, &varPos, &rsts))) return true; int pos = 0; @@ -839,7 +839,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o str.replace(pos, varLen, rsts); pos += rsts.length(); varPos = pos; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) { + if (!(varLen = findMacro(str, &varPos, &rsts))) { // Don't leave immediately, as we may be in CrtNeedWord state which could // be still resolved, or we may have inserted trailing backslashes. varPos = INT_MAX; @@ -954,7 +954,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o str.replace(pos, varLen, rsts); pos += rsts.length(); varPos = pos; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) + if (!(varLen = findMacro(str, &varPos, &rsts))) break; continue; } diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index 92f9a996246..84a32384efb 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -59,9 +59,12 @@ public: static QStringList splitArgs(const QString &cmd, OsType osType, bool abortOnMeta = false, SplitError *err = nullptr, const Environment *env = nullptr, const QString *pwd = nullptr); + + using FindMacro = std::function; + //! Safely replace the expandos in a shell command - static bool expandMacros(QString *cmd, AbstractMacroExpander *mx, - OsType osType = HostOsInfo::hostOs()); + static bool expandMacros( + QString *cmd, const FindMacro &findMacro, OsType osType = HostOsInfo::hostOs()); /*! Iterate over arguments from a command line. * Assumes that the name of the actual command is *not* part of the line. diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 09bd95bc7bd..63360c93f09 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Utils { namespace Internal { @@ -27,12 +28,112 @@ const char kNativePathPostfix[] = ":NativePath"; const char kFileNamePostfix[] = ":FileName"; const char kFileBaseNamePostfix[] = ":FileBaseName"; -class MacroExpanderPrivate : public AbstractMacroExpander +class MacroExpanderPrivate { public: MacroExpanderPrivate() = default; - bool resolveMacro(const QString &name, QString *ret, QSet &seen) override + static bool validateVarName(const QString &varName) { return !varName.startsWith("JS:"); } + + bool expandNestedMacros(const QString &str, int *pos, QString *ret) + { + QString varName; + QString pattern, replace; + QString defaultValue; + QString *currArg = &varName; + QChar prev; + QChar c; + QChar replacementChar; + bool replaceAll = false; + + int i = *pos; + int strLen = str.length(); + varName.reserve(strLen - i); + for (; i < strLen; prev = c) { + c = str.at(i++); + if (c == '\\' && i < strLen) { + c = str.at(i++); + // For the replacement, do not skip the escape sequence when followed by a digit. + // This is needed for enabling convenient capture group replacement, + // like %{var/(.)(.)/\2\1}, without escaping the placeholders. + if (currArg == &replace && c.isDigit()) + *currArg += '\\'; + *currArg += c; + } else if (c == '}') { + if (varName.isEmpty()) { // replace "%{}" with "%" + *ret = QString('%'); + *pos = i; + return true; + } + QSet seen; + if (resolveMacro(varName, ret, seen)) { + *pos = i; + if (!pattern.isEmpty() && currArg == &replace) { + const QRegularExpression regexp(pattern); + if (regexp.isValid()) { + if (replaceAll) { + ret->replace(regexp, replace); + } else { + // There isn't an API for replacing once... + const QRegularExpressionMatch match = regexp.match(*ret); + if (match.hasMatch()) { + *ret = ret->left(match.capturedStart(0)) + + match.captured(0).replace(regexp, replace) + + ret->mid(match.capturedEnd(0)); + } + } + } + } + return true; + } + if (!defaultValue.isEmpty()) { + *pos = i; + *ret = defaultValue; + return true; + } + return false; + } else if (c == '{' && prev == '%') { + if (!expandNestedMacros(str, &i, ret)) + return false; + varName.chop(1); + varName += *ret; + } else if (currArg == &varName && c == '-' && prev == ':' && validateVarName(varName)) { + varName.chop(1); + currArg = &defaultValue; + } else if (currArg == &varName && (c == '/' || c == '#') && validateVarName(varName)) { + replacementChar = c; + currArg = &pattern; + if (i < strLen && str.at(i) == replacementChar) { + ++i; + replaceAll = true; + } + } else if (currArg == &pattern && c == replacementChar) { + currArg = &replace; + } else { + *currArg += c; + } + } + return false; + } + + int findMacro(const QString &str, int *pos, QString *ret) + { + forever { + int openPos = str.indexOf("%{", *pos); + if (openPos < 0) + return 0; + int varPos = openPos + 2; + if (expandNestedMacros(str, &varPos, ret)) { + *pos = openPos; + return varPos - openPos; + } + // An actual expansion may be nested into a "false" one, + // so we continue right after the last %{. + *pos = openPos + 2; + } + } + + bool resolveMacro(const QString &name, QString *ret, QSet &seen) { // Prevent loops: const int count = seen.count(); @@ -229,7 +330,7 @@ MacroExpander::~MacroExpander() */ bool MacroExpander::resolveMacro(const QString &name, QString *ret) const { - QSet seen; + QSet seen; return d->resolveMacro(name, ret, seen); } @@ -242,6 +343,16 @@ QString MacroExpander::value(const QByteArray &variable, bool *found) const return d->value(variable, found); } +static void expandMacros(QString *str, MacroExpanderPrivate *mx) +{ + QString rsts; + + for (int pos = 0; int len = mx->findMacro(*str, &pos, &rsts);) { + str->replace(pos, len, rsts); + pos += rsts.length(); + } +} + /*! * Returns \a stringWithVariables with all variables replaced by their values. * See the MacroExpander overview documentation for other ways to expand variables. @@ -261,7 +372,7 @@ QString MacroExpander::expand(const QString &stringWithVariables) const ++d->m_lockDepth; QString res = stringWithVariables; - Utils::expandMacros(&res, d); + expandMacros(&res, d); --d->m_lockDepth; @@ -302,10 +413,14 @@ QVariant MacroExpander::expandVariant(const QVariant &v) const return v; } -QString MacroExpander::expandProcessArgs(const QString &argsWithVariables) const +QString MacroExpander::expandProcessArgs(const QString &argsWithVariables, Utils::OsType osType) const { QString result = argsWithVariables; - const bool ok = ProcessArgs::expandMacros(&result, d); + const bool ok = ProcessArgs::expandMacros( + &result, + [this](const QString &str, int *pos, QString *ret) { return d->findMacro(str, pos, ret); }, + osType); + QTC_ASSERT(ok, qCDebug(expanderLog) << "Expanding failed: " << argsWithVariables); return result; } diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h index ad9a21a938b..5608b39e7a5 100644 --- a/src/libs/utils/macroexpander.h +++ b/src/libs/utils/macroexpander.h @@ -5,6 +5,8 @@ #include "utils_global.h" +#include "hostosinfo.h" + #include #include @@ -35,7 +37,8 @@ public: QByteArray expand(const QByteArray &stringWithVariables) const; QVariant expandVariant(const QVariant &v) const; - QString expandProcessArgs(const QString &argsWithVariables) const; + QString expandProcessArgs( + const QString &argsWithVariables, Utils::OsType osType = Utils::HostOsInfo::hostOs()) const; using PrefixFunction = std::function; using ResolverFunction = std::function; diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index d21c7cb9bc1..2aa2f760427 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -78,126 +78,6 @@ QTCREATOR_UTILS_EXPORT QString commonPrefix(const QStringList &strings) return strings.at(0).left(commonLength); } -static bool validateVarName(const QString &varName) -{ - return !varName.startsWith("JS:"); -} - -bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QString *ret) -{ - QString varName; - QString pattern, replace; - QString defaultValue; - QString *currArg = &varName; - QChar prev; - QChar c; - QChar replacementChar; - bool replaceAll = false; - - int i = *pos; - int strLen = str.length(); - varName.reserve(strLen - i); - for (; i < strLen; prev = c) { - c = str.at(i++); - if (c == '\\' && i < strLen) { - c = str.at(i++); - // For the replacement, do not skip the escape sequence when followed by a digit. - // This is needed for enabling convenient capture group replacement, - // like %{var/(.)(.)/\2\1}, without escaping the placeholders. - if (currArg == &replace && c.isDigit()) - *currArg += '\\'; - *currArg += c; - } else if (c == '}') { - if (varName.isEmpty()) { // replace "%{}" with "%" - *ret = QString('%'); - *pos = i; - return true; - } - QSet seen; - if (resolveMacro(varName, ret, seen)) { - *pos = i; - if (!pattern.isEmpty() && currArg == &replace) { - const QRegularExpression regexp(pattern); - if (regexp.isValid()) { - if (replaceAll) { - ret->replace(regexp, replace); - } else { - // There isn't an API for replacing once... - const QRegularExpressionMatch match = regexp.match(*ret); - if (match.hasMatch()) { - *ret = ret->left(match.capturedStart(0)) - + match.captured(0).replace(regexp, replace) - + ret->mid(match.capturedEnd(0)); - } - } - } - } - return true; - } - if (!defaultValue.isEmpty()) { - *pos = i; - *ret = defaultValue; - return true; - } - return false; - } else if (c == '{' && prev == '%') { - if (!expandNestedMacros(str, &i, ret)) - return false; - varName.chop(1); - varName += *ret; - } else if (currArg == &varName && c == '-' && prev == ':' && validateVarName(varName)) { - varName.chop(1); - currArg = &defaultValue; - } else if (currArg == &varName && (c == '/' || c == '#') && validateVarName(varName)) { - replacementChar = c; - currArg = &pattern; - if (i < strLen && str.at(i) == replacementChar) { - ++i; - replaceAll = true; - } - } else if (currArg == &pattern && c == replacementChar) { - currArg = &replace; - } else { - *currArg += c; - } - } - return false; -} - -int AbstractMacroExpander::findMacro(const QString &str, int *pos, QString *ret) -{ - forever { - int openPos = str.indexOf("%{", *pos); - if (openPos < 0) - return 0; - int varPos = openPos + 2; - if (expandNestedMacros(str, &varPos, ret)) { - *pos = openPos; - return varPos - openPos; - } - // An actual expansion may be nested into a "false" one, - // so we continue right after the last %{. - *pos = openPos + 2; - } -} - -QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx) -{ - QString rsts; - - for (int pos = 0; int len = mx->findMacro(*str, &pos, &rsts); ) { - str->replace(pos, len, rsts); - pos += rsts.length(); - } -} - -QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx) -{ - QString ret = str; - expandMacros(&ret, mx); - return ret; -} - QTCREATOR_UTILS_EXPORT QString stripAccelerator(const QString &text) { QString res = text; diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index 141ab7f1040..7b21f869fff 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -41,29 +41,6 @@ QTCREATOR_UTILS_EXPORT bool readMultiLineString(const QJsonValue &value, QString // Compare case insensitive and use case sensitive comparison in case of that being equal. QTCREATOR_UTILS_EXPORT int caseFriendlyCompare(const QString &a, const QString &b); -class QTCREATOR_UTILS_EXPORT AbstractMacroExpander -{ -public: - virtual ~AbstractMacroExpander() {} - // Not const, as it may change the state of the expander. - //! Find an expando to replace and provide a replacement string. - //! \param str The string to scan - //! \param pos Position to start scan on input, found position on output - //! \param ret Replacement string on output - //! \return Length of string part to replace, zero if no (further) matches found - virtual int findMacro(const QString &str, int *pos, QString *ret); - //! Provide a replacement string for an expando - //! \param name The name of the expando - //! \param ret Replacement string on output - //! \return True if the expando was found - virtual bool resolveMacro(const QString &name, QString *ret, QSet &seen) = 0; -private: - bool expandNestedMacros(const QString &str, int *pos, QString *ret); -}; - -QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx); -QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx); - QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line); QTCREATOR_UTILS_EXPORT QString appendHelper(const QString &base, int n); diff --git a/tests/auto/utils/expander/tst_expander.cpp b/tests/auto/utils/expander/tst_expander.cpp index a1dac554fb8..33cb2d0bd43 100644 --- a/tests/auto/utils/expander/tst_expander.cpp +++ b/tests/auto/utils/expander/tst_expander.cpp @@ -173,6 +173,7 @@ private slots: { QTest::addColumn("input"); QTest::addColumn("expected"); + QTest::addColumn("os"); QTest::addColumn>("setup"); std::function empty; @@ -184,57 +185,63 @@ private slots: expander.registerVariable("WithSpace", "", [] { return "This has spaces"; }); }; - QTest::newRow("empty") << QString() << QString() << empty; - QTest::newRow("no expansion") << QString("foo") << QString("foo") << empty; + QTest::newRow("empty") << QString() << QString() << Utils::OsTypeLinux << empty; + QTest::newRow("no expansion") + << QString("foo") << QString("foo") << Utils::OsTypeLinux << empty; QTest::newRow("simple-expansion") - << QString("cat %{file}") << QString("cat foo.txt") << file; + << QString("cat %{file}") << QString("cat foo.txt") << Utils::OsTypeLinux << file; QTest::newRow("with-ticks") - << QString("echo -n 'foo %{file}'") << QString("echo -n 'foo foo.txt'") << file; + << QString("echo -n 'foo %{file}'") << QString("echo -n 'foo foo.txt'") + << Utils::OsTypeLinux << file; - QTest::newRow("with-ticks-env") << QString("file=%{file} echo -n 'foo \"$file\"'") - << QString("file=foo.txt echo -n 'foo \"$file\"'") << file; + QTest::newRow("with-ticks-env") + << QString("file=%{file} echo -n 'foo \"$file\"'") + << QString("file=foo.txt echo -n 'foo \"$file\"'") << Utils::OsTypeLinux << file; - if (Utils::HostOsInfo::isWindowsHost()) { - QTest::newRow("with-spaces") - << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") << withspace; + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") + << Utils::OsTypeLinux << withspace; - QTest::newRow("with-spaces-manual") - << QString("echo \"Some: %{WithSpace}\"") - << QString("echo \"Some: This has spaces\"") << withspace; + QTest::newRow("with-spaces-pre-quoted") + << QString("echo 'Some: %{WithSpace}'") << QString("echo 'Some: This has spaces'") + << Utils::OsTypeLinux << withspace; - QTest::newRow("with-spaces-nested") - << QString("cmd /k \"echo %{WithSpace}\"") - << QString("cmd /k \"echo This has spaces\"") << withspace; - } else { - QTest::newRow("with-spaces") - << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") << withspace; + QTest::newRow("with-spaces-nested") + << QString("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") + << Utils::OsTypeLinux << withspace; - QTest::newRow("with-spaces-pre-quoted") - << QString("echo 'Some: %{WithSpace}'") << QString("echo 'Some: This has spaces'") - << withspace; + // Due to security concerns, backslash-escaping an expando is treated as a quoting error + QTest::newRow("backslash-escaping") + << QString("echo \\%{file}") << QString("echo \\%{file}") << Utils::OsTypeLinux << file; - QTest::newRow("with-spaces-nested") - << QString("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") - << withspace; + QTest::newRow("expando-within-shell-substitution") + << QString("${VAR:-%{file}}") << QString("${VAR:-foo.txt}") << Utils::OsTypeLinux + << file; + QTest::newRow("expando-within-shell-substitution-with-space") + << QString("echo \"Some: ${VAR:-%{WithSpace}}\"") + << QString("echo \"Some: ${VAR:-This has spaces}\"") << Utils::OsTypeLinux << withspace; - // Due to security concerns, backslash-escaping an expando is treated as a quoting error - QTest::newRow("backslash-escaping") - << QString("echo \\%{file}") << QString("echo \\%{file}") << file; + // Windows tests + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") + << Utils::OsTypeWindows << withspace; - QTest::newRow("expando-within-shell-substitution") - << QString("${VAR:-%{file}}") << QString("${VAR:-foo.txt}") << file; - QTest::newRow("expando-within-shell-substitution-with-space") - << QString("echo \"Some: ${VAR:-%{WithSpace}}\"") - << QString("echo \"Some: ${VAR:-This has spaces}\"") << withspace; - } + QTest::newRow("with-spaces-manual") + << QString("echo \"Some: %{WithSpace}\"") << QString("echo \"Some: This has spaces\"") + << Utils::OsTypeWindows << withspace; + + QTest::newRow("with-spaces-nested") + << QString("cmd /k \"echo %{WithSpace}\"") << QString("cmd /k \"echo This has spaces\"") + << Utils::OsTypeWindows << withspace; } void expandCommandArgs() { QFETCH(QString, input); QFETCH(QString, expected); + QFETCH(OsType, os); QFETCH(std::function, setup); MacroExpander expander; @@ -242,7 +249,354 @@ private slots: if (setup) setup(expander); - QCOMPARE(expander.expandProcessArgs(input), expected); + QCOMPARE(expander.expandProcessArgs(input, os), expected); + } + + void expandProcessArgs_data() + { + QTest::addColumn("in"); + QTest::addColumn("out"); + QTest::addColumn("os"); + QChar sp(QLatin1Char(' ')); + + struct Val + { + const char *in; + const char *out; + OsType os; + }; + + const std::array vals + = {Val{"plain", 0, OsTypeWindows}, + Val{"%{a}", "hi", OsTypeWindows}, + Val{"%{aa}", "\"hi ho\"", OsTypeWindows}, + Val{"%{b}", "h\\i", OsTypeWindows}, + Val{"%{c}", "\\hi", OsTypeWindows}, + Val{"%{d}", "hi\\", OsTypeWindows}, + Val{"%{ba}", "\"h\\i ho\"", OsTypeWindows}, + Val{"%{ca}", "\"\\hi ho\"", OsTypeWindows}, + Val{"%{da}", "\"hi ho\\\\\"", OsTypeWindows}, // or "\"hi ho\"\\" + Val{"%{e}", "\"h\"\\^\"\"i\"", OsTypeWindows}, + Val{"%{f}", "\"\"\\^\"\"hi\"", OsTypeWindows}, + Val{"%{g}", "\"hi\"\\^\"\"\"", OsTypeWindows}, + Val{"%{h}", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, + Val{"%{i}", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, + Val{"%{j}", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"%{k}", "\"&special;\"", OsTypeWindows}, + Val{"%{x}", "\\", OsTypeWindows}, + Val{"%{y}", "\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{z}", "\"\"", OsTypeWindows}, + Val{"^%{z}%{z}", "^%{z}%{z}", OsTypeWindows}, // stupid user check + + Val{"quoted", 0, OsTypeWindows}, + Val{"\"%{a}\"", "\"hi\"", OsTypeWindows}, + Val{"\"%{aa}\"", "\"hi ho\"", OsTypeWindows}, + Val{"\"%{b}\"", "\"h\\i\"", OsTypeWindows}, + Val{"\"%{c}\"", "\"\\hi\"", OsTypeWindows}, + Val{"\"%{d}\"", "\"hi\\\\\"", OsTypeWindows}, + Val{"\"%{ba}\"", "\"h\\i ho\"", OsTypeWindows}, + Val{"\"%{ca}\"", "\"\\hi ho\"", OsTypeWindows}, + Val{"\"%{da}\"", "\"hi ho\\\\\"", OsTypeWindows}, + Val{"\"%{e}\"", "\"h\"\\^\"\"i\"", OsTypeWindows}, + Val{"\"%{f}\"", "\"\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\"%{g}\"", "\"hi\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{h}\"", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, + Val{"\"%{i}\"", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\"%{j}\"", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{k}\"", "\"&special;\"", OsTypeWindows}, + Val{"\"%{x}\"", "\"\\\\\"", OsTypeWindows}, + Val{"\"%{y}\"", "\"\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{z}\"", "\"\"", OsTypeWindows}, + + Val{"leading bs", 0, OsTypeWindows}, + Val{"\\%{a}", "\\hi", OsTypeWindows}, + Val{"\\%{aa}", "\\\\\"hi ho\"", OsTypeWindows}, + Val{"\\%{b}", "\\h\\i", OsTypeWindows}, + Val{"\\%{c}", "\\\\hi", OsTypeWindows}, + Val{"\\%{d}", "\\hi\\", OsTypeWindows}, + Val{"\\%{ba}", "\\\\\"h\\i ho\"", OsTypeWindows}, + Val{"\\%{ca}", "\\\\\"\\hi ho\"", OsTypeWindows}, + Val{"\\%{da}", "\\\\\"hi ho\\\\\"", OsTypeWindows}, + Val{"\\%{e}", "\\\\\"h\"\\^\"\"i\"", OsTypeWindows}, + Val{"\\%{f}", "\\\\\"\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\\%{g}", "\\\\\"hi\"\\^\"\"\"", OsTypeWindows}, + Val{"\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, + Val{"\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\\%{x}", "\\\\", OsTypeWindows}, + Val{"\\%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"\\%{z}", "\\", OsTypeWindows}, + + Val{"trailing bs", 0, OsTypeWindows}, + Val{"%{a}\\", "hi\\", OsTypeWindows}, + Val{"%{aa}\\", "\"hi ho\"\\", OsTypeWindows}, + Val{"%{b}\\", "h\\i\\", OsTypeWindows}, + Val{"%{c}\\", "\\hi\\", OsTypeWindows}, + Val{"%{d}\\", "hi\\\\", OsTypeWindows}, + Val{"%{ba}\\", "\"h\\i ho\"\\", OsTypeWindows}, + Val{"%{ca}\\", "\"\\hi ho\"\\", OsTypeWindows}, + Val{"%{da}\\", "\"hi ho\\\\\"\\", OsTypeWindows}, + Val{"%{e}\\", "\"h\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"%{f}\\", "\"\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"%{g}\\", "\"hi\"\\^\"\"\"\\", OsTypeWindows}, + Val{"%{h}\\", "\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"%{i}\\", "\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"%{j}\\", "\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, + Val{"%{x}\\", "\\\\", OsTypeWindows}, + Val{"%{y}\\", "\"\"\\^\"\"\"\\", OsTypeWindows}, + Val{"%{z}\\", "\\", OsTypeWindows}, + + Val{"bs-enclosed", 0, OsTypeWindows}, + Val{"\\%{a}\\", "\\hi\\", OsTypeWindows}, + Val{"\\%{aa}\\", "\\\\\"hi ho\"\\", OsTypeWindows}, + Val{"\\%{b}\\", "\\h\\i\\", OsTypeWindows}, + Val{"\\%{c}\\", "\\\\hi\\", OsTypeWindows}, + Val{"\\%{d}\\", "\\hi\\\\", OsTypeWindows}, + Val{"\\%{ba}\\", "\\\\\"h\\i ho\"\\", OsTypeWindows}, + Val{"\\%{ca}\\", "\\\\\"\\hi ho\"\\", OsTypeWindows}, + Val{"\\%{da}\\", "\\\\\"hi ho\\\\\"\\", OsTypeWindows}, + Val{"\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\", OsTypeWindows}, + Val{"\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, + Val{"\\%{x}\\", "\\\\\\", OsTypeWindows}, + Val{"\\%{y}\\", "\\\\\"\"\\^\"\"\"\\", OsTypeWindows}, + Val{"\\%{z}\\", "\\\\", OsTypeWindows}, + + Val{"bs-enclosed and trailing literal quote", 0, OsTypeWindows}, + Val{"\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"", OsTypeWindows}, + Val{"\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"", OsTypeWindows}, + Val{"\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"", OsTypeWindows}, + Val{"\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"", OsTypeWindows}, + Val{"\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"", OsTypeWindows}, + Val{"\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"", OsTypeWindows}, + + Val{"bs-enclosed and trailing unclosed quote", 0, OsTypeWindows}, + Val{"\\%{a}\\\\\"", "\\hi\\\\\"", OsTypeWindows}, + Val{"\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"", OsTypeWindows}, + Val{"\\%{b}\\\\\"", "\\h\\i\\\\\"", OsTypeWindows}, + Val{"\\%{c}\\\\\"", "\\\\hi\\\\\"", OsTypeWindows}, + Val{"\\%{d}\\\\\"", "\\hi\\\\\\\\\"", OsTypeWindows}, + Val{"\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"", OsTypeWindows}, + Val{"\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"", OsTypeWindows}, + Val{"\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"", OsTypeWindows}, + Val{"\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"", OsTypeWindows}, + Val{"\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, + Val{"\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"", OsTypeWindows}, + Val{"\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"", OsTypeWindows}, + Val{"\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, + Val{"\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"", OsTypeWindows}, + Val{"\\%{x}\\\\\"", "\\\\\\\\\\\\\"", OsTypeWindows}, + Val{"\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"", OsTypeWindows}, + Val{"\\%{z}\\\\\"", "\\\\\\\\\"", OsTypeWindows}, + + Val{"multi-var", 0, OsTypeWindows}, + Val{"%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{x}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, + Val{"%{x}%{z}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, + Val{"%{x}%{z}\\^\"", "\\\\\\^\"", OsTypeWindows}, + Val{"%{x}\\%{z}", "\\\\", OsTypeWindows}, + Val{"%{x}%{z}\\%{z}", "\\\\", OsTypeWindows}, + Val{"%{x}%{z}\\", "\\\\", OsTypeWindows}, + Val{"%{aa}%{a}", "\"hi hohi\"", OsTypeWindows}, + Val{"%{aa}%{aa}", "\"hi hohi ho\"", OsTypeWindows}, + Val{"%{aa}:%{aa}", "\"hi ho\":\"hi ho\"", OsTypeWindows}, + Val{"hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|", OsTypeWindows}, + + Val{"quoted multi-var", 0, OsTypeWindows}, + Val{"\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, + Val{"\"%{x}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, + Val{"\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, + Val{"\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"", OsTypeWindows}, + Val{"\"%{aa}%{a}\"", "\"hi hohi\"", OsTypeWindows}, + Val{"\"%{aa}%{aa}\"", "\"hi hohi ho\"", OsTypeWindows}, + Val{"\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"", OsTypeWindows}, + + Val{"plain", 0, OsTypeLinux}, + Val{"%{a}", "hi", OsTypeLinux}, + Val{"%{b}", "'hi ho'", OsTypeLinux}, + Val{"%{c}", "'&special;'", OsTypeLinux}, + Val{"%{d}", "'h\\i'", OsTypeLinux}, + Val{"%{e}", "'h\"i'", OsTypeLinux}, + Val{"%{f}", "'h'\\''i'", OsTypeLinux}, + Val{"%{z}", "''", OsTypeLinux}, + Val{"\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux}, // stupid user check + + Val{"single-quoted", 0, OsTypeLinux}, + Val{"'%{a}'", "'hi'", OsTypeLinux}, + Val{"'%{b}'", "'hi ho'", OsTypeLinux}, + Val{"'%{c}'", "'&special;'", OsTypeLinux}, + Val{"'%{d}'", "'h\\i'", OsTypeLinux}, + Val{"'%{e}'", "'h\"i'", OsTypeLinux}, + Val{"'%{f}'", "'h'\\''i'", OsTypeLinux}, + Val{"'%{z}'", "''", OsTypeLinux}, + + Val{"double-quoted", 0, OsTypeLinux}, + Val{"\"%{a}\"", "\"hi\"", OsTypeLinux}, + Val{"\"%{b}\"", "\"hi ho\"", OsTypeLinux}, + Val{"\"%{c}\"", "\"&special;\"", OsTypeLinux}, + Val{"\"%{d}\"", "\"h\\\\i\"", OsTypeLinux}, + Val{"\"%{e}\"", "\"h\\\"i\"", OsTypeLinux}, + Val{"\"%{f}\"", "\"h'i\"", OsTypeLinux}, + Val{"\"%{z}\"", "\"\"", OsTypeLinux}, + + Val{"complex", 0, OsTypeLinux}, + Val{"echo \"$(echo %{a})\"", "echo \"$(echo hi)\"", OsTypeLinux}, + Val{"echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"", OsTypeLinux}, + Val{"echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"", OsTypeLinux}, + // These make no sense shell-wise, but they test expando nesting + Val{"echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"", OsTypeLinux}, + Val{"echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"", OsTypeLinux}, + Val{"echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"", OsTypeLinux}}; + + const char *title = 0; + for (const auto &val : vals) { + if (!val.out) { + title = val.in; + } else { + QString name + = QString("%1: %2 (%3)") + .arg(title, val.in, val.os == OsTypeWindows ? "windows" : "linux"); + QTest::newRow(name.toLatin1()) + << QString::fromLatin1(val.in) << QString::fromLatin1(val.out) << val.os; + QTest::newRow(("padded " + name).toLatin1()) + << QString(sp + QString::fromLatin1(val.in) + sp) + << QString(sp + QString::fromLatin1(val.out) + sp) << val.os; + } + } + } + + void expandProcessArgs() + { + QFETCH(QString, in); + QFETCH(QString, out); + QFETCH(OsType, os); + + MacroExpander expander; + + if (os == Utils::OsTypeWindows) { + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("aa", "", [] { return "hi ho"; }); + expander.registerVariable("b", "", [] { return "h\\i"; }); + expander.registerVariable("c", "", [] { return "\\hi"; }); + expander.registerVariable("d", "", [] { return "hi\\"; }); + expander.registerVariable("ba", "", [] { return "h\\i ho"; }); + expander.registerVariable("ca", "", [] { return "\\hi ho"; }); + expander.registerVariable("da", "", [] { return "hi ho\\"; }); + expander.registerVariable("e", "", [] { return "h\"i"; }); + expander.registerVariable("f", "", [] { return "\"hi"; }); + expander.registerVariable("g", "", [] { return "hi\""; }); + expander.registerVariable("h", "", [] { return "h\\\"i"; }); + expander.registerVariable("i", "", [] { return "\\\"hi"; }); + expander.registerVariable("j", "", [] { return "hi\\\""; }); + expander.registerVariable("k", "", [] { return "&special;"; }); + expander.registerVariable("x", "", [] { return "\\"; }); + expander.registerVariable("y", "", [] { return "\""; }); + expander.registerVariable("z", "", [] { return ""; }); + } else { + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("b", "", [] { return "hi ho"; }); + expander.registerVariable("c", "", [] { return "&special;"; }); + expander.registerVariable("d", "", [] { return "h\\i"; }); + expander.registerVariable("e", "", [] { return "h\"i"; }); + expander.registerVariable("f", "", [] { return "h'i"; }); + expander.registerVariable("z", "", [] { return ""; }); + } + + QCOMPARE(expander.expandProcessArgs(in, os), out); + } + + void testMacroExpander_data() + { + QTest::addColumn("in"); + QTest::addColumn("out"); + + struct Val + { + const char *in; + const char *out; + }; + + const std::array vals = { + Val{"text", "text"}, + Val{"%{a}", "hi"}, + Val{"%%{a}", "%hi"}, + Val{"%%%{a}", "%%hi"}, + Val{"%{b}", "%{b}"}, + Val{"pre%{a}", "prehi"}, + Val{"%{a}post", "hipost"}, + Val{"pre%{a}post", "prehipost"}, + Val{"%{a}%{a}", "hihi"}, + Val{"%{a}text%{a}", "hitexthi"}, + Val{"%{foo}%{a}text%{a}", "ahitexthi"}, + Val{"%{}{a}", "%{a}"}, + Val{"%{}", "%"}, + Val{"test%{}", "test%"}, + Val{"%{}test", "%test"}, + Val{"%{abc", "%{abc"}, + Val{"%{%{a}", "%{hi"}, + Val{"%{%{a}}", "ho"}, + Val{"%{%{a}}}post", "ho}post"}, + Val{"%{hi%{a}}", "bar"}, + Val{"%{hi%{%{foo}}}", "bar"}, + Val{"%{hihi/b/c}", "car"}, + Val{"%{hihi/a/}", "br"}, // empty replacement + Val{"%{hihi/b}", "bar"}, // incomplete substitution + Val{"%{hihi/./c}", "car"}, + Val{"%{hihi//./c}", "ccc"}, + Val{"%{hihi/(.)(.)r/\\2\\1c}", "abc"}, // no escape for capture groups + Val{"%{hihi/b/c/d}", "c/dar"}, + Val{"%{hihi/a/e{\\}e}", "be{}er"}, // escape closing brace + Val{"%{JS:with \\} inside}", "yay"}, // escape closing brace also in JS: + Val{"%{JS:literal%\\{}", "hurray"}, + Val{"%{slash/o\\/b/ol's c}", "fool's car"}, + Val{"%{sl\\/sh/(.)(a)(.)/\\2\\1\\3as}", "salsash"}, // escape in variable name + Val{"%{JS:foo/b/c}", "%{JS:foo/b/c}"}, // No replacement for JS (all considered varName) + Val{"%{%{a}%{a}/b/c}", "car"}, + Val{"%{nonsense:-sense}", "sense"}, + }; + + for (const auto &val : vals) + QTest::newRow(val.in) << QString::fromLatin1(val.in) << QString::fromLatin1(val.out); + } + + void testMacroExpander() + { + QFETCH(QString, in); + QFETCH(QString, out); + + MacroExpander expander; + expander.registerVariable("foo", "", [] { return "a"; }); + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("hi", "", [] { return "ho"; }); + expander.registerVariable("hihi", "", [] { return "bar"; }); + expander.registerVariable("slash", "", [] { return "foo/bar"; }); + expander.registerVariable("sl/sh", "", [] { return "slash"; }); + expander.registerVariable("JS:foor", "", [] { return "bar"; }); + expander.registerVariable("JS:with } inside", "", [] { return "yay"; }); + expander.registerVariable("JS:literal%{", "", [] { return "hurray"; }); + + QCOMPARE(expander.expand(in), out); } }; diff --git a/tests/auto/utils/process/tst_process.cpp b/tests/auto/utils/process/tst_process.cpp index d6e283e4f9a..01704d3d2ac 100644 --- a/tests/auto/utils/process/tst_process.cpp +++ b/tests/auto/utils/process/tst_process.cpp @@ -66,29 +66,6 @@ protected: int MessageHandler::s_destroyCount = 0; QtMessageHandler MessageHandler::s_oldMessageHandler = 0; -class MacroMapExpander : public AbstractMacroExpander { -public: - bool resolveMacro(const QString &name, QString *ret, QSet &seen) - override - { - // loop prevention - const int count = seen.count(); - seen.insert(this); - if (seen.count() == count) - return false; - - QHash::const_iterator it = m_map.constFind(name); - if (it != m_map.constEnd()) { - *ret = it.value(); - return true; - } - return false; - } - void insert(const QString &key, const QString &value) { m_map.insert(key, value); } -private: - QHash m_map; -}; - static constexpr char s_skipTerminateOnWindows[] = "Windows implementation of this test is lacking handling of WM_CLOSE message."; @@ -135,8 +112,6 @@ private slots: void prepareArgs(); void prepareArgsEnv_data(); void prepareArgsEnv(); - void expandMacros_data(); - void expandMacros(); void iterations_data(); void iterations(); void iteratorEditsWindows(); @@ -181,8 +156,6 @@ private: Environment envWindows; Environment envLinux; - MacroMapExpander mxWin; - MacroMapExpander mxUnix; QString homeStr; QString home; @@ -206,38 +179,6 @@ void tst_Process::initTestCase() env << "empty=" << "word=hi" << "words=hi ho" << "spacedwords= hi ho sucker "; envWindows = Environment(env, OsTypeWindows); envLinux = Environment(env, OsTypeLinux); - - mxWin.insert("a", "hi"); - mxWin.insert("aa", "hi ho"); - - mxWin.insert("b", "h\\i"); - mxWin.insert("c", "\\hi"); - mxWin.insert("d", "hi\\"); - mxWin.insert("ba", "h\\i ho"); - mxWin.insert("ca", "\\hi ho"); - mxWin.insert("da", "hi ho\\"); - - mxWin.insert("e", "h\"i"); - mxWin.insert("f", "\"hi"); - mxWin.insert("g", "hi\""); - - mxWin.insert("h", "h\\\"i"); - mxWin.insert("i", "\\\"hi"); - mxWin.insert("j", "hi\\\""); - - mxWin.insert("k", "&special;"); - - mxWin.insert("x", "\\"); - mxWin.insert("y", "\""); - mxWin.insert("z", ""); - - mxUnix.insert("a", "hi"); - mxUnix.insert("b", "hi ho"); - mxUnix.insert("c", "&special;"); - mxUnix.insert("d", "h\\i"); - mxUnix.insert("e", "h\"i"); - mxUnix.insert("f", "h'i"); - mxUnix.insert("z", ""); } void tst_Process::cleanupTestCase() @@ -550,252 +491,7 @@ void tst_Process::prepareArgsEnv() QCOMPARE(outstr, out); } -void tst_Process::expandMacros_data() -{ - QTest::addColumn("in"); - QTest::addColumn("out"); - QTest::addColumn("os"); - QChar sp(QLatin1Char(' ')); - - static const struct { - const char * const in; - const char * const out; - OsType os; - } vals[] = { - {"plain", 0, OsTypeWindows}, - {"%{a}", "hi", OsTypeWindows}, - {"%{aa}", "\"hi ho\"", OsTypeWindows}, - {"%{b}", "h\\i", OsTypeWindows}, - {"%{c}", "\\hi", OsTypeWindows}, - {"%{d}", "hi\\", OsTypeWindows}, - {"%{ba}", "\"h\\i ho\"", OsTypeWindows}, - {"%{ca}", "\"\\hi ho\"", OsTypeWindows}, - {"%{da}", "\"hi ho\\\\\"", OsTypeWindows}, // or "\"hi ho\"\\" - {"%{e}", "\"h\"\\^\"\"i\"", OsTypeWindows}, - {"%{f}", "\"\"\\^\"\"hi\"", OsTypeWindows}, - {"%{g}", "\"hi\"\\^\"\"\"", OsTypeWindows}, - {"%{h}", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, - {"%{i}", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, - {"%{j}", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, - {"%{k}", "\"&special;\"", OsTypeWindows}, - {"%{x}", "\\", OsTypeWindows}, - {"%{y}", "\"\"\\^\"\"\"", OsTypeWindows}, - {"%{z}", "\"\"", OsTypeWindows}, - {"^%{z}%{z}", "^%{z}%{z}", OsTypeWindows}, // stupid user check - - {"quoted", 0, OsTypeWindows}, - {"\"%{a}\"", "\"hi\"", OsTypeWindows}, - {"\"%{aa}\"", "\"hi ho\"", OsTypeWindows}, - {"\"%{b}\"", "\"h\\i\"", OsTypeWindows}, - {"\"%{c}\"", "\"\\hi\"", OsTypeWindows}, - {"\"%{d}\"", "\"hi\\\\\"", OsTypeWindows}, - {"\"%{ba}\"", "\"h\\i ho\"", OsTypeWindows}, - {"\"%{ca}\"", "\"\\hi ho\"", OsTypeWindows}, - {"\"%{da}\"", "\"hi ho\\\\\"", OsTypeWindows}, - {"\"%{e}\"", "\"h\"\\^\"\"i\"", OsTypeWindows}, - {"\"%{f}\"", "\"\"\\^\"\"hi\"", OsTypeWindows}, - {"\"%{g}\"", "\"hi\"\\^\"\"\"", OsTypeWindows}, - {"\"%{h}\"", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, - {"\"%{i}\"", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, - {"\"%{j}\"", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{k}\"", "\"&special;\"", OsTypeWindows}, - {"\"%{x}\"", "\"\\\\\"", OsTypeWindows}, - {"\"%{y}\"", "\"\"\\^\"\"\"", OsTypeWindows}, - {"\"%{z}\"", "\"\"", OsTypeWindows}, - - {"leading bs", 0, OsTypeWindows}, - {"\\%{a}", "\\hi", OsTypeWindows}, - {"\\%{aa}", "\\\\\"hi ho\"", OsTypeWindows}, - {"\\%{b}", "\\h\\i", OsTypeWindows}, - {"\\%{c}", "\\\\hi", OsTypeWindows}, - {"\\%{d}", "\\hi\\", OsTypeWindows}, - {"\\%{ba}", "\\\\\"h\\i ho\"", OsTypeWindows}, - {"\\%{ca}", "\\\\\"\\hi ho\"", OsTypeWindows}, - {"\\%{da}", "\\\\\"hi ho\\\\\"", OsTypeWindows}, - {"\\%{e}", "\\\\\"h\"\\^\"\"i\"", OsTypeWindows}, - {"\\%{f}", "\\\\\"\"\\^\"\"hi\"", OsTypeWindows}, - {"\\%{g}", "\\\\\"hi\"\\^\"\"\"", OsTypeWindows}, - {"\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, - {"\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, - {"\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\\%{x}", "\\\\", OsTypeWindows}, - {"\\%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"\\%{z}", "\\", OsTypeWindows}, - - {"trailing bs", 0, OsTypeWindows}, - {"%{a}\\", "hi\\", OsTypeWindows}, - {"%{aa}\\", "\"hi ho\"\\", OsTypeWindows}, - {"%{b}\\", "h\\i\\", OsTypeWindows}, - {"%{c}\\", "\\hi\\", OsTypeWindows}, - {"%{d}\\", "hi\\\\", OsTypeWindows}, - {"%{ba}\\", "\"h\\i ho\"\\", OsTypeWindows}, - {"%{ca}\\", "\"\\hi ho\"\\", OsTypeWindows}, - {"%{da}\\", "\"hi ho\\\\\"\\", OsTypeWindows}, - {"%{e}\\", "\"h\"\\^\"\"i\"\\", OsTypeWindows}, - {"%{f}\\", "\"\"\\^\"\"hi\"\\", OsTypeWindows}, - {"%{g}\\", "\"hi\"\\^\"\"\"\\", OsTypeWindows}, - {"%{h}\\", "\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, - {"%{i}\\", "\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, - {"%{j}\\", "\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, - {"%{x}\\", "\\\\", OsTypeWindows}, - {"%{y}\\", "\"\"\\^\"\"\"\\", OsTypeWindows}, - {"%{z}\\", "\\", OsTypeWindows}, - - {"bs-enclosed", 0, OsTypeWindows}, - {"\\%{a}\\", "\\hi\\", OsTypeWindows}, - {"\\%{aa}\\", "\\\\\"hi ho\"\\", OsTypeWindows}, - {"\\%{b}\\", "\\h\\i\\", OsTypeWindows}, - {"\\%{c}\\", "\\\\hi\\", OsTypeWindows}, - {"\\%{d}\\", "\\hi\\\\", OsTypeWindows}, - {"\\%{ba}\\", "\\\\\"h\\i ho\"\\", OsTypeWindows}, - {"\\%{ca}\\", "\\\\\"\\hi ho\"\\", OsTypeWindows}, - {"\\%{da}\\", "\\\\\"hi ho\\\\\"\\", OsTypeWindows}, - {"\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\", OsTypeWindows}, - {"\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\", OsTypeWindows}, - {"\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\", OsTypeWindows}, - {"\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, - {"\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, - {"\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, - {"\\%{x}\\", "\\\\\\", OsTypeWindows}, - {"\\%{y}\\", "\\\\\"\"\\^\"\"\"\\", OsTypeWindows}, - {"\\%{z}\\", "\\\\", OsTypeWindows}, - - {"bs-enclosed and trailing literal quote", 0, OsTypeWindows}, - {"\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"", OsTypeWindows}, - {"\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"", OsTypeWindows}, - {"\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"", OsTypeWindows}, - {"\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"", OsTypeWindows}, - {"\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"", OsTypeWindows}, - {"\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"", OsTypeWindows}, - {"\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"", OsTypeWindows}, - {"\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"", OsTypeWindows}, - {"\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, - {"\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, - {"\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, - {"\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, - {"\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, - {"\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, - {"\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"", OsTypeWindows}, - {"\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, - {"\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"", OsTypeWindows}, - - {"bs-enclosed and trailing unclosed quote", 0, OsTypeWindows}, - {"\\%{a}\\\\\"", "\\hi\\\\\"", OsTypeWindows}, - {"\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"", OsTypeWindows}, - {"\\%{b}\\\\\"", "\\h\\i\\\\\"", OsTypeWindows}, - {"\\%{c}\\\\\"", "\\\\hi\\\\\"", OsTypeWindows}, - {"\\%{d}\\\\\"", "\\hi\\\\\\\\\"", OsTypeWindows}, - {"\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"", OsTypeWindows}, - {"\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"", OsTypeWindows}, - {"\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"", OsTypeWindows}, - {"\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"", OsTypeWindows}, - {"\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, - {"\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"", OsTypeWindows}, - {"\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"", OsTypeWindows}, - {"\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, - {"\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"", OsTypeWindows}, - {"\\%{x}\\\\\"", "\\\\\\\\\\\\\"", OsTypeWindows}, - {"\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"", OsTypeWindows}, - {"\\%{z}\\\\\"", "\\\\\\\\\"", OsTypeWindows}, - - {"multi-var", 0, OsTypeWindows}, - {"%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"%{x}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, - {"%{x}%{z}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, - {"%{x}%{z}\\^\"", "\\\\\\^\"", OsTypeWindows}, - {"%{x}\\%{z}", "\\\\", OsTypeWindows}, - {"%{x}%{z}\\%{z}", "\\\\", OsTypeWindows}, - {"%{x}%{z}\\", "\\\\", OsTypeWindows}, - {"%{aa}%{a}", "\"hi hohi\"", OsTypeWindows}, - {"%{aa}%{aa}", "\"hi hohi ho\"", OsTypeWindows}, - {"%{aa}:%{aa}", "\"hi ho\":\"hi ho\"", OsTypeWindows}, - {"hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|", OsTypeWindows}, - - {"quoted multi-var", 0, OsTypeWindows}, - {"\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, - {"\"%{x}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, - {"\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, - {"\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"", OsTypeWindows}, - {"\"%{aa}%{a}\"", "\"hi hohi\"", OsTypeWindows}, - {"\"%{aa}%{aa}\"", "\"hi hohi ho\"", OsTypeWindows}, - {"\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"", OsTypeWindows}, - - {"plain", 0, OsTypeLinux}, - {"%{a}", "hi", OsTypeLinux}, - {"%{b}", "'hi ho'", OsTypeLinux}, - {"%{c}", "'&special;'", OsTypeLinux}, - {"%{d}", "'h\\i'", OsTypeLinux}, - {"%{e}", "'h\"i'", OsTypeLinux}, - {"%{f}", "'h'\\''i'", OsTypeLinux}, - {"%{z}", "''", OsTypeLinux}, - {"\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux}, // stupid user check - - {"single-quoted", 0, OsTypeLinux}, - {"'%{a}'", "'hi'", OsTypeLinux}, - {"'%{b}'", "'hi ho'", OsTypeLinux}, - {"'%{c}'", "'&special;'", OsTypeLinux}, - {"'%{d}'", "'h\\i'", OsTypeLinux}, - {"'%{e}'", "'h\"i'", OsTypeLinux}, - {"'%{f}'", "'h'\\''i'", OsTypeLinux}, - {"'%{z}'", "''", OsTypeLinux}, - - {"double-quoted", 0, OsTypeLinux}, - {"\"%{a}\"", "\"hi\"", OsTypeLinux}, - {"\"%{b}\"", "\"hi ho\"", OsTypeLinux}, - {"\"%{c}\"", "\"&special;\"", OsTypeLinux}, - {"\"%{d}\"", "\"h\\\\i\"", OsTypeLinux}, - {"\"%{e}\"", "\"h\\\"i\"", OsTypeLinux}, - {"\"%{f}\"", "\"h'i\"", OsTypeLinux}, - {"\"%{z}\"", "\"\"", OsTypeLinux}, - - {"complex", 0, OsTypeLinux}, - {"echo \"$(echo %{a})\"", "echo \"$(echo hi)\"", OsTypeLinux}, - {"echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"", OsTypeLinux}, - {"echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"", OsTypeLinux}, - // These make no sense shell-wise, but they test expando nesting - {"echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"", OsTypeLinux}, - {"echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"", OsTypeLinux}, - {"echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"", OsTypeLinux }, - }; - - const char *title = 0; - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { - if (!vals[i].out) { - title = vals[i].in; - } else { - char buf[80]; - snprintf(buf, 80, "%s: %s", title, vals[i].in); - QTest::newRow(buf) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out) - << vals[i].os; - snprintf(buf, 80, "padded %s: %s", title, vals[i].in); - QTest::newRow(buf) << QString(sp + QString::fromLatin1(vals[i].in) + sp) - << QString(sp + QString::fromLatin1(vals[i].out) + sp) - << vals[i].os; - } - } -} - -void tst_Process::expandMacros() -{ - QFETCH(QString, in); - QFETCH(QString, out); - QFETCH(OsType, os); - - if (os == OsTypeWindows) - ProcessArgs::expandMacros(&in, &mxWin, os); - else - ProcessArgs::expandMacros(&in, &mxUnix, os); - QCOMPARE(in, out); -} void tst_Process::iterations_data() { diff --git a/tests/auto/utils/stringutils/tst_stringutils.cpp b/tests/auto/utils/stringutils/tst_stringutils.cpp index 0b5c5cd1527..4cfa1c7f35a 100644 --- a/tests/auto/utils/stringutils/tst_stringutils.cpp +++ b/tests/auto/utils/stringutils/tst_stringutils.cpp @@ -10,66 +10,12 @@ using namespace Utils; -class TestMacroExpander : public Utils::AbstractMacroExpander -{ -public: - bool resolveMacro(const QString &name, QString *ret, QSet &seen) - override - { - // loop prevention - const int count = seen.count(); - seen.insert(this); - if (seen.count() == count) - return false; - - if (name == QLatin1String("foo")) { - *ret = QLatin1String("a"); - return true; - } - if (name == QLatin1String("a")) { - *ret = QLatin1String("hi"); - return true; - } - if (name == QLatin1String("hi")) { - *ret = QLatin1String("ho"); - return true; - } - if (name == QLatin1String("hihi")) { - *ret = QLatin1String("bar"); - return true; - } - if (name == "slash") { - *ret = "foo/bar"; - return true; - } - if (name == "sl/sh") { - *ret = "slash"; - return true; - } - if (name == "JS:foo") { - *ret = "bar"; - return true; - } - if (name == "JS:with } inside") { - *ret = "yay"; - return true; - } - if (name == "JS:literal%{") { - *ret = "hurray"; - return true; - } - return false; - } -}; - class tst_StringUtils : public QObject { Q_OBJECT private slots: void testWithTildeHomePath(); - void testMacroExpander_data(); - void testMacroExpander(); void testStripAccelerator_data(); void testStripAccelerator(); void testParseUsedPortFromNetstatOutput_data(); @@ -84,9 +30,6 @@ private slots: void testSplitAtFirst(); void testAsciify_data(); void testAsciify(); - -private: - TestMacroExpander mx; }; void tst_StringUtils::testWithTildeHomePath() @@ -118,68 +61,6 @@ void tst_StringUtils::testWithTildeHomePath() #endif } -void tst_StringUtils::testMacroExpander_data() - -{ - QTest::addColumn("in"); - QTest::addColumn("out"); - - static const struct { - const char * const in; - const char * const out; - } vals[] = { - {"text", "text"}, - {"%{a}", "hi"}, - {"%%{a}", "%hi"}, - {"%%%{a}", "%%hi"}, - {"%{b}", "%{b}"}, - {"pre%{a}", "prehi"}, - {"%{a}post", "hipost"}, - {"pre%{a}post", "prehipost"}, - {"%{a}%{a}", "hihi"}, - {"%{a}text%{a}", "hitexthi"}, - {"%{foo}%{a}text%{a}", "ahitexthi"}, - {"%{}{a}", "%{a}"}, - {"%{}", "%"}, - {"test%{}", "test%"}, - {"%{}test", "%test"}, - {"%{abc", "%{abc"}, - {"%{%{a}", "%{hi"}, - {"%{%{a}}", "ho"}, - {"%{%{a}}}post", "ho}post"}, - {"%{hi%{a}}", "bar"}, - {"%{hi%{%{foo}}}", "bar"}, - {"%{hihi/b/c}", "car"}, - {"%{hihi/a/}", "br"}, // empty replacement - {"%{hihi/b}", "bar"}, // incomplete substitution - {"%{hihi/./c}", "car"}, - {"%{hihi//./c}", "ccc"}, - {"%{hihi/(.)(.)r/\\2\\1c}", "abc"}, // no escape for capture groups - {"%{hihi/b/c/d}", "c/dar"}, - {"%{hihi/a/e{\\}e}", "be{}er"}, // escape closing brace - {"%{JS:with \\} inside}", "yay"}, // escape closing brace also in JS: - {"%{JS:literal%\\{}", "hurray"}, - {"%{slash/o\\/b/ol's c}", "fool's car"}, - {"%{sl\\/sh/(.)(a)(.)/\\2\\1\\3as}", "salsash"}, // escape in variable name - {"%{JS:foo/b/c}", "%{JS:foo/b/c}"}, // No replacement for JS (all considered varName) - {"%{%{a}%{a}/b/c}", "car"}, - {"%{nonsense:-sense}", "sense"}, - }; - - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) - QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out); -} - -void tst_StringUtils::testMacroExpander() -{ - QFETCH(QString, in); - QFETCH(QString, out); - - Utils::expandMacros(&in, &mx); - QCOMPARE(in, out); -} - void tst_StringUtils::testStripAccelerator_data() { QTest::addColumn("expected"); From cd35434c651b16a52a6bc44255adf8edb562ad07 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 21 Oct 2024 14:07:56 +0200 Subject: [PATCH 12/47] Utils: Remove default argument for ProcessArgs::expandMacros() We know the right OS when calling this. Change-Id: I3d1e961107f14c8217052babdd6c88cec12568e0 Reviewed-by: Marcus Tillmanns --- src/libs/utils/commandline.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index 84a32384efb..78f756a9f61 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -63,8 +63,7 @@ public: using FindMacro = std::function; //! Safely replace the expandos in a shell command - static bool expandMacros( - QString *cmd, const FindMacro &findMacro, OsType osType = HostOsInfo::hostOs()); + static bool expandMacros(QString *cmd, const FindMacro &findMacro, OsType osType); /*! Iterate over arguments from a command line. * Assumes that the name of the actual command is *not* part of the line. From 95b88f786c190225b411209dc0b773a382abf00e Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 17 Oct 2024 08:42:46 +0200 Subject: [PATCH 13/47] Utils: Fix documentation of ProcessArgs Change-Id: I7e64ec61f5e350b0182dfe1d13a2e2a121dd59f8 Reviewed-by: hjk Reviewed-by: Oswald Buddenhagen --- src/libs/utils/commandline.cpp | 86 ++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index d5ad99a4178..1da60c3565c 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -43,8 +43,7 @@ namespace Utils { \class Utils::ProcessArgs \inmodule QtCreator - \brief The ProcessArgs class provides functionality for dealing with - shell-quoted process arguments. + \brief Handles shell-quoted process arguments. */ inline static bool isMetaCharWin(ushort c) @@ -695,11 +694,27 @@ static int quoteArgInternalWin(QString &ret, int bslashes) // TODO: This documentation is relevant for end-users. Where to put it? -/** - * Perform safe macro expansion (substitution) on a string for use - * in shell commands. +/*! + * Uses the macro expander \a mx to perform in-place macro expansion + * (substitution) on the string \a cmd, which is expected to contain a shell + * command. \a osType specifies the syntax, which is Bourne Shell compatible + * for Unix and \c cmd compatible for Windows. * - * \section Unix notes + * Returns \c false if substitution cannot be performed safely, because the + * command cannot be parsed -- for example due to quoting errors. + * + * \note This function is designed to be safe to use with expando objects + * that contain shell meta-characters. However, placing expandos in the wrong + * place of the command may defeat the expander's efforts to quote their + * contents, which will likely result in incorrect command execution. + * In particular, expandos that contain untrusted data might expose the + * end-user of the application to critical shell code injection + * vulnerabilities. To avoid these issues, follow the guidelines in + * \l {Unix security considerations} and \l {Windows security considerations}. + * Generally, it is a better idea to invoke shell scripts rather than to + * assemble complex one-line commands. + * + * \section1 Unix notes * * Explicitly supported shell constructs: * \\ '' "" {} () $(()) ${} $() `` @@ -713,41 +728,45 @@ static int quoteArgInternalWin(QString &ret, int bslashes) * \li Bash-style \c{$""} and \c{$''} string quoting syntax. * \endlist * - * The rest of the shell (incl. bash) syntax is simply ignored, - * as it is not expected to cause problems. + * The rest of the shell syntax (including bash syntax) should not cause + * problems and is ignored. * - * Security considerations: + * \section2 Unix security considerations * \list - * \li Backslash-escaping an expando is treated as a quoting error - * \li Do not put expandos into double quoted substitutions: + * \li Backslash-escaping an expando is treated as a quoting error. + * \li Do not put expandos into double quoted substitutions as this may + * trigger parser bugs in some shells: * \badcode * "${VAR:-%{macro}}" * \endcode - * \li Do not put expandos into command line arguments which are nested - * shell commands: + * \li Do not put expandos into command line arguments that are nested shell + * commands. For example, the following is unsafe: * \badcode - * sh -c 'foo \%{file}' + * su %{user} -c 'foo %{file}' * \endcode - * \goodcode - * file=\%{file} sh -c 'foo "$file"' + * Instead you can assign the macro to an environment variable and pass + * that into the call: + * \badcode + * file=%{file} su %{user} -c 'foo "$file"' * \endcode * \endlist * - * \section Windows notes + * \section1 Windows notes * - * All quoting syntax supported by splitArgs() is supported here as well. - * Additionally, command grouping via parentheses is recognized - note - * however, that the parser is much stricter about unquoted parentheses - * than cmd itself. - * The rest of the cmd syntax is simply ignored, as it is not expected - * to cause problems. + * All quoting syntax supported by \c splitArgs() is supported here as well. + * Additionally, command grouping via parentheses is recognized -- but + * note that the parser is much stricter about unquoted parentheses + * than \c cmd itself. + * The rest of the \c cmd syntax should not cause problems and is ignored. * - * Security considerations: + * + * \section2 Windows security considerations * \list - * \li Circumflex-escaping an expando is treated as a quoting error + * \li Circumflex-escaping an expando is treated as a quoting error. * \li Closing double quotes right before expandos and opening double quotes - * right after expandos are treated as quoting errors - * \li Do not put expandos into nested commands: + * right after expandos are treated as quoting errors. + * \li Do not put expandos into nested commands. + * For example, the following is unsafe: * \badcode * for /f "usebackq" \%v in (`foo \%{file}`) do \@echo \%v * \endcode @@ -755,16 +774,13 @@ static int quoteArgInternalWin(QString &ret, int bslashes) * as an environment variable expansion. A solution is replacing any * percent signs with a fixed string like \c{\%PERCENT_SIGN\%} and * injecting \c{PERCENT_SIGN=\%} into the shell's environment. - * \li Enabling delayed environment variable expansion (cmd /v:on) should have - * no security implications, but may still wreak havoc due to the - * need for doubling circumflexes if any exclamation marks are present, - * and the need to circumflex-escape the exclamation marks themselves. + * \li Enabling delayed environment variable expansion (\c{cmd /v:on}) should + * have no security implications. But it might still cause issues because + * the parser is not prepared for the fact that literal exclamation marks + * in the command need to be \e circumflex-escaped, and pre-existing + * circumflexes need to be doubled. * \endlist * - * \param cmd pointer to the string in which macros are expanded in-place - * \param mx pointer to a macro expander instance - * \return false if the string could not be parsed and therefore no safe - * substitution was possible */ bool ProcessArgs::expandMacros(QString *cmd, const FindMacro &findMacro, OsType osType) { From c00ce6341bfe9d59535f7f0aeea0fa01623c3d9e Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 21 Oct 2024 15:38:31 +0200 Subject: [PATCH 14/47] QmlJSEditor: Fix running QML checks for CMake Amends 99a4184761449054020da661b84f78b8ebf3a757. Fixes: QTCREATORBUG-31867 Change-Id: I6fa5f4982cfd48ba542a7acfffbba2e874802e52 Reviewed-by: hjk --- src/plugins/qmljseditor/qmltaskmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp index 9260e0069ee..43c908a6445 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.cpp +++ b/src/plugins/qmljseditor/qmltaskmanager.cpp @@ -126,7 +126,7 @@ void QmlTaskManager::updateSemanticMessagesNow() // abort any update that's going on already, and remove old codemodel warnings m_messageCollector.cancel(); removeAllTasks(true); - buildSystem->buildTarget(Constants::QMLLINT_BUILD_TARGET); + buildSystem->buildNamedTarget(Constants::QMLLINT_BUILD_TARGET); return; } From fbaff902b85d16ac51e55de40bdd898a130a78df Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 21 Oct 2024 15:18:58 +0200 Subject: [PATCH 15/47] ProjectExplorer: Do not detect clang-cl executables more than once This fixes soft asserts in ToolchainManager. Change-Id: I629d57ae51fab90874b00685494c9aa1c49ef86d Reviewed-by: hjk --- src/plugins/projectexplorer/msvctoolchain.cpp | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index cac80d12235..ce4b4c05143 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1623,9 +1623,8 @@ static Toolchains detectClangClToolChainInPath(const FilePath &clangClPath, .arg(QLatin1String(isDefault ? "Default " : "")) .arg(targetAbi.wordWidth()) .arg(Abi::toString(targetAbi.osFlavor()).toUpper()); - for (auto language : {Id(Constants::C_LANGUAGE_ID), Id(Constants::CXX_LANGUAGE_ID)}) { - ClangClToolchain *tc = static_cast( - Utils::findOrDefault(alreadyKnown, [&](Toolchain *tc) -> bool { + for (const Id language : {Id(Constants::C_LANGUAGE_ID), Id(Constants::CXX_LANGUAGE_ID)}) { + if (Utils::findOrDefault(alreadyKnown, [&](Toolchain *tc) { if (tc->typeId() != Constants::CLANG_CL_TOOLCHAIN_TYPEID) return false; if (tc->targetAbi() != targetAbi) @@ -1633,18 +1632,17 @@ static Toolchains detectClangClToolChainInPath(const FilePath &clangClPath, if (tc->language() != language) return false; return tc->compilerCommand().isSameExecutable(clangClPath); - })); - if (tc) { - res << tc; - } else { - auto cltc = new ClangClToolchain; - cltc->setClangPath(clangClPath); - cltc->setDisplayName(name); - cltc->setDetection(Toolchain::AutoDetection); - cltc->setLanguage(language); - cltc->setupVarsBat(toolChain->targetAbi(), toolChain->varsBat(), toolChain->varsBatArg()); - res << cltc; + })) { + continue; } + + auto cltc = new ClangClToolchain; + cltc->setClangPath(clangClPath); + cltc->setDisplayName(name); + cltc->setDetection(Toolchain::AutoDetection); + cltc->setLanguage(language); + cltc->setupVarsBat(toolChain->targetAbi(), toolChain->varsBat(), toolChain->varsBatArg()); + res << cltc; } return res; } From a62d483ede13a95d269bd35723cea9b60731f39a Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 21 Oct 2024 15:28:11 +0200 Subject: [PATCH 16/47] ProjectExplorer: Fix toolchain bundling for llvm-mingw Change-Id: I1fd6b59a9192510d9049b7540ae654560a4872fb Reviewed-by: hjk --- src/plugins/projectexplorer/gcctoolchain.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 90c0d81f4d9..831e4aa35d7 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1606,6 +1606,12 @@ std::unique_ptr GccToolchainFactory::createConfigurationW FilePath GccToolchainFactory::correspondingCompilerCommand( const FilePath &srcPath, Id targetLang) const { + // llvm-mingw + if (supportedToolchainType() == Constants::MINGW_TOOLCHAIN_TYPEID + && srcPath.fileName().contains("clang")) { + return GccToolchain::correspondingCompilerCommand(srcPath, targetLang, "clang", "clang++"); + } + if (supportedToolchainType() == Constants::GCC_TOOLCHAIN_TYPEID || supportedToolchainType() == Constants::MINGW_TOOLCHAIN_TYPEID) { return GccToolchain::correspondingCompilerCommand(srcPath, targetLang, "gcc", "g++"); From 5a9630dff6e7c45b1afcb639edc8003afbd0a3ac Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 21 Oct 2024 13:53:28 +0200 Subject: [PATCH 17/47] Doc: Describe Debug > Start Debugging > Load Last Core File Task-number: QTCREATORBUG-31510 Change-Id: I6f974c32f0480f28abb97df1304c6a43ba86154e Reviewed-by: Friedemann Kleint Reviewed-by: hjk --- .../creator-only/creator-debugger.qdoc | 21 +++++++++++++++---- .../external-resources.qdoc | 4 ++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc index 26884aee027..130a4665e0d 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc @@ -421,6 +421,8 @@ generated from crashed processes on Linux and Unix systems if the system is set up to allow this. + \section1 Get core files + To enable the dumping of core files on a Unix system, enter the following command in the shell from which the application is launched: @@ -428,11 +430,13 @@ ulimit -c unlimited \endcode + \section1 Attach to a core file + To launch the debugger in the core mode: \list 1 - \li Go to \uicontrol Debug > \uicontrol {Start Debugging} > - \uicontrol {Load Core File}. + \li Go to \uicontrol Debug > \uicontrol {Start Debugging}, and then + select \uicontrol {Load Core File}. \image qtcreator-debugger-load-core-file.png \li In \uicontrol Kit, select a build and run kit that was used for building the binary for which the core file was created. @@ -452,8 +456,17 @@ the \c sysroot to use instead of the default \c sysroot. \endlist - Even though using a properly configured project that has the sources of the - crashed application is not strictly necessary, it is helpful. + For better results, use a properly configured project that has the sources of + the crashed application. + + \section1 Attach to the latest core file + + On Linux systems, \QC uses the \c coredumpctl command provided by the + \c systemd crash handling to get core files. For more information, see + \l {systemd-coredump}. + + To attach to the latest core file, go to \uicontrol Debug > + \uicontrol {Start Debugging}, and then select \uicontrol {Load Last Core File}. \sa {Debug}{How To: Debug}, {Debugging}, {Debuggers}, {Debugger}, {Kits} */ diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index bc9ee595a80..d7ab44bbe67 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -5,6 +5,10 @@ \externalpage https://doc.qt.io/index.html \title Qt Documentation */ +/*! + \externalpage https://www.freedesktop.org/software/systemd/man/latest/systemd-coredump.html + \title systemd-coredump +*/ /*! \externalpage https://www.perforce.com/manuals/cmdref/Content/CmdRef/P4CONFIG.html \title Perforce: P4CONFIG From 17c373138996a06614fb0af526955b811e290f86 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 21 Oct 2024 17:25:09 +0200 Subject: [PATCH 18/47] RemoteLinux: Do not use a shared connection when running test This will fail the !isDisconnected() check and bail out without re-setting the disconnected status. Also, make the m_useConnectionSharing value function local. It's used at max once before retesting. Change-Id: I16cb950db96c6829b599f2a0b361220f46a83b15 Reviewed-by: Christian Kandeler --- src/plugins/remotelinux/linuxdevice.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 705891e3900..f41514ed6e1 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -431,7 +431,6 @@ public: QByteArray m_output; QByteArray m_error; bool m_pidParsed = false; - bool m_useConnectionSharing = false; }; SshProcessInterface::SshProcessInterface(const IDevice::ConstPtr &device) @@ -689,13 +688,17 @@ void SshProcessInterfacePrivate::start() return; } - m_useConnectionSharing = SshSettings::connectionSharingEnabled() && !q->m_setup.m_extraData.value(Constants::DisableSharing).toBool(); + auto linuxDevice = std::dynamic_pointer_cast(m_device); + QTC_ASSERT(linuxDevice, handleDone(); return); + const bool useConnectionSharing = !linuxDevice->isDisconnected() + && SshSettings::connectionSharingEnabled() + && !q->m_setup.m_extraData.value(Constants::DisableSharing).toBool(); // TODO: Do we really need it for master process? m_sshParameters.x11DisplayName = q->m_setup.m_extraData.value("Ssh.X11ForwardToDisplay").toString(); - if (m_useConnectionSharing) { + if (useConnectionSharing) { m_connecting = true; m_connectionHandle.reset(new SshConnectionHandle(m_device)); m_connectionHandle->setParent(this); @@ -703,16 +706,6 @@ void SshProcessInterfacePrivate::start() this, &SshProcessInterfacePrivate::handleConnected); connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected, this, &SshProcessInterfacePrivate::handleDisconnected); - auto linuxDevice = std::dynamic_pointer_cast(m_device); - QTC_ASSERT(linuxDevice, handleDone(); return); - if (linuxDevice->isDisconnected()) { - emit q->done( - {-1, - QProcess::CrashExit, - QProcess::FailedToStart, - Tr::tr("Device \"%1\" is disconnected.").arg(linuxDevice->displayName())}); - return; - } linuxDevice->connectionAccess() ->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters); } else { From f3eb94145f525bb7a244574462459f267a857aee Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 18 Oct 2024 12:49:55 +0200 Subject: [PATCH 19/47] Utils: Make sure env expansion is cleaned We don't know what the user has in his environment, so better use "fromUserInput" Change-Id: I84e705a343dd1e389699f88e5d8ba5c59b02a7c9 Reviewed-by: hjk --- src/libs/utils/environment.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index f9bd493edc5..76906b5d599 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -337,7 +337,7 @@ QString Environment::expandVariables(const QString &input) const FilePath Environment::expandVariables(const FilePath &variables) const { - return FilePath::fromString(expandVariables(variables.toString())); + return FilePath::fromUserInput(expandVariables(variables.toString())); } QStringList Environment::expandVariables(const QStringList &variables) const From cd131f10068e0128d38e736b25d930821eb6667f Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 21 Oct 2024 10:27:18 +0200 Subject: [PATCH 20/47] Utils: Fix StringAspect enabler Previously m_checked->value() was used which might not have been updated yet. Using volatileValue makes sure that we get the actual Widget state. Change-Id: I1c86d809408a08a5ad483af7530d6852b0822531 Reviewed-by: hjk --- src/libs/utils/aspects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index cc8a5aa625b..bbebf7b26d8 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -896,7 +896,7 @@ public: template void updateWidgetFromCheckStatus(BaseAspect *aspect, Widget *w) { - const bool enabled = !m_checked || m_checked->value(); + const bool enabled = !m_checked || m_checked->volatileValue(); if (m_uncheckedSemantics == UncheckedSemantics::Disabled) w->setEnabled(enabled && aspect->isEnabled()); else From 6681ff5c1809543232a91eb6a401ebe7a86d8c92 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 11 Oct 2024 14:22:37 +0200 Subject: [PATCH 21/47] Axivion: Partially redo handling project selection If we cannot find the preferred project inside the selected dashboard we just dropped out with an error message which could easily be missed. Anyhow we should fetch the first available project in that case to refill other filter controls if the dashboard contains any. Only if the dashboard has no configured projects drop out early. Change-Id: Iff72ccb79b9ec3e11620ae435c5322f40269c9ef Reviewed-by: hjk Reviewed-by: Mohammad Mehdi Salem Naraghi --- src/plugins/axivion/axivionplugin.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 3f69cd5ee1e..39bc6167085 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -798,22 +798,12 @@ Group projectInfoRecipe(const QString &projectName) return SetupResult::StopWithError; } - const QString targetProjectName - = (projectName.isEmpty() && !dd->m_dashboardInfo->projects.isEmpty()) - ? dd->m_dashboardInfo->projects.first() : projectName; - - if (targetProjectName.isEmpty()) { + if (dd->m_dashboardInfo->projects.isEmpty()) { + updatePerspectiveToolbar(); updateDashboard(); return SetupResult::StopWithSuccess; } - const auto it = dd->m_dashboardInfo->projectUrls.constFind(targetProjectName); - if (it == dd->m_dashboardInfo->projectUrls.constEnd()) { - MessageManager::writeDisrupting(QString("Axivion: %1") - .arg(Tr::tr("The DashboardInfo doesn't contain project \"%1\".").arg(targetProjectName))); - return SetupResult::StopWithError; - } - const auto handler = [](const Dto::ProjectInfoDto &data) { dd->m_currentProjectInfo = data; if (!dd->m_currentProjectInfo->versions.empty()) @@ -822,6 +812,11 @@ Group projectInfoRecipe(const QString &projectName) updateDashboard(); }; + const QString targetProjectName = projectName.isEmpty() + ? dd->m_dashboardInfo->projects.first() : projectName; + auto it = dd->m_dashboardInfo->projectUrls.constFind(targetProjectName); + if (it == dd->m_dashboardInfo->projectUrls.constEnd()) + it = dd->m_dashboardInfo->projectUrls.constBegin(); taskTree.setRecipe( fetchDataRecipe(dd->m_dashboardInfo->source.resolved(*it), handler)); return SetupResult::Continue; From a3eca9a36c30ed1222e9223343214fde97409c35 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Mon, 21 Oct 2024 19:55:46 +0200 Subject: [PATCH 22/47] ExtensionManager: Implement latest design for inactive plugins The background gradient for the inactive extension icon is a dimmed green gradient (decreased opacity) instead of a gray one. The status label for inactive is also muted. Fixes: QTCREATORBUG-31872 Change-Id: I4aeebf899d16f3bf5d3a2078aec758779bc79901 Reviewed-by: Marcus Tillmanns --- share/qtcreator/themes/dark.figmatokens | 2 +- .../extensionmanager/extensionsbrowser.cpp | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/share/qtcreator/themes/dark.figmatokens b/share/qtcreator/themes/dark.figmatokens index 93262535a83..0c63a7755dd 100644 --- a/share/qtcreator/themes/dark.figmatokens +++ b/share/qtcreator/themes/dark.figmatokens @@ -42,7 +42,7 @@ Token_Notification_Success_Default=ff1EC974 Token_Notification_Success_Muted=ff12834B Token_Notification_Success_Subtle=ff092C1B -Token_Gradient01_Start=ff057880 +Token_Gradient01_Start=ff00414A Token_Gradient01_End=ff12A75D Token_Gradient02_Start=ff4A4A4A Token_Gradient02_End=ff909090 diff --git a/src/plugins/extensionmanager/extensionsbrowser.cpp b/src/plugins/extensionmanager/extensionsbrowser.cpp index 2ba17d22036..69ce63268f2 100644 --- a/src/plugins/extensionmanager/extensionsbrowser.cpp +++ b/src/plugins/extensionmanager/extensionsbrowser.cpp @@ -180,8 +180,10 @@ public: constexpr static TextFormat vendorTF {Theme::Token_Text_Muted, UiElement::UiElementLabelSmall, Qt::AlignVCenter | Qt::TextDontClip}; - constexpr static TextFormat stateTF + constexpr static TextFormat stateActiveTF {vendorTF.themeColor, UiElement::UiElementCaption, vendorTF.drawTextFlags}; + constexpr static TextFormat stateInactiveTF + {Theme::Token_Text_Subtle, stateActiveTF.uiElement, stateActiveTF.drawTextFlags}; constexpr static TextFormat descriptionTF {itemNameTF.themeColor, UiElement::UiElementCaption}; @@ -213,7 +215,7 @@ public: m_releaseStatus = tfLabel(releaseStatusTF, false); m_releaseStatus->setAlignment(Qt::AlignLeft); m_releaseStatus->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); - m_installStateLabel = tfLabel(stateTF, false); + m_installStateLabel = tfLabel(stateActiveTF, false); m_installStateLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); m_installStateIcon = new QLabel; m_installStateIcon->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); @@ -284,13 +286,17 @@ public: const bool showState = !stateString.isEmpty(); m_installState->setVisible(showState); if (showState) { + const bool active = state == InstalledEnabled; + QPalette pal = m_installStateLabel->palette(); + pal.setColor(QPalette::WindowText, (active ? stateActiveTF : stateInactiveTF).color()); + m_installStateLabel->setPalette(pal); m_installStateLabel->setText(stateString); const FilePath checkmarkMask = ":/extensionmanager/images/checkmark.png"; - static const QPixmap enabled = Icon({{checkmarkMask, Theme::Token_Accent_Muted}}, - Icon::Tint).pixmap(); - static const QPixmap disabled = Icon({{checkmarkMask, stateTF.themeColor}}, - Icon::Tint).pixmap(); - m_installStateIcon->setPixmap(state == InstalledEnabled ? enabled : disabled); + static const QPixmap iconActive = Icon({{checkmarkMask, Theme::Token_Accent_Muted}}, + Icon::Tint).pixmap(); + static const QPixmap iconInactive = Icon({{checkmarkMask, stateInactiveTF.themeColor}}, + Icon::Tint).pixmap(); + m_installStateIcon->setPixmap(active ? iconActive : iconInactive); } m_vendorLabel->setText(index.data(RoleVendor).toString()); @@ -772,10 +778,8 @@ QPixmap itemIcon(const QModelIndex &index, Size size) const PluginSpec *ps = pluginSpecForId(index.data(RoleId).toString()); const bool isEnabled = ps == nullptr || ps->isEffectivelyEnabled(); const QGradientStops gradientStops = { - {0, creatorColor(isEnabled ? Theme::Token_Gradient01_Start - : Theme::Token_Gradient02_Start)}, - {1, creatorColor(isEnabled ? Theme::Token_Gradient01_End - : Theme::Token_Gradient02_End)}, + {0, creatorColor(Theme::Token_Gradient01_Start)}, + {1, creatorColor(Theme::Token_Gradient01_End)}, }; const Theme::Color color = Theme::Token_Basic_White; @@ -790,14 +794,14 @@ QPixmap itemIcon(const QModelIndex &index, Size size) const ItemType itemType = index.data(RoleItemType).value(); const QIcon &icon = (itemType == ItemTypePack) ? (size == SizeSmall ? packS : packB) : (size == SizeSmall ? extensionS : extensionB); - const qreal iconOpacityDisabled = 0.6; + const qreal iconOpacityDisabled = 0.5; QPainter p(&pixmap); QLinearGradient gradient(iconBgR.topRight(), iconBgR.bottomLeft()); gradient.setStops(gradientStops); - WelcomePageHelpers::drawCardBackground(&p, iconBgR, gradient, Qt::NoPen, iconRectRounding); if (!isEnabled) p.setOpacity(iconOpacityDisabled); + WelcomePageHelpers::drawCardBackground(&p, iconBgR, gradient, Qt::NoPen, iconRectRounding); icon.paint(&p, iconBgR); return pixmap; From ec4537dc014bf7133be94dfd13caee211df0abb1 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 18 Oct 2024 14:52:44 +0200 Subject: [PATCH 23/47] Glsl: Fix internal declaration ..and add two missing functions. Change-Id: I5af6a190b76d7de2b6f45623d36c29565494285b Reviewed-by: David Schulz --- share/qtcreator/glsl/glsl_330.vert | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/share/qtcreator/glsl/glsl_330.vert b/share/qtcreator/glsl/glsl_330.vert index 0389cd2bc27..71e4eda386d 100644 --- a/share/qtcreator/glsl/glsl_330.vert +++ b/share/qtcreator/glsl/glsl_330.vert @@ -14,20 +14,9 @@ struct gl_PerVertex { float gl_PointSize; float gl_ClipDistance[]; }; - -struct gl_PerVertex { - vec4 gl_Position; - float gl_PointSize; - float gl_ClipDistance[]; -} gl_in[]; +uniform gl_PerVertex[] gl_in; int gl_PrimitiveIDIn; -struct gl_PerVertex { - vec4 gl_Position; - float gl_PointSize; - float gl_ClipDistance[]; -}; - int gl_PrimitiveID; int gl_Layer; @@ -38,13 +27,7 @@ struct gl_PerVertex { float gl_ClipDistance[]; vec4 gl_ClipVertex; }; - -struct gl_PerVertex { - vec4 gl_Position; - float gl_PointSize; - float gl_ClipDistance[]; - vec4 gl_ClipVertex; -} gl_in[]; +uniform gl_PerVertex[] gl_in; vec4 gl_Color; vec4 gl_SecondaryColor; @@ -59,3 +42,6 @@ vec4 gl_MultiTexCoord5; vec4 gl_MultiTexCoord6; vec4 gl_MultiTexCoord7; float gl_FogCoord; + +void EmitVertex(); +void EndPrimitive(); From 907c0a14010c1bfc130fdef96e7a855fa93a6603 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 15 Oct 2024 15:02:36 +0200 Subject: [PATCH 24/47] iOS: Do not post message on fallback transfer as error Amends 84cc7208dee3a8176ccbf24f7f46167c680eed7a Since that change, "error" messages from iostool are posted as errors in the Issues view. The problem with that is that iostool did not distinguish between errors and informative messages, so the message "Failed to transfer and install application, trying old way" was added as an error. Errors in the Issues view also make Qt Creator pop up the question if the user wants to continue with starting the application even though there are errors, which is wrong and annoying for that message. Split messages from iostool into errors and "normal" messages. Normal messages are not posted in the Issues view and use the "normal" font for the text in the output view. Fixes: QTCREATORBUG-31811 Change-Id: I99b456b408585440028f5211195d205cfeb54f09 Reviewed-by: Marcus Tillmanns --- src/plugins/ios/iosdeploystep.cpp | 5 +++++ src/plugins/ios/iosrunner.cpp | 20 +++++++++++------- src/plugins/ios/iostoolhandler.cpp | 8 ++++++- src/plugins/ios/iostoolhandler.h | 1 + src/tools/iostool/iosdevicemanager.cpp | 8 ++++++- src/tools/iostool/iosdevicemanager.h | 1 + src/tools/iostool/iostool.cpp | 29 +++++++++++++------------- src/tools/iostool/iostool.h | 4 ++-- src/tools/iostool/relayserver.cpp | 27 ++++++++++++------------ 9 files changed, 65 insertions(+), 38 deletions(-) diff --git a/src/plugins/ios/iosdeploystep.cpp b/src/plugins/ios/iosdeploystep.cpp index 11575821330..5e00230b551 100644 --- a/src/plugins/ios/iosdeploystep.cpp +++ b/src/plugins/ios/iosdeploystep.cpp @@ -54,6 +54,7 @@ public: int progress, int maxProgress, const QString &info) { emit progressValueChanged(progress * 100 / maxProgress, info); }); + connect(m_toolHandler.get(), &IosToolHandler::message, this, &IosTransfer::message); connect( m_toolHandler.get(), &IosToolHandler::errorMsg, @@ -85,6 +86,7 @@ public: signals: void done(DoneResult result); void progressValueChanged(int progress, const QString &info); // progress in % + void message(const QString &message); void errorMessage(const QString &message); private: @@ -253,6 +255,9 @@ GroupItem IosDeployStep::runRecipe() transfer.setExpectSuccess(checkProvisioningProfile()); emit progress(0, transferringMessage); connect(&transfer, &IosTransfer::progressValueChanged, this, &IosDeployStep::progress); + connect(&transfer, &IosTransfer::message, this, [this](const QString &message) { + emit addOutput(message, OutputFormat::NormalMessage); + }); connect(&transfer, &IosTransfer::errorMessage, this, [this](const QString &message) { emit addOutput(message, OutputFormat::ErrorMessage); }); diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index f2b59a46948..a3396892ac0 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -421,6 +421,7 @@ private: void handleGotInferiorPid(Ios::IosToolHandler *handler, const FilePath &bundlePath, const QString &deviceId, qint64 pid); void handleAppOutput(Ios::IosToolHandler *handler, const QString &output); + void handleMessage(const QString &msg); void handleErrorMsg(Ios::IosToolHandler *handler, const QString &msg); void handleToolExited(Ios::IosToolHandler *handler, int code); void handleFinished(Ios::IosToolHandler *handler); @@ -529,8 +530,8 @@ void IosRunner::start() m_toolHandler = new IosToolHandler(m_deviceType, this); connect(m_toolHandler, &IosToolHandler::appOutput, this, &IosRunner::handleAppOutput); - connect(m_toolHandler, &IosToolHandler::errorMsg, - this, &IosRunner::handleErrorMsg); + connect(m_toolHandler, &IosToolHandler::message, this, &IosRunner::handleMessage); + connect(m_toolHandler, &IosToolHandler::errorMsg, this, &IosRunner::handleErrorMsg); connect(m_toolHandler, &IosToolHandler::gotServerPorts, this, &IosRunner::handleGotServerPorts); connect(m_toolHandler, &IosToolHandler::gotInferiorPid, @@ -627,6 +628,16 @@ void IosRunner::handleAppOutput(IosToolHandler *handler, const QString &output) appendMessage(output, StdOutFormat); } +void IosRunner::handleMessage(const QString &msg) +{ + QString res(msg); + QRegularExpression qmlPortRe("QML Debugger: Waiting for connection on port ([0-9]+)..."); + const QRegularExpressionMatch match = qmlPortRe.match(msg); + if (match.hasMatch() && m_qmlServerPort.isValid()) + res.replace(match.captured(1), QString::number(m_qmlServerPort.number())); + appendMessage(res, StdOutFormat); +} + void IosRunner::handleErrorMsg(IosToolHandler *handler, const QString &msg) { Q_UNUSED(handler) @@ -640,11 +651,6 @@ void IosRunner::handleErrorMsg(IosToolHandler *handler, const QString &msg) TaskHub::addTask(DeploymentTask(Task::Error, message)); res.replace(lockedErr, message); } - QRegularExpression qmlPortRe("QML Debugger: Waiting for connection on port ([0-9]+)..."); - const QRegularExpressionMatch match = qmlPortRe.match(msg); - if (match.hasMatch() && m_qmlServerPort.isValid()) - res.replace(match.captured(1), QString::number(m_qmlServerPort.number())); - appendMessage(res, StdErrFormat); } diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index 0626a552533..7e90da0d633 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -93,6 +93,7 @@ signals: struct ParserState { enum Kind { Msg, + Error, DeviceId, Key, Value, @@ -119,6 +120,7 @@ struct ParserState { bool collectChars() { switch (kind) { case Msg: + case Error: case DeviceId: case Key: case Value: @@ -386,6 +388,8 @@ void IosDeviceToolHandlerPrivate::processXml() const auto elName = outputParser.name(); if (elName == QLatin1String("msg")) { stack.append(ParserState(ParserState::Msg)); + } else if (elName == QLatin1String("error")) { + stack.append(ParserState(ParserState::Error)); } else if (elName == QLatin1String("exit")) { stack.append(ParserState(ParserState::Exit)); toolExited(outputParser.attributes().value(QLatin1String("code")) @@ -459,8 +463,10 @@ void IosDeviceToolHandlerPrivate::processXml() stack.removeLast(); switch (p.kind) { case ParserState::Msg: - errorMsg(p.chars); + emit q->message(p.chars); break; + case ParserState::Error: + errorMsg(p.chars); case ParserState::DeviceId: if (m_deviceId.isEmpty()) m_deviceId = p.chars; diff --git a/src/plugins/ios/iostoolhandler.h b/src/plugins/ios/iostoolhandler.h index 874d5f7f5c3..b7ad9413aef 100644 --- a/src/plugins/ios/iostoolhandler.h +++ b/src/plugins/ios/iostoolhandler.h @@ -62,6 +62,7 @@ signals: void deviceInfo(Ios::IosToolHandler *handler, const QString &deviceId, const Ios::IosToolHandler::Dict &info); void appOutput(Ios::IosToolHandler *handler, const QString &output); + void message(const QString &msg); void errorMsg(Ios::IosToolHandler *handler, const QString &msg); void toolExited(Ios::IosToolHandler *handler, int code); void finished(Ios::IosToolHandler *handler); diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp index 3f523807857..e96149aaa26 100644 --- a/src/tools/iostool/iosdevicemanager.cpp +++ b/src/tools/iostool/iosdevicemanager.cpp @@ -218,6 +218,7 @@ public: void startDeviceLookup(int timeout); bool connectToPort(quint16 port, ServiceSocket *fd) override; int qmljsDebugPort() const override; + void addMessage(const QString &msg); void addError(const QString &msg); bool writeAll(ServiceSocket fd, const char *cmd, qptrdiff len = -1); bool mountDeveloperDiskImage(); @@ -957,6 +958,11 @@ void CommandSession::startDeviceLookup(int timeout) this); } +void CommandSession::addMessage(const QString &msg) +{ + IosDeviceManager::instance()->message(msg); +} + void CommandSession::addError(const QString &msg) { qCCritical(loggingCategory) << "CommandSession ERROR:" << msg; @@ -1303,7 +1309,7 @@ bool AppOpSession::installApp() bool success = false; if (device) { if (!installAppNew()) { - addError(QString::fromLatin1( + addMessage(QString::fromLatin1( "Failed to transfer and install application, trying old way ...")); const CFUrl_t bundleUrl(QUrl::fromLocalFile(bundlePath).toCFURL()); diff --git a/src/tools/iostool/iosdevicemanager.h b/src/tools/iostool/iosdevicemanager.h index ecc192a82fe..42b21f00715 100644 --- a/src/tools/iostool/iosdevicemanager.h +++ b/src/tools/iostool/iosdevicemanager.h @@ -58,6 +58,7 @@ signals: Ios::DeviceSession *deviceSession); void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info); void appOutput(const QString &output); + void message(const QString &msg); void errorMsg(const QString &msg); private: diff --git a/src/tools/iostool/iostool.cpp b/src/tools/iostool/iostool.cpp index d788fb18b2d..b710997d448 100644 --- a/src/tools/iostool/iostool.cpp +++ b/src/tools/iostool/iostool.cpp @@ -92,7 +92,7 @@ void IosTool::run(const QStringList &args) bool ok = false; timeout = cmdLineParser.value("timeout").toInt(&ok); if (!ok || timeout < 0) { - writeMsg("timeout value should be an integer"); + writeError("timeout value should be an integer"); printHelp = true; } } @@ -118,7 +118,8 @@ void IosTool::run(const QStringList &args) connect(manager, &IosDeviceManager::didStartApp, this, &IosTool::didStartApp); connect(manager, &IosDeviceManager::deviceInfo, this, &IosTool::deviceInfo); connect(manager, &IosDeviceManager::appOutput, this, &IosTool::appOutput); - connect(manager, &IosDeviceManager::errorMsg, this, &IosTool::errorMsg); + connect(manager, &IosDeviceManager::message, this, &IosTool::writeMsg); + connect(manager, &IosDeviceManager::errorMsg, this, &IosTool::writeError); manager->watchDevices(); const QRegularExpression qmlPortRe(QLatin1String("-qmljsdebugger=port:([0-9]+)")); for (const QString &arg : extraArgs) { @@ -233,12 +234,12 @@ void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, return; } if (gdbFd <= 0) { - writeMsg("no gdb connection"); + writeError("no gdb connection"); doExit(-2); return; } if (m_requestedOperation != IosDeviceManager::InstallAndRun && m_requestedOperation != IosDeviceManager::Run) { - writeMsg(QString::fromLatin1("unexpected appOp value %1").arg(m_requestedOperation)); + writeError(QString::fromLatin1("unexpected appOp value %1").arg(m_requestedOperation)); doExit(-3); return; } @@ -283,15 +284,15 @@ void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, } } -void IosTool::writeMsg(const char *msg) -{ - writeMsg(QString::fromLatin1(msg)); -} - void IosTool::writeMsg(const QString &msg) +{ + writeMessageElement(msg, "msg"); +} + +void IosTool::writeMessageElement(const QString &msg, const QString &element) { QMutexLocker l(&m_xmlMutex); - m_xmlWriter.writeStartElement(QLatin1String("msg")); + m_xmlWriter.writeStartElement(element); writeTextInElement(msg); m_xmlWriter.writeCharacters(QLatin1String("\n")); m_xmlWriter.writeEndElement(); @@ -380,15 +381,15 @@ void IosTool::readStdin() int c = getchar(); if (c == 'k') { QMetaObject::invokeMethod(this, "stopGdbRunner"); - errorMsg(QLatin1String("iostool: Killing inferior.\n")); + writeError(QLatin1String("iostool: Killing inferior.\n")); } else if (c != EOF) { - errorMsg(QLatin1String("iostool: Unexpected character in stdin, stop listening.\n")); + writeError(QLatin1String("iostool: Unexpected character in stdin, stop listening.\n")); } } -void IosTool::errorMsg(const QString &msg) +void IosTool::writeError(const QString &msg) { - writeMsg(msg); + writeMessageElement(msg, "error"); } void IosTool::stopGdbRunner() diff --git a/src/tools/iostool/iostool.h b/src/tools/iostool/iostool.h index 2135166e660..909736969db 100644 --- a/src/tools/iostool/iostool.h +++ b/src/tools/iostool/iostool.h @@ -27,17 +27,17 @@ public: virtual ~IosTool(); void run(const QStringList &args); void doExit(int errorCode = 0); - void writeMsg(const char *msg); void writeMsg(const QString &msg); void stopXml(int errorCode); void writeTextInElement(const QString &output); void stopRelayServers(int errorCode = 0); void writeMaybeBin(const QString &extraMsg, const char *msg, quintptr len); - void errorMsg(const QString &msg); + void writeError(const QString &msg); Q_INVOKABLE void stopGdbRunner(); bool echoRelays() const { return m_echoRelays; } private: + void writeMessageElement(const QString &msg, const QString &element); void stopGdbRunner2(); void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress, const QString &info); diff --git a/src/tools/iostool/relayserver.cpp b/src/tools/iostool/relayserver.cpp index 0ea1be33ce9..0d329661db2 100644 --- a/src/tools/iostool/relayserver.cpp +++ b/src/tools/iostool/relayserver.cpp @@ -69,7 +69,7 @@ void Relayer::handleSocketHasData(int socket) m_serverNotifier->setEnabled(true); return; } - iosTool()->errorMsg(qt_error_string(errno)); + iosTool()->writeError(qt_error_string(errno)); close(socket); iosTool()->stopRelayServers(-1); return; @@ -86,7 +86,7 @@ void Relayer::handleSocketHasData(int socket) while (true) { qint64 writtenNow = m_clientSocket->write(buf + int(pos), rRead); if (writtenNow == -1) { - iosTool()->writeMsg(m_clientSocket->errorString()); + iosTool()->writeError(m_clientSocket->errorString()); iosTool()->stopRelayServers(-1); return; } @@ -110,7 +110,7 @@ void Relayer::handleClientHasData() toRead = sizeof(buf)-1; qint64 rRead = m_clientSocket->read(buf, toRead); if (rRead == -1) { - iosTool()->errorMsg(m_clientSocket->errorString()); + iosTool()->writeError(m_clientSocket->errorString()); iosTool()->stopRelayServers(); return; } @@ -137,7 +137,7 @@ void Relayer::handleClientHasData() } continue; } - iosTool()->errorMsg(qt_error_string(errno)); + iosTool()->writeError(qt_error_string(errno)); iosTool()->stopRelayServers(); return; } @@ -157,7 +157,7 @@ void Relayer::handleClientHasData() void Relayer::handleClientHasError(QAbstractSocket::SocketError error) { - iosTool()->errorMsg(tr("iOS Debugging connection to creator failed with error %1").arg(error)); + iosTool()->writeError(tr("iOS debugging connection failed with error %1").arg(error)); server()->removeRelayConnection(this); } @@ -207,7 +207,7 @@ RemotePortRelayer::RemotePortRelayer(QmlRelayServer *parent, QTcpSocket *clientS void RemotePortRelayer::tryRemoteConnect() { - iosTool()->errorMsg(QLatin1String("tryRemoteConnect")); + iosTool()->writeMsg(QLatin1String("tryRemoteConnect")); if (m_serverFileDescriptor > 0) return; ServiceSocket ss; @@ -216,15 +216,16 @@ void RemotePortRelayer::tryRemoteConnect() return; if (grServer->m_deviceSession->connectToPort(grServer->m_remotePort, &ss)) { if (ss > 0) { - iosTool()->errorMsg(QString::fromLatin1("tryRemoteConnect *succeeded* on remote port %1") - .arg(grServer->m_remotePort)); + iosTool()->writeMsg( + QString::fromLatin1("tryRemoteConnect *succeeded* on remote port %1") + .arg(grServer->m_remotePort)); startRelay(ss); emit didConnect(grServer); return; } } - iosTool()->errorMsg(QString::fromLatin1("tryRemoteConnect *failed* on remote port %1") - .arg(grServer->m_remotePort)); + iosTool()->writeMsg(QString::fromLatin1("tryRemoteConnect *failed* on remote port %1") + .arg(grServer->m_remotePort)); m_remoteConnectTimer.start(); } @@ -277,7 +278,7 @@ IosTool *RelayServer::iosTool() const void RelayServer::handleNewRelayConnection() { - iosTool()->errorMsg(QLatin1String("handleNewRelayConnection")); + iosTool()->writeMsg(QLatin1String("handleNewRelayConnection")); newRelayConnection(); } @@ -318,7 +319,7 @@ QmlRelayServer::QmlRelayServer(IosTool *parent, int remotePort, m_remotePort(remotePort), m_deviceSession(deviceSession) { - parent->errorMsg(QLatin1String("created qml server")); + parent->writeMsg(QLatin1String("created qml server")); } @@ -327,7 +328,7 @@ void QmlRelayServer::newRelayConnection() QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); if (clientSocket) { - iosTool()->errorMsg(QString::fromLatin1("setting up relayer for new connection")); + iosTool()->writeMsg(QString::fromLatin1("setting up relayer for new connection")); RemotePortRelayer *newConnection = new RemotePortRelayer(this, clientSocket); m_connections.append(newConnection); newConnection->tryRemoteConnect(); From 63f4ecd32bb5def19e574d4bc9b20555af751d35 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 18 Oct 2024 14:02:11 +0200 Subject: [PATCH 25/47] Axivion: Remove frame around dock widget content Matches the style that we use for other widgets and looks better. Change-Id: Idaa0ed64aa4b4fa05a9cfaf076161a3adfb3204e Reviewed-by: Christian Stenger --- src/plugins/axivion/axivionperspective.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/axivion/axivionperspective.cpp b/src/plugins/axivion/axivionperspective.cpp index b3dfdee5122..e10fc96b31a 100644 --- a/src/plugins/axivion/axivionperspective.cpp +++ b/src/plugins/axivion/axivionperspective.cpp @@ -235,6 +235,7 @@ private: IssuesWidget::IssuesWidget(QWidget *parent) : QScrollArea(parent) { + setFrameStyle(QFrame::NoFrame); QWidget *widget = new QWidget(this); m_dashboards = new QComboBox(this); m_dashboards->setMinimumContentsLength(15); @@ -874,6 +875,7 @@ void AxivionPerspective::initPerspective() m_issuesWidget->setPalette(pal); m_issueDetails = new QTextBrowser; + m_issueDetails->setFrameStyle(QFrame::NoFrame); m_issueDetails->setObjectName("AxivionIssuesDetails"); m_issueDetails->setWindowTitle(Tr::tr("Issue Details")); const QString text = Tr::tr( From 3bb4419aec9a2f126c0b352bfc055b6efa878c01 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 21 Oct 2024 13:42:46 +0200 Subject: [PATCH 26/47] Utils: Add one more known environment list variable Change-Id: Icec0ab49d126d1cdc3e2bcf83fb82e35aa71a54a Reviewed-by: Eike Ziller Reviewed-by: hjk --- src/libs/utils/environmentmodel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp index 8e0458b429a..a2f9c7ab3cd 100644 --- a/src/libs/utils/environmentmodel.cpp +++ b/src/libs/utils/environmentmodel.cpp @@ -419,8 +419,10 @@ bool EnvironmentModel::currentEntryIsPathList(const QModelIndex ¤t) const const QString varName = indexToVariable(current); if (varName.compare("PATH", Utils::HostOsInfo::fileNameCaseSensitivity()) == 0) return true; - if (Utils::HostOsInfo::isMacHost() && varName == "DYLD_LIBRARY_PATH") + if (Utils::HostOsInfo::isMacHost() + && (varName == "DYLD_LIBRARY_PATH" || varName == "DYLD_FRAMEWORK_PATH")) { return true; + } if (Utils::HostOsInfo::isAnyUnixHost() && varName == "LD_LIBRARY_PATH") return true; if (varName == "PKG_CONFIG_DIR") From 739cd306306adae165c1ee15bdc261a669c66790 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 22 Oct 2024 08:02:14 +0200 Subject: [PATCH 27/47] Utils: Hide FilePath access setter a bit Make it clear that it can be called only once. Change-Id: Iac46a03c3a9a929127dbfcf7112b17dda6c07dee Reviewed-by: Marcus Tillmanns --- src/libs/utils/filepath.cpp | 62 +++++++++++-------- src/libs/utils/filepath.h | 6 +- .../devicesupport/devicemanager.cpp | 4 +- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index c7231a9976a..3602461773e 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -35,9 +35,29 @@ namespace Utils { -static DeviceFileHooks s_deviceHooks; -inline bool isWindowsDriveLetter(QChar ch); +static DeviceFileHooks &deviceFileHooks() +{ + static DeviceFileHooks theDeviceHooks; + return theDeviceHooks; +} +const DeviceFileHooks &DeviceFileHooks::instance() +{ + return deviceFileHooks(); +} + +void DeviceFileHooks::setupDeviceFileHooks(const DeviceFileHooks &hooks) +{ + static bool wasAlreadySet = false; + QTC_ASSERT(!wasAlreadySet, return); + wasAlreadySet = true; + deviceFileHooks() = hooks; +} + +static bool isWindowsDriveLetter(QChar ch) +{ + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); +} /*! \class Utils::FilePath @@ -705,8 +725,8 @@ expected_str FilePath::fileContents(qint64 maxSize, qint64 offset) c bool FilePath::ensureReachable(const FilePath &other) const { if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.ensureReachable, return false); - return s_deviceHooks.ensureReachable(*this, other); + QTC_ASSERT(deviceFileHooks().ensureReachable, return false); + return deviceFileHooks().ensureReachable(*this, other); } else if (!other.needsDevice()) { return true; } @@ -747,8 +767,8 @@ bool FilePath::isSameDevice(const FilePath &other) const if (!needsDevice() && !other.needsDevice()) return true; - QTC_ASSERT(s_deviceHooks.isSameDevice, return true); - return s_deviceHooks.isSameDevice(*this, other); + QTC_ASSERT(deviceFileHooks().isSameDevice, return true); + return deviceFileHooks().isSameDevice(*this, other); } bool FilePath::isSameFile(const FilePath &other) const @@ -1116,8 +1136,8 @@ QString FilePath::displayName(const QString &args) const { QString deviceName; if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.deviceDisplayName, return nativePath()); - deviceName = s_deviceHooks.deviceDisplayName(*this); + QTC_ASSERT(deviceFileHooks().deviceDisplayName, return nativePath()); + deviceName = deviceFileHooks().deviceDisplayName(*this); } const QString fullPath = nativePath(); @@ -1169,11 +1189,6 @@ FilePath FilePath::fromString(const QString &filepath) return fn; } -bool isWindowsDriveLetter(QChar ch) -{ - return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); -} - void FilePath::setPath(QStringView path) { setParts(scheme(), host(), path); @@ -1250,13 +1265,13 @@ static expected_str getFileAccess(const FilePath &filePath) if (!filePath.needsDevice()) return DesktopDeviceFileAccess::instance(); - if (!s_deviceHooks.fileAccess) { + if (!deviceFileHooks().fileAccess) { // Happens during startup and in tst_fsengine QTC_CHECK(false); return DesktopDeviceFileAccess::instance(); } - return s_deviceHooks.fileAccess(filePath); + return deviceFileHooks().fileAccess(filePath); } DeviceFileAccess *FilePath::fileAccess() const @@ -1863,8 +1878,8 @@ Environment FilePath::deviceEnvironment() const expected_str FilePath::deviceEnvironmentWithError() const { if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.environment, return {}); - return s_deviceHooks.environment(*this); + QTC_ASSERT(deviceFileHooks().environment, return {}); + return deviceFileHooks().environment(*this); } return Environment::systemEnvironment(); } @@ -1976,8 +1991,8 @@ OsType FilePath::osType() const if (!needsDevice()) return HostOsInfo::hostOs(); - QTC_ASSERT(s_deviceHooks.osType, return HostOsInfo::hostOs()); - return s_deviceHooks.osType(*this); + QTC_ASSERT(deviceFileHooks().osType, return HostOsInfo::hostOs()); + return deviceFileHooks().osType(*this); } Result FilePath::removeFile() const @@ -2295,9 +2310,9 @@ expected_str FilePath::localSource() const if (!needsDevice()) return *this; - QTC_ASSERT(s_deviceHooks.localSource, + QTC_ASSERT(deviceFileHooks().localSource, return make_unexpected(Tr::tr("No \"localSource\" device hook set."))); - return s_deviceHooks.localSource(*this); + return deviceFileHooks().localSource(*this); } /*! @@ -2436,11 +2451,6 @@ QStringList FileFilter::asFindArguments(const QString &path) const return arguments; } -DeviceFileHooks &DeviceFileHooks::instance() -{ - return s_deviceHooks; -} - QTCREATOR_UTILS_EXPORT bool operator==(const FilePath &first, const FilePath &second) { return FilePath::equals(first, second, first.caseSensitivity()); diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index b79ef3c1d71..ac236cd3866 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -349,7 +349,7 @@ private: class QTCREATOR_UTILS_EXPORT DeviceFileHooks { public: - static DeviceFileHooks &instance(); + static const DeviceFileHooks &instance(); std::function(const FilePath &)> fileAccess; std::function deviceDisplayName; @@ -359,8 +359,12 @@ public: std::function(const FilePath &)> localSource; std::function openTerminal; std::function osType; + + // Only call once. + static void setupDeviceFileHooks(const DeviceFileHooks &hooks); }; + // For testing QTCREATOR_UTILS_EXPORT QString doCleanPath(const QString &input); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 50864bef432..4af922fae0a 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -400,7 +400,7 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniqueosType(); }; + DeviceFileHooks::setupDeviceFileHooks(deviceHooks); + DeviceProcessHooks processHooks; processHooks.processImplHook = [](const FilePath &filePath) -> ProcessInterface * { From 53fe7d704c6c6d43de28f1b15b73f1c1e13742f8 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 22 Oct 2024 09:29:39 +0200 Subject: [PATCH 28/47] Utils: Remove access to DeviceFileHooks singleton Re-route through FilePath instead. Not exactly the right place to talk about terminals, but practically not abusable. Change-Id: I55d7747329d120a0d3218c031e749fb0f4dae143 Reviewed-by: Marcus Tillmanns --- src/libs/utils/filepath.cpp | 10 +++++----- src/libs/utils/filepath.h | 3 +-- src/libs/utils/terminalhooks.cpp | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 3602461773e..9ec36e09fb0 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -41,11 +41,6 @@ static DeviceFileHooks &deviceFileHooks() return theDeviceHooks; } -const DeviceFileHooks &DeviceFileHooks::instance() -{ - return deviceFileHooks(); -} - void DeviceFileHooks::setupDeviceFileHooks(const DeviceFileHooks &hooks) { static bool wasAlreadySet = false; @@ -348,6 +343,11 @@ Utils::expected_str> FilePath::watch() const return fileAccess()->watch(*this); } +void FilePath::openTerminal(const Environment &env) const +{ + deviceFileHooks().openTerminal(*this, env); +} + /*! Returns a QString for passing on to QString based APIs. diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index ac236cd3866..36ab6ad532f 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -314,6 +314,7 @@ public: bool equalsCaseSensitive(const FilePath &other) const; Utils::expected_str> watch() const; + void openTerminal(const Environment &env) const; private: // These are needed. @@ -349,8 +350,6 @@ private: class QTCREATOR_UTILS_EXPORT DeviceFileHooks { public: - static const DeviceFileHooks &instance(); - std::function(const FilePath &)> fileAccess; std::function deviceDisplayName; std::function ensureReachable; diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp index 3e5151e6f39..0084121bce9 100644 --- a/src/libs/utils/terminalhooks.cpp +++ b/src/libs/utils/terminalhooks.cpp @@ -38,9 +38,8 @@ public: HooksPrivate() { auto openTerminal = [](const OpenTerminalParameters ¶meters) { - DeviceFileHooks::instance().openTerminal(parameters.workingDirectory.value_or( - FilePath{}), - parameters.environment.value_or(Environment{})); + parameters.workingDirectory.value_or(FilePath{}) + .openTerminal(parameters.environment.value_or(Environment{})); }; auto createProcessInterface = [] { return new ExternalTerminalProcessImpl; }; From 7ec59c764939ad032754ffe459c946836f132596 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 22 Oct 2024 11:11:52 +0200 Subject: [PATCH 29/47] Docker: Fix crash when deleting containers Deleting containers while they are running triggers a stopContainer from a different thread. We must make sure that the FileAccess thread is deleted from the original creator thread though, so we defer the deletion. Change-Id: I02c7a8ba71400e16bd23144f0fe03ee9b14a8a52 Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index c7427e5fcb1..b3f516b076c 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -763,8 +763,14 @@ void DockerDevicePrivate::stopCurrentContainer() return; auto fileAccess = m_fileAccess.writeLocked(); - if (*fileAccess) - fileAccess->reset(); + if (*fileAccess) { + if (QThread::currentThread() == thread()) { + fileAccess->reset(); + } else { + QMetaObject::invokeMethod( + this, [ptr = fileAccess->release()]() { delete ptr; }, Qt::QueuedConnection); + } + } Process proc; proc.setCommand({settings().dockerBinaryPath(), {"container", "kill", m_container}}); From 21a92af6b82cc6011b30209c5fb071f2cb86cd62 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 14 Oct 2024 16:29:06 +0200 Subject: [PATCH 30/47] Doc: Update the screenshot of Sessions in the Welcome mode It now shows just files if the session doesn't include projects. Change-Id: I4d4294f6773fadfdd56652e1bf2fd6372c4c513a Reviewed-by: Eike Ziller --- .../images/qtcreator-welcome-session.webp | Bin 2282 -> 4416 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/qtcreator/images/qtcreator-welcome-session.webp b/doc/qtcreator/images/qtcreator-welcome-session.webp index 7a67042f1cb4c1a4d4d88f0bad68d4a6564c4a54..3911bd66e65b44ac0ede615e4d3e7c34d84150de 100644 GIT binary patch literal 4416 zcmWIYbaS&1WMBw)bqWXzu<+3lWMI&5JQ&9CHDcQT`=z&^1>RYIxqAJ(vRPiMc9h<% zJR@n_puIg)>U-Y$povPkk3*%dUzz-Xx8&dc|NmoRVqVGZn6T_bK&r;+-mX)HjnYAq z{Vt5166RhWg$sE#L@%9edD4*Q;n3rI?!p{zr%l`5RuuD3j?CR%za}%cugN!L^#`>( z8wLMb%)GiSclUqInA_WWZCC&Ke`DL*E!*VZz5f56F;Db%nctf~+pNU@PG`NnZGzeD zDVicF_rJ3+313<~pWSi!+J5r`S3f9wIIWPY-z5~s|Lp5?_6-buEd^HtBUr9C7S@J} zJN?j#V#+o(1bic^B+*Dot z)2};W(G-;f6Dk>;RogCgt4-Rpl=sw!lD7>uIj*nuBEJg#uMbH+G?n4by#rZ!$_uBt z?}+=dVa}@5qtac*ZpWsJ8{M~SFwMN6$tZ90+V9Sx=~7yndY#K2&2wkjd*Wg8)vumE zUo3FrYiiEO6#f}ex;JTOzCoY4=ET&H(&C3(iUt05 zmd+`jZ+iE@s~dhizAUPKac)MwBDb*E?|-lCKlym^ z!@sHx-U2>KCif#QE4myy9{gh_kL|p2=jjvH3-pCvJe;BUPBv8JRuKQY`x9-p2{g4B zOq%lY=w8LXrMc7WH^1dfyEucxYuC{OjnmRz3w}}R_?x?^QX=fKTG+ANmCv>*b2v85 zYdbHL?`7{-F864jxDSge1HbX3^8G1J990vm_4NK^Yltm5mo}@o;Fj{a zT#+wI)1U4;7=J(i%k{-qF0T-t;PYVCo8F~6{(V(HDi_D{?EmFS|9A7f(`$M2zC2%} zN9y`@(GRaJLi*$FVVhqo2%;tY!0O!;NiWhD1s&-wK4 zVMR*K`nOB2t~~c9_WHxXYp*Wah!(pPd2|}L?-k#wo&C3)ivYdW)o7Gq0 zZ)bK~FLqtzWajq4y_WUN&wC4}YO476)qXIQ`1d$7VA@O>oyh9I^Y6dU^0ACMI4`Wf z?$A6*wsbzPbGvH#68W0HJAZw~e|X)itsgcwuyb!y`}1LUdt3W{9E@WjDI^D(Gg;>fp;sEZ!ejn^Y$VUVnAvS!S5jhDeX2 z4s6evN-gSa1r0RMD4mN8;(k(`#me+^&RUUY>g*>wk_BGh+HIZ@RIha7>Q%pKOgS45 z**N;wrLBK6gMG0v`#*+BR*^QQ=Zp{fJjn7;D{wUs-l28xrM>yA6xRZUzQ-!pSViTn zq<*KJo%OkU8}rMT$GVzoA0J(uxisYBe8vr1QcBa79=X&r>j|f4-|qCPM-{237EW*f zI>TG*_}wz^AjQ7Z*Cw5O^7Fm3q2-+SvO#Qjk9oCDd#iP0rrg%v&|O?yw`8`fh`!dD zG3Wi#-4~nfKW;3MaaLXC+oiTie)Y`CoUn^8v@Wb()EF&2!Di#RS#LU8|83iLF;t-A z<;C?rEhnw7A5CRW3o_pjc5GF0(9M#>$<=F;yyNY^mD=6$xIvqpYB2HE^P0+$1LTLb2B=9gJ73`AIG{)=GX4%&GfqyxVfy#^NsCSZ(b?2 zgSY2wy{eeeo#mVHn&D$O!}o*lwq5Srz1?!k&Qfj8kgxiyKP0R+-I$s`cZbM=YcoSO zTw6Xdq$q*qU-`nj%cGOxHiVyecB#~@<7H!m=RyXx@|S0`#Wc-cvn;<|)&0SVX|>7W z(_a|2=>%skWXQ@DoDs3ML6+sp@y)BIuP`n!&b;QDK1Vu8jluLj!^)ehino~RcIx`v zd6?z8mD{;BZ7##*PU{#Ock5ZR<}+>BVt;1G$2APXE0RmE&N_O1)}4KO&ZK?1dQV8T zqIF%`%bBxEHMliGFA3V@&0W~FAXU z`hWiuJIL1iPW4LwbK7L^%Cki|bE{H1a^)_FePvuRcUIc!^tX+BEw^2-*!GSs$9A4_ z>&v%WYA2@GGW@#j&v;_8@{j(~s#cF1R@KYA6CJK?3DYR<2nl4e$(oimcjd*6EZPDk zEd7gpc&@HsTr%zG-#1_E-#wYzlXoMI=}N&1BbD633ojDoJ$5hO#WCYU)9ZC@?Qhf< zPQF<(VbwK}FQ>|#FEgLDx+t!YGyPP{O_95kt5rNE|5~29_QA#a3QkpNca<%vTtXK@ zu1bB?*uG&B-}fC;Y_2gpN^n^jmhHs(qV28f0x7;g-4l&&`SO!9#g|y-G<5Pr+U^MC zUb$KBK-}7wTmozz0gDV~s${G;`M=ux`d;^=rT>(*7wQVDFsW=jcu!Hse~!(k2glt1 zPZafXF#jUf(B|B|X!Ta+C31HcxP9n|ls&)d*rEKnCs?Fg-!i<~vi`EcM_t%zKbscUfHm%wq8)TS1LPjBX=xm*Ⓢc%I&*}6dv+HYw zSF)4@zg^n=wsEI}wZy@*^)}oG1UJ8snWGo-vVvVd@OjMUa|>^6wwPC2a3WUf>E17F zcN6-HO}Fh1sGhj1_~V@UOckwvc6b-%=!a}xVYyd!S@wk~njRDWPu}2oKuOiidoN_KicW@U{;R%n;jp1l;`mMyTw|% zxnAnSiOi}+oXeKo6PNqqxa&_pA2*knum8pEZZ3=vhFyTs$~j zP$401FL_T~?g{IcrUvnei@$Y#Ur>5w<0NN&{&Umz8U0;1QMXo`>)Xq6!wcRE3+pbH zF&yu$zH#~OKi~hh8?;&a?|<>nnKkp4uTqAnz?uf9^$G{Fy3R3btl{9Ba`kiL(OnjO z)j{tKZ>6u5pA(cV_$xDORi{RY?Cid<#tlC@vJ1t1nmy?~uB5koS75xPrt<$`zF zzP4u<%FMYK7Bb60(OJ_)$2QWWSu{;AH7@72!^WoN(-v+v`kB4jThzw1`Pt_XhoH_m z8>a8+-o*LIV_C*p-GzrH1v~Nnl$m^V!t99?R+k(MyW6yezpMC#{>3Hn3J;#2RQ$Zh zCLv6xShIrhMx<-@c^R?BgEf55lP67XUd7HYvXn96wO7N_4I7kXF3r$+6RWHguK7^^ z>hk)iSgW^RYY!StH`4E(-PU}zp#Q+hM=Ov1oU!DV%|U;LwM&IqL|I#`Lzl%&`=_W> zG&S$#>+OwBf@=O^{(lb|O>dg+VyDjd+j$PZ>feTIMVvmY@lkdiO)*P#nmrdNUt6{K z@X1Fj|2L+_<*2aESi*KPDws)cdBE+BY*L*qF-r@=%M~_rFnZ0k%H#jVY&_ja|K3`K z&+-0e8PWwmeQZowtH~JJa?@J!}I50?CMscvCdD)4GshNAOIzG*u}_O!eY za#WkooGz($(p))?ZMBzP4E!f+PtS6B3)jf}h$*8&D(=68#KR2}2{Z`t?yXJYUYgQYVTTsboF(3GU6pvQg| zYbJnM!tx%8UW$$@E0y^wnu3Je+clqZdimCEJelVaFpD#Wcc)?lUtQ7EW#+D#6Dp78 z&6S9JyEG{K{!XvUGZ&e7WwP(|x_sn|!xW)kF-tRj15aJ*Su|aZPxF}k(?xYb6Qefj zO^G}!_$_AXOW(jQOC?9cd6OSc*z{ncPssePDG8;XeG_drnC;c{iZt_Ew<`4|AJV z^!v>72`lRPc5&|u7&FAyT2|u8UJ*}6oELVW0O}; zS%u+|5U0fL_O2b*$UmSa>o4h|>e7d&dufyIG#0|Ka;_k-GU&XMN zS1tUd*bC|IHlOL!FN9_s`8%oJVFS~#G*-s2$upm5O^v-6@M5{eJ@#1f4fcBu2Z75K zqZu(vQzBk&&(L^z&W4TUt^YSC#vs*Gw{nW6dbJ7lt~+$@Nv9~st@&MT;Ns)Sb1mMa z*(*y#4@_8iE-P%=jBvSy95?p!|DSChG9^a+k(~Xtx&NMPf&F>Ec(!pv%+mii<}g0d zx;vFIMl`=fV(RKES0>6f`QLxpwd@4<`aVh1it6l!`O=zCOUg%)&%IUuC6jD|zDZ8% z3bQ<9ah^pnq*(1krkL~(QAha{rtqWrE}qN(e>W`uRWJXUciDHt!_`lXFaDkpd_c5! zx_F$dah_UL&Z}~NS&hgYregONFMU>)x{ZnH{=%gbHx|eC^FH8ny1ICBv-AhssI0v3 zXW9wxAAQVsJ9Z{HyLm@{Wl;h1U5lpo7lVp-8^tqz{IOwS@0|Ctui~xjgcu5fpEU|q z_fMO3d+JZF8`T&G`5H&XxMb9kvC5 z*CQ`(`ktYAf8CjIhs_79x0o2N+1e}j-+xK%^HXoY4oTAv0BHkvCAP0lRS9RYp1rHT GQUm}rR)J$(bVBvF>gMmRm{h$HE{ikM|a<^K(@8*%LOI_v^(`Z$A@JEqr z&=DRzro5cnt7g~Ei#QcwRe1aojZ5w+n7mR5sNy--e`WX za;^JwOy=wB=_<>TTrOxfDt~C?XJ+D1QWR2?P*N1i+w7q-$F0%h52Kny(RzaiqD*`d z4UAls4vkJ5*jNk{80@;)nKc6*I5MO%vatwk;Adu1*-uhQEp4Gf-5J!ZZCoyUE3^>wpf1iY@9HnroQbJA_MNlRv}eXD$R?X7+6 zvTy1aSWS0c6_fR6`LTMD8FJzG4bnxwN0pl{Jl_5`_GIMIHx<`oJ$KAfdp=e6`c#(d z76PBcJQL2n@&2kgXV;RgbtMez9PDHC4JUGLxpbQ4?Urj74rJ^VTKC|iW9RZm1#jkT zb4fZhHHE?X3FC+B$~)p7EeVeOR{LKu>yX#=5Zhag%8zbD9#l?FY?>;-ma*mXp9_L7 zrf?su;uN}Q_B1T9;{=nos>AW_6;m_vzchDGkFj)_B2jr#O#YNsZJ?-S;loMSvQ<|< z-n^jGh4V6>RfA~9+gtP2vod@YQnSDF^U9;@!}~LWXHVR2-@Wh0mssacXI%+FTYDDW zH{2V4F1~*1fSQo%3C#*sfz^?m+qCEPeHBvqJbOxh-h^ulP9=5Q{@S;n)%Lj1vjE{| zMV5AoZx66GzWrQaE3wgFBFB+{hmX^jW~hDinUJ!vdh7h+BNvq(w2K-CI88pSdPhg- zXxf|DmL3T)!P6-lVsB^uH+L`M^_=iw#wT8xv;ESXPEJkk$7U*?zOa$eNykUUYuZ_x zlKp+xlrMTu2oRoTx5WFqiAL;Yrp+8)6)An@3FmKL(Ys%;!c^$v35(@3uh#C4(=?o@ z!T8{1e3iVW4~x)V7f-S3b02Np^i`)$U~qi0a6-`X#_hL^lsHegth?e>-EuZYsmG#y zY0Q%8D)YV{?U1Tpe`)2r?|->`PNhD{Ed6Kq&0gov;_r3mw#xFQJTFyU_`Qm$nqCR$| zzggrTe)aH#Se`4V!e7{byt7Y7ciyQaovvF-#R2hqX5U_Oa;CIfihnQP(-XZRojv~2 zx`xN@yp^_|oZh-c^`K{~mdoeshJ{CW?A~?MXJTKYrdLazv~cZvsbcR#D;}L-@oN;H zD>=(YZ@Qp>~dZ*V8 zWX%K%?Vo{4UX@LOmpejwZaA;;tjzdya^}Mv*VT_=FPwO>;lUG+8Ty|65=sWf&a6{T zY3lJv9kFn&v704Qdi=wLK${~RMawQpU-hO-><*&^Y@MaxeL#{ zfBt1__r1HJ{qy#|dDOAI;=TX3H^n!$eQ)gl@@jR-d*<1PO_w~;{r0{*WaekF-}0f) z_8i@J>Q8OX|5wvKi^Xo|da8Ts^WH~i!so<9++=ZYUtR0r?sdNf#kFCZOt$TD zm7bWc+c!t{dG$M?ixwv|<+5D2P4{B3-?v`Yp}kZoQTf;&*WWXOGQPk6aXCZi%z8%i zb^LM}>wfx42p*ZeeTK2Am9Y%V$u5~G$7amq4*e$(+08n!bF*{8a`$N(mYqBJ3z#hX zUp_LF>*Zc#a{PhHqC%sA-=NXa8k+^0f}?G&fc7XFIbA@igyT^yG^yfeA%NzTY>+A{w3CnB9oq^#f9cK zN{3j@oF86tv9R}mt)&Fp$u6cDamO3ACUjhk>ds}p6Tl1la~Lkp1HJ1Ogr-Gx6iM<78J|6{VP%zSmEyyuPR$$UwyK3*}uta&rS;w zJH6dZ`}PYCvAnC9+m6g?@KhJR6Lo$I-e&5nj?PP@e@uSyeBZg3%fH@mdbe5N=W+f!7Zdm|%wLcc zT)XIs-GaF(1=+f9Cmr;O&cCUTfyZX6vx&1be1BGt*b^Y58X(xZI|d zpZpN6Y;^OJ-tzF90+UWoP+U}a^cYV!*RR_b*JPgDtha$(=%&M8Ru5H69gPc9CeA;k zczU-&iFdzN*lmXe|F+j25O{bkdB2X Date: Tue, 22 Oct 2024 11:12:45 +0200 Subject: [PATCH 31/47] RemoteLinux: Fix recursive dead-lock Change-Id: I490080d852e9c35f96557dd4b2335e608dfd4317 Reviewed-by: hjk --- src/plugins/remotelinux/linuxdevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index f41514ed6e1..7063d9bc27c 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -332,7 +332,7 @@ public: LinuxDevice *q = nullptr; QThread m_shellThread; ShellThreadHandler *m_handler = nullptr; - mutable QMutex m_shellMutex; + mutable QRecursiveMutex m_shellMutex; LinuxDeviceFileAccess m_fileAccess{this}; QReadWriteLock m_environmentCacheLock; From 093cd6035cade83dfc5281128b5b7250dd8f5259 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 16 Oct 2024 12:01:54 +0200 Subject: [PATCH 32/47] Axivion: Fix reloading dashboard Ensure cached data is gone when user forces a reload. Change-Id: I4c0e4a4f1905ab34cf07f1a47b7f7c32de1105db Reviewed-by: hjk Reviewed-by: Mohammad Mehdi Salem Naraghi --- src/plugins/axivion/axivionperspective.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/axivion/axivionperspective.cpp b/src/plugins/axivion/axivionperspective.cpp index e10fc96b31a..152fac26a45 100644 --- a/src/plugins/axivion/axivionperspective.cpp +++ b/src/plugins/axivion/axivionperspective.cpp @@ -890,6 +890,7 @@ void AxivionPerspective::initPerspective() reloadDataAct->setIcon(Utils::Icons::RELOAD_TOOLBAR.icon()); reloadDataAct->setToolTip(Tr::tr("Reload")); connect(reloadDataAct, &QAction::triggered, this, [this] { + switchActiveDashboardId(activeDashboardId()); // reset cached data reinitDashboardList({}); }); From f8a40eec025fefc99d6b8e502583a6b6abf25945 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 11 Oct 2024 09:50:02 +0200 Subject: [PATCH 33/47] Axivion: Correctly cleanup issues Beside improved visual reflection of the state this also avoids some crashes which may have been triggered when triggering actions on active controls which should have been disabled at that point. Change-Id: I1912ae3b98ac2ad6068f67f700a97afb5a38c224 Reviewed-by: hjk Reviewed-by: Mohammad Mehdi Salem Naraghi --- src/plugins/axivion/axivionperspective.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionperspective.cpp b/src/plugins/axivion/axivionperspective.cpp index 152fac26a45..335657304a7 100644 --- a/src/plugins/axivion/axivionperspective.cpp +++ b/src/plugins/axivion/axivionperspective.cpp @@ -454,11 +454,15 @@ void IssuesWidget::reinitProjectList(const QString ¤tProject) if (!info) return; GuardLocker lock(m_signalBlocker); - m_dashboardProjects->clear(); m_dashboardProjects->addItems(info->projects); if (!currentProject.isEmpty() && info->projects.contains(currentProject)) m_dashboardProjects->setCurrentText(currentProject); }; + { + GuardLocker lock(m_signalBlocker); + m_dashboardProjects->clear(); + } + updateBasicProjectInfo(std::nullopt); fetchDashboardAndProjectInfo(onDashboardInfoFetched, currentProject); } @@ -669,7 +673,11 @@ void IssuesWidget::updateBasicProjectInfo(const std::optionalsetText("0"); m_removedFilter->setText("0"); + setFiltersEnabled(false); m_issuesModel->clear(); + m_issuesModel->setHeader({}); + if (m_overlay) + m_overlay->hide(); return; } From 8545b619c8acb94dd8480eefc3ef225749c5cb6b Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 11 Oct 2024 12:47:10 +0200 Subject: [PATCH 34/47] Axivion: Show progress indicator earlier Especially for longer network communication there was no hint what is going on until e.g. some timeout error appeared on the General Messages. Start the progress indicator as soon the network communication starts. Fixes: QTCREATORBUG-31781 Change-Id: I5869f56fe998d25c52d3a0f8b81068600d521a48 Reviewed-by: Mohammad Mehdi Salem Naraghi Reviewed-by: hjk --- src/plugins/axivion/axivionperspective.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionperspective.cpp b/src/plugins/axivion/axivionperspective.cpp index 335657304a7..b58c4cce684 100644 --- a/src/plugins/axivion/axivionperspective.cpp +++ b/src/plugins/axivion/axivionperspective.cpp @@ -254,6 +254,7 @@ IssuesWidget::IssuesWidget(QWidget *parent) m_dashboardProjects->clear(); } updateBasicProjectInfo(std::nullopt); + m_issuesView->hideProgressIndicator(); } }); @@ -451,8 +452,10 @@ void IssuesWidget::reinitProjectList(const QString ¤tProject) { const auto onDashboardInfoFetched = [this, currentProject] (const expected_str &info) { - if (!info) + if (!info) { + m_issuesView->hideProgressIndicator(); return; + } GuardLocker lock(m_signalBlocker); m_dashboardProjects->addItems(info->projects); if (!currentProject.isEmpty() && info->projects.contains(currentProject)) @@ -463,6 +466,9 @@ void IssuesWidget::reinitProjectList(const QString ¤tProject) m_dashboardProjects->clear(); } updateBasicProjectInfo(std::nullopt); + if (m_overlay) + m_overlay->hide(); + m_issuesView->showProgressIndicator(); fetchDashboardAndProjectInfo(onDashboardInfoFetched, currentProject); } From 755b960e76691cb28166c910ee7d9a2b7cc2cb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Mon, 21 Oct 2024 15:47:27 +0200 Subject: [PATCH 35/47] SquishTests: Remove disappeared lines from expected tree for qbs Change-Id: I625bf6ff6d750515d932fd018484ac012cb4355d Reviewed-by: Christian Stenger --- .../testdata/projecttree_creator.tsv | 182 ------------------ 1 file changed, 182 deletions(-) diff --git a/tests/system/suite_general/tst_opencreator_qbs/testdata/projecttree_creator.tsv b/tests/system/suite_general/tst_opencreator_qbs/testdata/projecttree_creator.tsv index 2a1ec41794f..965ad144c73 100644 --- a/tests/system/suite_general/tst_opencreator_qbs/testdata/projecttree_creator.tsv +++ b/tests/system/suite_general/tst_opencreator_qbs/testdata/projecttree_creator.tsv @@ -7678,8 +7678,6 @@ "mesonprojectmanager.qbs:3" "3" "mesoninfoparser" "3" "mesonprojectmanager.qbs:113" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -7699,8 +7697,6 @@ "toolwrapper.h" "4" "mesonparser" "3" "mesonprojectmanager.qbs:151" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -7801,8 +7797,6 @@ "versionhelper.h" "4" "mesonwrapper" "3" "mesonprojectmanager.qbs:93" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -7821,8 +7815,6 @@ "toolwrapper.h" "4" "ninjaparser" "3" "mesonprojectmanager.qbs:134" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -11729,8 +11721,6 @@ "proparser.qbs:3" "2" "qtcjson" "1" "json.qbs:3" "2" -"Group 1" "2" -"Library.qbs:81" "3" "json.cpp" "2" "json.h" "2" "qtcreator" "1" @@ -12867,8 +12857,6 @@ "auto.qbs:3" "2" "Aggregation autotest" "2" "aggregation.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -12880,8 +12868,6 @@ "tst_aggregate.cpp" "3" "Algorithm autotest" "2" "algorithm.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -12893,8 +12879,6 @@ "tst_algorithm.cpp" "3" "Android AVD Manager autotest" "2" "android.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "Resource files" "3" "android.qbs:25" "4" "Test.avd" "4" @@ -12920,8 +12904,6 @@ "tst_avdmanageroutputparser.cpp" "4" "ChangeSet autotest" "2" "changeset.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -12938,8 +12920,6 @@ "Data Files" "4" "c99.qbs:11" "5" "designatedInitializer.1.c" "5" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Source Files" "4" "c99.qbs:6" "5" "tst_c99.cpp" "5" @@ -12953,8 +12933,6 @@ "qtcreator_pch.h" "6" "CPlusPlus AST autotest" "3" "ast.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -12966,8 +12944,6 @@ "tst_ast.cpp" "4" "CPlusPlus check symbols autotest" "3" "checksymbols.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -12981,8 +12957,6 @@ "tst_checksymbols.cpp" "4" "CPlusPlus code formatter autotest" "3" "codeformatter.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -12994,8 +12968,6 @@ "tst_codeformatter.cpp" "4" "CPlusPlus fileiterationorder autotest" "3" "fileiterationorder.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13007,8 +12979,6 @@ "tst_fileiterationorder.cpp" "4" "CPlusPlus find usages autotest" "3" "findusages.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13022,8 +12992,6 @@ "tst_findusages.cpp" "4" "CPlusPlus lexer autotest" "3" "lexer.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13037,8 +13005,6 @@ "tst_lexer.cpp" "4" "CPlusPlus lookup autotest" "3" "lookup.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13050,8 +13016,6 @@ "tst_lookup.cpp" "4" "CPlusPlus miscellaneous autotest" "3" "misc.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13092,8 +13056,6 @@ "recursive.1.out.cpp" "5" "reserved.1.cpp" "5" "reserved.1.out.cpp" "5" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Source Files" "4" "preprocessor.qbs:6" "5" "tst_preprocessor.cpp" "5" @@ -13107,8 +13069,6 @@ "qtcreator_pch.h" "6" "CPlusPlus pretty printer autotest" "3" "typeprettyprinter.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13123,8 +13083,6 @@ "Data Files" "4" "cppselectionchanger.qbs:12" "5" "testCppFile.cpp" "5" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Source Files" "4" "cppselectionchanger.qbs:7" "5" "tst_cppselectionchangertest.cpp" "5" @@ -13138,8 +13096,6 @@ "qtcreator_pch.h" "6" "CPlusPlus semantic autotest" "3" "semantic.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13151,8 +13107,6 @@ "tst_semantic.cpp" "4" "CPlusPlus translation unit autotest" "3" "translationunit.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13174,8 +13128,6 @@ "noExcept.1.errors.txt" "5" "staticAssert.1.cpp" "5" "staticAssert.1.errors.txt" "5" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Source Files" "4" "cxx11.qbs:6" "5" "tst_cxx11.cpp" "5" @@ -13189,8 +13141,6 @@ "qtcreator_pch.h" "6" "declaration comments autotest" "3" "declarationcomments.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13204,8 +13154,6 @@ "debugger.qbs:3" "3" "Debugger dumpers autotest" "3" "dumpers.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Sources from Debugger plugin" "4" "dumpers.qbs:8" "5" "debuggerprotocol.cpp" "5" @@ -13230,8 +13178,6 @@ "tst_dumpers.cpp" "5" "debugger protocol autotest" "3" "protocol.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Sources from Debugger plugin" "4" "protocol.qbs:7" "5" "debuggerprotocol.cpp" "5" @@ -13249,8 +13195,6 @@ "tst_protocol.cpp" "5" "disassembler autotest" "3" "disassembler.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Sources from Debugger plugin" "4" "disassembler.qbs:5" "5" "disassemblerlines.cpp" "5" @@ -13267,8 +13211,6 @@ "tst_disassembler.cpp" "5" "gdb autotest" "3" "gdb.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Sources from Debugger plugin" "4" "gdb.qbs:7" "5" "debuggerprotocol.cpp" "5" @@ -13286,8 +13228,6 @@ "tst_gdb.cpp" "5" "offsets autotest" "3" "offsets.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13301,8 +13241,6 @@ "tst_offsets.cpp" "5" "simplifytypes autotest" "3" "simplifytypes.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Sources from Debugger plugin" "4" "simplifytypes.qbs:6" "5" "simplifytype.cpp" "5" @@ -13321,8 +13259,6 @@ "diff.qbs:3" "3" "Differ autotest" "3" "differ.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13334,8 +13270,6 @@ "tst_differ.cpp" "4" "Environment autotest" "2" "environment.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13347,8 +13281,6 @@ "tst_environment.cpp" "3" "Examples autotest" "2" "examples.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13364,8 +13296,6 @@ "pluginspec.qbs:3" "4" "ExtensionSystem pluginspec autotest" "4" "test.qbs:3" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "Sources" "5" "test.qbs:7" "6" "tst_pluginspec.cpp" "6" @@ -13435,8 +13365,6 @@ "plugin3.h" "6" "PluginManager autotest" "4" "test.qbs:3" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -13448,8 +13376,6 @@ "tst_pluginmanager.cpp" "5" "ExternalTool autotest" "2" "externaltool.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13463,8 +13389,6 @@ "tst_externaltooltest.cpp" "4" "File search autotest" "2" "filesearch.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13480,8 +13404,6 @@ "testfile.txt" "5" "json autotest" "2" "json.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13500,8 +13422,6 @@ "tst_json.cpp" "3" "Language Server Protocol autotest" "2" "languageserverprotocol.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13513,8 +13433,6 @@ "tst_languageserverprotocol.cpp" "3" "ProFileWriter autotest" "2" "profilewriter.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "Sources from ProParser" "3" "profilewriter.qbs:7" "4" "ioutils.cpp" "4" @@ -13553,8 +13471,6 @@ "codemodel.qbs:3" "4" "QML code model check autotest" "4" "check.qbs:3" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -13566,8 +13482,6 @@ "tst_check.cpp" "5" "QML code model dependencies autotest" "4" "dependencies.qbs:3" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -13579,8 +13493,6 @@ "tst_dependencies.cpp" "5" "QML code model imports check autotest" "4" "importscheck.qbs:3" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -13594,8 +13506,6 @@ "qmleditor.qbs:3" "4" "QML code formatter autotest" "4" "qmlcodeformatter.qbs:3" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -13607,8 +13517,6 @@ "tst_qmlcodeformatter.cpp" "5" "QML persistenttrie autotest" "3" "persistenttrie.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13621,8 +13529,6 @@ "tst_testtrie.h" "4" "QML reformatter autotest" "3" "reformatter.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13634,8 +13540,6 @@ "tst_reformatter.cpp" "4" "QMLJS simple reader autotest" "3" "qmljssimplereader.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13647,8 +13551,6 @@ "tst_qmljssimplereader.cpp" "4" "qrc parser autotest" "3" "qrcparser.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13660,8 +13562,6 @@ "tst_qrcparser.cpp" "4" "sdktool autotest" "2" "sdktool.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13677,8 +13577,6 @@ "solutions.qbs:3" "3" "Tasking autotest" "3" "tasking.qbs:1" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13692,8 +13590,6 @@ "texteditor.qbs:3" "3" "Highlighter autotest" "3" "highlighter.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Source Files" "4" "highlighter.qbs:11" "5" "tst_highlighter.cpp" "5" @@ -13707,8 +13603,6 @@ "qtcreator_pch.h" "6" "ToolChainCache autotest" "2" "toolchaincache.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13724,8 +13618,6 @@ "tracing.qbs:3" "3" "FlameGraph autotest" "3" "flamegraph.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13739,8 +13631,6 @@ "tst_flamegraph.cpp" "5" "FlameGraphView autotest" "3" "flamegraphview.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "Qml Files" "4" "flamegraphview.qbs:18" "5" "TestFlameGraphView.qml" "5" @@ -13758,8 +13648,6 @@ "tst_flamegraphview.cpp" "5" "TimelineAbstractRenderer autotest" "3" "timelineabstractrenderer.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13773,8 +13661,6 @@ "tst_timelineabstractrenderer.cpp" "5" "TimelineItemsRenderPass autotest" "3" "timelineitemsrenderpass.qbs:5" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13788,8 +13674,6 @@ "tst_timelineitemsrenderpass.cpp" "5" "TimelineModel autotest" "3" "timelinemodel.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13803,8 +13687,6 @@ "tst_timelinemodel.cpp" "5" "TimelineModelAggregator autotest" "3" "timelinemodelaggregator.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13818,8 +13700,6 @@ "tst_timelinemodelaggregator.cpp" "5" "TimelineNotesModel autotest" "3" "timelinenotesmodel.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13833,8 +13713,6 @@ "tst_timelinenotesmodel.cpp" "5" "TimelineNotesRenderPass autotest" "3" "timelinenotesrenderpass.qbs:5" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13848,8 +13726,6 @@ "tst_timelinenotesrenderpass.cpp" "5" "TimelineOverviewRenderer autotest" "3" "timelineoverviewrenderer.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13863,8 +13739,6 @@ "tst_timelineoverviewrenderer.cpp" "5" "TimelineRenderer autotest" "3" "timelinerenderer.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13878,8 +13752,6 @@ "tst_timelinerenderer.cpp" "5" "TimelineRenderPass autotest" "3" "timelinerenderpass.qbs:5" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13893,8 +13765,6 @@ "tst_timelinerenderpass.cpp" "5" "TimelineRenderState autotest" "3" "timelinerenderstate.qbs:5" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13908,8 +13778,6 @@ "tst_timelinerenderstate.cpp" "5" "TimelineSelectionRenderPass autotest" "3" "timelineselectionrenderpass.qbs:5" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13923,8 +13791,6 @@ "tst_timelineselectionrenderpass.cpp" "5" "TimelineZoomcontrol autotest" "3" "timelinezoomcontrol.qbs:4" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13938,8 +13804,6 @@ "tst_timelinezoomcontrol.cpp" "5" "TreeViewFind autotest" "2" "treeviewfind.qbs:3" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13951,8 +13815,6 @@ "tst_treeviewfind.cpp" "3" "UpdateInfo autotest" "2" "updateinfo.qbs:1" "3" -"Group 1" "3" -"QtcProduct.qbs:68" "4" "standard pch file (gui)" "3" "QtcProduct.qbs:83" "4" "." "4" @@ -13966,8 +13828,6 @@ "utils.qbs:3" "3" "ANSI autotest" "3" "ansiescapecodehandler.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13979,8 +13839,6 @@ "tst_ansiescapecodehandler.cpp" "4" "Async autotest" "3" "async.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -13994,8 +13852,6 @@ "commandline.qbs:1" "4" "CommandLine autotest" "4" "commandline.qbs:2" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -14009,8 +13865,6 @@ "deviceshell.qbs:1" "4" "DeviceShell autotest" "4" "deviceshell.qbs:2" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -14022,8 +13876,6 @@ "tst_deviceshell.cpp" "5" "Expected autotest" "3" "expected.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14035,8 +13887,6 @@ "tst_expected.cpp" "4" "FilePath autotest" "3" "filepath.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14048,8 +13898,6 @@ "tst_filepath.cpp" "4" "FileUtils autotest" "3" "fileutils.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14061,8 +13909,6 @@ "tst_fileutils.cpp" "4" "FSEngine autotest" "3" "fsengine.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14074,8 +13920,6 @@ "tst_fsengine.cpp" "4" "FuzzyMatcher autotest" "3" "fuzzymatcher.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14087,8 +13931,6 @@ "tst_fuzzymatcher.cpp" "4" "IndexedContainerProxyConstIterator autotest" "3" "indexedcontainerproxyconstiterator.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14100,8 +13942,6 @@ "tst_indexedcontainerproxyconstiterator.cpp" "4" "MathUtils autotest" "3" "mathutils.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14113,8 +13953,6 @@ "tst_mathutils.cpp" "4" "MultiTextCursor autotest" "3" "multicursor.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14126,8 +13964,6 @@ "tst_multicursor.cpp" "4" "PersistentSettings autotest" "3" "persistentsettings.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14141,8 +13977,6 @@ "process.qbs:3" "4" "Process autotest" "4" "process.qbs:4" "5" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "standard pch file (gui)" "5" "QtcProduct.qbs:83" "6" "." "6" @@ -14162,8 +13996,6 @@ "processtestapp.h" "5" "Settings autotest" "3" "settings.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14175,8 +14007,6 @@ "tst_settings.cpp" "4" "StringUtils autotest" "3" "stringutils.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14188,8 +14018,6 @@ "tst_stringutils.cpp" "4" "TemplateEngine autotest" "3" "templateengine.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14201,8 +14029,6 @@ "tst_templateengine.cpp" "4" "Text autotest" "3" "text.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14214,8 +14040,6 @@ "tst_text.cpp" "4" "TreeModel autotest" "3" "treemodel.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14227,8 +14051,6 @@ "tst_treemodel.cpp" "4" "UnixDeviceFileAccess autotest" "3" "unixdevicefileaccess.qbs:3" "4" -"Group 1" "4" -"QtcProduct.qbs:68" "5" "standard pch file (gui)" "4" "QtcProduct.qbs:83" "5" "." "5" @@ -14269,8 +14091,6 @@ "callgrindproxymodel.h" "6" "callgrindstackbrowser.cpp" "6" "callgrindstackbrowser.h" "6" -"Group 1" "5" -"QtcProduct.qbs:68" "6" "Memcheck runner files from plugin" "5" "valgrindautotest.qbs:20" "6" "Other files from plugin" "5" @@ -14430,8 +14250,6 @@ "plugin3.h" "4" "Manual ProParser test" "2" "testreader.qbs:1" "3" -"Porting Helper" "3" -"testreader.qbs:51" "4" "ProParser files" "3" "testreader.qbs:23" "4" "." "4" From 929a574c226d10b9e88e4b7b38e42210360dff71 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 18 Oct 2024 16:46:26 +0200 Subject: [PATCH 36/47] Doc: Describe specifying build and run settings for workspaces Task-number: QTCREATORBUG-31510 Change-Id: I378674f438c8b16a2c7715f4ca773ada281c64ae Reviewed-by: David Schulz --- dist/changelog/changes-15.0.0.md | 6 +++-- .../external-resources.qdoc | 4 +++ .../creator-projects-opening.qdoc | 25 ++++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/dist/changelog/changes-15.0.0.md b/dist/changelog/changes-15.0.0.md index c4cf156e50e..fdd89a79d9b 100644 --- a/dist/changelog/changes-15.0.0.md +++ b/dist/changelog/changes-15.0.0.md @@ -181,8 +181,10 @@ Projects ### Workspace -* Added the option to add build configurations -* Added automatic updating of the project tree +* Added the option to add build configurations into + `Projects > Build & Run > Build > Add` + ([Documentation](https://doc.qt.io/qtcreator/creator-project-opening.html)) +* Added automatic updating of the project tree in `Projects` * Fixed that cloned run configurations were not editable ### vcpkg diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index d7ab44bbe67..51c786a2d53 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -237,3 +237,7 @@ \externalpage https://www.openssh.com/ \title OpenSSH */ +/*! + \externalpage https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/share/qtcreator/jsonschemas/project.json + \title project.json +*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc index b71064f96e0..d7533aea787 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc @@ -56,8 +56,26 @@ \uicontrol {Open Workspace}. \QC generates the \e .qtcreator/project.json project file in the directory - for setting a project name and file exclusion filters. You can open either - the JSON file or the workspace to open the project the next time. + for setting a project name and file exclusion filters. You can add build and + run settings to the file, as defined by its \l{project.json}{JSON schema}. + + You can open either the JSON file or the workspace to open the project the + next time. + + When you add files to the directory or remove them from there, the contents + of the \l Projects view are updated automatically. + + To add a build configuration to the workspace: + + \list 1 + \li Go to \uicontrol Projects > \uicontrol {Build & Run} > + \uicontrol Build. + \li Select \uicontrol Add > \uicontrol Build. + \li Specify build settings. + \endlist + + To specify run settings for the workspace, go to \uicontrol Projects > + \uicontrol {Build & Run} > \uicontrol Run. \section1 Re-configure projects @@ -123,5 +141,6 @@ later. Select the \inlineimage icons/pin.png (\uicontrol Pin) button to pin the progress bar back to the toggle button. - \sa {Manage Kits}{How To: Manage Kits}, {Kits}, + \sa {Configure projects for building}, {Configure projects for running}, + {Manage Kits}{How To: Manage Kits}, {Kits} */ From 49e268d64909c0a29bc137029cf07d2d4dc1ac22 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 22 Oct 2024 12:41:37 +0200 Subject: [PATCH 37/47] QbsProjectManager: Use qbs.toolchainType in profiles This has been the property to set for a long time, so let's finaly do it here as well. Change-Id: I9b1c1d7d5004d54ccb50d42b2fb6ea43128f89bf Reviewed-by: Christian Stenger --- .../defaultpropertyprovider.cpp | 28 +++++++++---------- .../qbsprojectmanagerconstants.h | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp index 402c97bc8b2..be6466b3199 100644 --- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp +++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp @@ -102,30 +102,30 @@ static QString targetPlatform(const ProjectExplorer::Abi &abi, const ProjectExpl return QString(); } -static QStringList toolchainList(const ProjectExplorer::Toolchain *tc) +static QString toolchainType(const ProjectExplorer::Toolchain *tc) { const Utils::Id type = tc->typeId(); if (type == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID || (type == Android::Constants::ANDROID_TOOLCHAIN_TYPEID && tc->compilerCommand().fileName().contains("clang"))) { - return {"clang", "llvm", "gcc"}; + return "clang"; } if (type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID || type == Android::Constants::ANDROID_TOOLCHAIN_TYPEID) { - return {"gcc"}; // TODO: Detect llvm-gcc + return "gcc"; // TODO: Detect llvm-gcc } if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) - return {"mingw", "gcc"}; + return "mingw"; if (type == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) - return {"clang-cl", "msvc"}; + return "clang-cl"; if (type == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) - return {"msvc"}; + return "msvc"; if (type == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID) - return {"iar"}; + return "iar"; if (type == BareMetal::Constants::KEIL_TOOLCHAIN_TYPEID) - return {"keil"}; + return "keil"; if (type == BareMetal::Constants::SDCC_TOOLCHAIN_TYPEID) - return {"sdcc"}; + return "sdcc"; return {}; } @@ -258,7 +258,7 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor } data.insert(QLatin1String(QBS_TARGETPLATFORM), targetPlatform(targetAbi, k)); - QStringList toolchain = toolchainList(mainTc); + QString toolchain = toolchainType(mainTc); if (targetAbi.osFlavor() == Abi::AndroidLinuxFlavor) { const IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (dev) { @@ -313,12 +313,12 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor if (!mainToolchainPrefix.isEmpty()) data.insert(QLatin1String(CPP_TOOLCHAINPREFIX), mainToolchainPrefix); - if (toolchain.contains(QLatin1String("clang-cl"))) { + if (toolchain == "clang-cl") { data.insert(QLatin1String(CPP_COMPILERNAME), mainCompilerName); const auto clangClToolchain = static_cast(mainTc); data.insert(QLatin1String(CPP_VCVARSALLPATH), clangClToolchain->varsBat()); - } else if (toolchain.contains(QLatin1String("msvc"))) { + } else if (toolchain == "msvc") { data.insert(QLatin1String(CPP_COMPILERNAME), mainCompilerName); } else { if (!mainCompilerName.isEmpty()) @@ -353,7 +353,7 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor if (compilerReMatch.hasMatch()) { const QString developerPath = compilerReMatch.captured(QStringLiteral("developerpath")); data.insert(QLatin1String(XCODE_DEVELOPERPATH), developerPath); - toolchain.insert(0, QStringLiteral("xcode")); + toolchain = "xcode"; // If the sysroot is part of this developer path, set the canonical SDK name const QDir sysrootdir(QDir::cleanPath(sysroot)); @@ -382,7 +382,7 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor } if (!toolchain.isEmpty()) - data.insert(QLatin1String(QBS_TOOLCHAIN), toolchain); + data.insert("qbs.toolchainType", toolchain); return data; } diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h b/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h index 6780455a730..c03ef27356a 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h @@ -54,7 +54,6 @@ const char QBS_TARGETPLATFORM[] = "qbs.targetPlatform"; const char QBS_SYSROOT[] = "qbs.sysroot"; const char QBS_ARCHITECTURES[] = "qbs.architectures"; const char QBS_ARCHITECTURE[] = "qbs.architecture"; -const char QBS_TOOLCHAIN[] = "qbs.toolchain"; const char CPP_TOOLCHAINPATH[] = "cpp.toolchainInstallPath"; const char CPP_TOOLCHAINPREFIX[] = "cpp.toolchainPrefix"; const char CPP_COMPILERNAME[] = "cpp.compilerName"; From ca38a7a2d3d9dc04136036fa0d8dfc7651101bf4 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 18 Oct 2024 12:51:15 +0200 Subject: [PATCH 38/47] ProjectExplorer: Simplify WorkingDirectoryAspect expandProcessArgs has some very specific rules that are not necessary for the build directory, so we use expand(FilePath) instead. Since Environment::expandVariables(FilePath) cleans the path we do not have to call QDir::cleanPath ourselves. Change-Id: Ia08a4abafa00c33ebbc283523051e987c58fa983 Reviewed-by: hjk Reviewed-by: Christian Kandeler --- .../projectexplorer/runconfigurationaspects.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 74d9d2b7b0f..fa6d86a44b9 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -248,13 +248,10 @@ FilePath WorkingDirectoryAspect::workingDirectory() const { const Environment env = m_envAspect ? m_envAspect->environment() : Environment::systemEnvironment(); - QString workingDir = m_workingDirectory.path(); - if (auto expander = macroExpander()) - workingDir = expander->expandProcessArgs(workingDir); - - QString res = workingDir.isEmpty() ? QString() : QDir::cleanPath(env.expandVariables(workingDir)); - - return m_workingDirectory.withNewPath(res); + const FilePath workingDir = macroExpander()->expand(m_workingDirectory); + if (m_envAspect) + return env.expandVariables(workingDir); + return workingDir; } FilePath WorkingDirectoryAspect::defaultWorkingDirectory() const From d0707e17b30e6011adf17f4e9b40d9a29b381694 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 21 Oct 2024 10:27:01 +0200 Subject: [PATCH 39/47] MacroExpander: Return error from expandArgs and handle them Change-Id: Ie4eea11d6e85ab45bc6abf7d255aa204b9c73c92 Reviewed-by: hjk --- src/libs/utils/macroexpander.cpp | 10 ++++++---- src/libs/utils/macroexpander.h | 2 +- .../coreplugin/dialogs/externaltoolconfig.cpp | 7 ++++++- src/plugins/coreplugin/externaltool.cpp | 5 ++++- .../runconfigurationaspects.cpp | 8 +++++--- .../qmakebuildconfiguration.cpp | 20 +++++++++++++++++-- .../qmakebuildconfiguration.h | 8 +++++++- 7 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 63360c93f09..fdaf4c7aebc 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -19,8 +19,6 @@ namespace Utils { namespace Internal { -static Q_LOGGING_CATEGORY(expanderLog, "qtc.utils.macroexpander", QtWarningMsg) - const char kFilePathPostfix[] = ":FilePath"; const char kPathPostfix[] = ":Path"; const char kNativeFilePathPostfix[] = ":NativeFilePath"; @@ -413,7 +411,8 @@ QVariant MacroExpander::expandVariant(const QVariant &v) const return v; } -QString MacroExpander::expandProcessArgs(const QString &argsWithVariables, Utils::OsType osType) const +expected_str MacroExpander::expandProcessArgs( + const QString &argsWithVariables, Utils::OsType osType) const { QString result = argsWithVariables; const bool ok = ProcessArgs::expandMacros( @@ -421,7 +420,10 @@ QString MacroExpander::expandProcessArgs(const QString &argsWithVariables, Utils [this](const QString &str, int *pos, QString *ret) { return d->findMacro(str, pos, ret); }, osType); - QTC_ASSERT(ok, qCDebug(expanderLog) << "Expanding failed: " << argsWithVariables); + if (!ok) { + return make_unexpected( + Tr::tr("Failed to expand macros in process arguments: %1").arg(argsWithVariables)); + } return result; } diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h index 5608b39e7a5..d080a9942ee 100644 --- a/src/libs/utils/macroexpander.h +++ b/src/libs/utils/macroexpander.h @@ -37,7 +37,7 @@ public: QByteArray expand(const QByteArray &stringWithVariables) const; QVariant expandVariant(const QVariant &v) const; - QString expandProcessArgs( + expected_str expandProcessArgs( const QString &argsWithVariables, Utils::OsType osType = Utils::HostOsInfo::hostOs()) const; using PrefixFunction = std::function; diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp index 0adf830204f..95d37354da6 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp @@ -923,7 +923,12 @@ void ExternalToolConfig::addCategory() void ExternalToolConfig::updateEffectiveArguments() { - m_arguments->setToolTip(Utils::globalMacroExpander()->expandProcessArgs(m_arguments->text())); + const expected_str result = Utils::globalMacroExpander()->expandProcessArgs( + m_arguments->text()); + if (result) + m_arguments->setToolTip(*result); + else + m_arguments->setToolTip(result.error()); } void ExternalToolConfig::editEnvironmentChanges() diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index 87cc4d043fa..80bde5b67fc 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -598,7 +598,10 @@ bool ExternalToolRunner::resolve() } } - m_resolvedArguments = expander->expandProcessArgs(m_tool->arguments()); + const expected_str args = expander->expandProcessArgs(m_tool->arguments()); + QTC_ASSERT_EXPECTED(args, return false); + + m_resolvedArguments = *args; m_resolvedInput = expander->expand(m_tool->input()); m_resolvedWorkingDirectory = expander->expand(m_tool->workingDirectory()); diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index fa6d86a44b9..b7e5886cfb5 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -328,9 +328,11 @@ QString ArgumentsAspect::arguments() const return m_arguments; m_currentlyExpanding = true; - const QString expanded = macroExpander()->expandProcessArgs(m_arguments); + const expected_str expanded = macroExpander()->expandProcessArgs(m_arguments); + QTC_ASSERT_EXPECTED(expanded, return m_arguments); + m_currentlyExpanding = false; - return expanded; + return *expanded; } /*! @@ -962,7 +964,7 @@ X11ForwardingAspect::X11ForwardingAspect(AspectContainer *container) QString X11ForwardingAspect::display() const { - return !isChecked() ? QString() : macroExpander()->expandProcessArgs(value()); + return !isChecked() ? QString() : macroExpander()->expand(value()); } diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index 4a5ce922a44..5d5d1caa455 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -241,6 +241,7 @@ void QmakeBuildConfiguration::updateProblemLabel() bool targetMismatch = false; bool incompatibleBuild = false; bool allGood = false; + bool invalidArguments = false; // we only show if we actually have a qmake and makestep QString errorString; if (qmakeStep() && makeStep()) { @@ -258,6 +259,9 @@ void QmakeBuildConfiguration::updateProblemLabel() case QmakeBuildConfiguration::MakefileForWrongProject: targetMismatch = true; break; + case QmakeBuildConfiguration::InvalidArguments: + invalidArguments = true; + break; } } @@ -305,6 +309,10 @@ void QmakeBuildConfiguration::updateProblemLabel() } else if (unalignedBuildDir) { buildDirectoryAspect()->setProblem(unalignedBuildDirWarning()); return; + } else if (invalidArguments) { + buildDirectoryAspect()->setProblem( + Tr::tr("Starting qmake failed with the following error: %1").arg(errorString)); + return; } buildDirectoryAspect()->setProblem({}); @@ -516,8 +524,16 @@ QmakeBuildConfiguration::MakefileState QmakeBuildConfiguration::compareToImportF // and compare that on its own FilePath workingDirectory = makefile.parentDir(); QStringList actualArgs; - QString allArgs = macroExpander()->expandProcessArgs(qs->allArguments( - QtKitAspect::qtVersion(target()->kit()), QMakeStep::ArgumentFlag::Expand)); + expected_str expandResult = macroExpander()->expandProcessArgs( + qs->allArguments(QtKitAspect::qtVersion(target()->kit()), QMakeStep::ArgumentFlag::Expand)); + + if (!expandResult) { + if (errorString) + *errorString = expandResult.error(); + return InvalidArguments; + } + + QString allArgs = *expandResult; // This copies the settings from allArgs to actualArgs (minus some we // are not interested in), splitting them up into individual strings: extractSpecFromArguments(&allArgs, workingDirectory, version, &actualArgs); diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h index f83c8228ce0..0715311822d 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h @@ -61,7 +61,13 @@ public: Utils::FilePath makefile() const; - enum MakefileState { MakefileMatches, MakefileForWrongProject, MakefileIncompatible, MakefileMissing }; + enum MakefileState { + MakefileMatches, + MakefileForWrongProject, + MakefileIncompatible, + MakefileMissing, + InvalidArguments + }; MakefileState compareToImportFrom(const Utils::FilePath &makefile, QString *errorString = nullptr); static QString extractSpecFromArguments( QString *arguments, const Utils::FilePath &directory, const QtSupport::QtVersion *version, From 458d1292b032c1a7474df911649dcacbcf6dd2b4 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 22 Oct 2024 11:12:31 +0200 Subject: [PATCH 40/47] Docker: Fix Toolchain registration Toolchains were not registered, as ToolchainBundle(...., AutoRegister) only registers missing parts of a bundle. Task-number: QTCREATORBUG-31877 Change-Id: I310b469425ed94274ba12ff5d396b44c9b2eee87 Reviewed-by: Christian Kandeler --- src/plugins/docker/kitdetector.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/docker/kitdetector.cpp b/src/plugins/docker/kitdetector.cpp index 547ba943315..51856408f87 100644 --- a/src/plugins/docker/kitdetector.cpp +++ b/src/plugins/docker/kitdetector.cpp @@ -264,6 +264,7 @@ Toolchains KitDetectorPrivate::autoDetectToolchains() .arg(toolchain->compilerCommand().toUserOutput())); toolchain->setDetectionSource(m_sharedId); } + ToolchainManager::registerToolchains(newToolchains); const QList bundles = ToolchainBundle::collectBundles(newToolchains, ToolchainBundle::AutoRegister::On); alreadyKnown.append(newToolchains); From 7a56d615dff6ae629599093f41b7610880949a8c Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 18 Oct 2024 10:59:14 +0200 Subject: [PATCH 41/47] UI Tour: Fix clicking on the User Interface link at the end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amends df54a15651567e534bdb56c84e357eb2dc81d88b When fixing QTCREATORBUG-31447 this broke in turn. We may not filter out click events to the text. Fixes: QTCREATORBUG-31850 Change-Id: I0afd0db64c28d111109c80a8644bb4d94535ef6f Reviewed-by: Christian Stenger Reviewed-by: Robert Löhning --- src/plugins/welcome/introductionwidget.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp index 4da7e937b33..1869947503f 100644 --- a/src/plugins/welcome/introductionwidget.cpp +++ b/src/plugins/welcome/introductionwidget.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -86,8 +87,12 @@ IntroductionWidget::IntroductionWidget() m_stepText->setTextFormat(Qt::RichText); // why is palette not inherited??? m_stepText->setPalette(palette()); - m_stepText->setOpenExternalLinks(true); - m_stepText->installEventFilter(this); + connect(m_stepText, &QLabel::linkActivated, this, [this](const QString &link) { + // clicking the User Interface link should both open the documentation page + // and step forward (=end the tour) + step(); + QDesktopServices::openUrl(link); + }); layout->addWidget(m_stepText); m_continueLabel = new QLabel(this); @@ -173,12 +178,8 @@ bool IntroductionWidget::event(QEvent *e) bool IntroductionWidget::eventFilter(QObject *obj, QEvent *ev) { - if (obj == parent() && ev->type() == QEvent::Resize) { + if (obj == parent() && ev->type() == QEvent::Resize) resizeToParent(); - } else if (obj == m_stepText && ev->type() == QEvent::MouseButtonRelease) { - step(); - return true; - } return QWidget::eventFilter(obj, ev); } From a044dd4a08e5bc5a15a5255a08aa30d38b646a67 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 22 Oct 2024 12:28:46 +0200 Subject: [PATCH 42/47] ProjectExplorer: Fix a warning Code is technically correct, but gcc 13.2.0 complains: toolchain.cpp:925:295: warning: misleading indentation; statement is not part of the previous 'for' [-Wmisleading-indentation] 925 | if (__builtin_expect(!!(factory()->supportedLanguages().contains(tc->language())), true)) {} else { ::Utils::writeAssertLocation( "\"" "factory()->supportedLanguages().contains(tc->language())""\" in " "/data/dev/creator/src/plugins/projectexplorer/toolchain.cpp" ":" "925"); return; } do {} while (0); Change-Id: I37bb34917fc06e51a2d40807d9f4a26447880b81 Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/toolchain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index e625c143d59..420532ef48a 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -921,8 +921,9 @@ ToolchainBundle::ToolchainBundle(const Toolchains &toolchains, AutoRegister auto // Check pre-conditions. QTC_ASSERT(!m_toolchains.isEmpty(), return); QTC_ASSERT(m_toolchains.size() <= factory()->supportedLanguages().size(), return); - for (const Toolchain * const tc : toolchains) + for (const Toolchain * const tc : toolchains) { QTC_ASSERT(factory()->supportedLanguages().contains(tc->language()), return); + } for (int i = 1; i < int(toolchains.size()); ++i) { const Toolchain * const tc = toolchains.at(i); QTC_ASSERT(tc->typeId() == toolchains.first()->typeId(), return); From be243b366deafe578de292c369b9413cc322d105 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 22 Oct 2024 16:44:21 +0200 Subject: [PATCH 43/47] ProjectExplorer: Rename X11Forward checker name Forward to local display sounded as if selecting the checkbox would forward X11 to the local display, instead of what entered in the text field. Changing it to "Enabled" is supposed to make it clear that this checkbox enables X11 Forwarding completely. Change-Id: I77655a12b017f905b3dd4b125608cf456191f91a Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/runconfigurationaspects.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index b7e5886cfb5..e497963815d 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -955,8 +955,7 @@ X11ForwardingAspect::X11ForwardingAspect(AspectContainer *container) setDisplayStyle(LineEditDisplay); setId("X11ForwardingAspect"); setSettingsKey("RunConfiguration.X11Forwarding"); - makeCheckable(CheckBoxPlacement::Right, Tr::tr("Forward to local display"), - "RunConfiguration.UseX11Forwarding"); + makeCheckable(CheckBoxPlacement::Right, Tr::tr("Enable"), "RunConfiguration.UseX11Forwarding"); setValue(defaultDisplay()); addDataExtractor(this, &X11ForwardingAspect::display, &Data::display); From 05a4850389d89f4b958e62c92f5b4b35b78f155b Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 22 Oct 2024 16:25:26 +0200 Subject: [PATCH 44/47] Doc: Link to VxWorks docs from the changelog Task-number: QTCREATORBUG-31510 Change-Id: Id33103e7dfd36a6505f7cd94d32281815278c014 Reviewed-by: Eike Ziller --- dist/changelog/changes-15.0.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/changelog/changes-15.0.0.md b/dist/changelog/changes-15.0.0.md index fdd89a79d9b..e016757125d 100644 --- a/dist/changelog/changes-15.0.0.md +++ b/dist/changelog/changes-15.0.0.md @@ -298,6 +298,7 @@ Platforms ### VxWorks * Added support for VxWorks 24.03 + ([Documentation](https://doc-snapshots.qt.io/qtcreator-15.0/creator-how-to-create-vxworks-kits.html)) Credits for these changes go to: -------------------------------- From f6c1a103d6e4292965bd87e16946f97ef4b8c330 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 22 Oct 2024 14:58:36 +0200 Subject: [PATCH 45/47] Doc: Describe "VCS Log Directory" option in Projects view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...in the context menu. Task-number: QTCREATORBUG-31510 Change-Id: I145425bc085a664e89879c996f83aa66e2f8145d Reviewed-by: André Hartmann --- doc/qtcreator/src/user-interface/creator-projects-view.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc index 8dd1e84f5da..a6826c9ca3e 100644 --- a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc @@ -95,7 +95,7 @@ \li Add and remove subprojects. \li Find unused functions. \li Search in the selected directory. - + \li View version control system log for the current directory. \li Open a terminal window in the project directory. To specify the terminal to use on Linux and \macos, select \preferences > \uicontrol Environment > \uicontrol System. From dd6cf04e6f732df6081b423c61858ba5b090a754 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 23 Oct 2024 08:43:54 +0200 Subject: [PATCH 46/47] Utils: Fix expander test expandProcessArgs now returns an error if an invalid sequence is given. Change-Id: I6e7398bbd4101d4b3faae11d5c4f2d23fb71f000 Reviewed-by: Christian Stenger --- tests/auto/utils/expander/tst_expander.cpp | 25 +++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/auto/utils/expander/tst_expander.cpp b/tests/auto/utils/expander/tst_expander.cpp index 33cb2d0bd43..eb886e25ec7 100644 --- a/tests/auto/utils/expander/tst_expander.cpp +++ b/tests/auto/utils/expander/tst_expander.cpp @@ -212,10 +212,6 @@ private slots: << QString("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") << Utils::OsTypeLinux << withspace; - // Due to security concerns, backslash-escaping an expando is treated as a quoting error - QTest::newRow("backslash-escaping") - << QString("echo \\%{file}") << QString("echo \\%{file}") << Utils::OsTypeLinux << file; - QTest::newRow("expando-within-shell-substitution") << QString("${VAR:-%{file}}") << QString("${VAR:-foo.txt}") << Utils::OsTypeLinux << file; @@ -286,7 +282,6 @@ private slots: Val{"%{x}", "\\", OsTypeWindows}, Val{"%{y}", "\"\"\\^\"\"\"", OsTypeWindows}, Val{"%{z}", "\"\"", OsTypeWindows}, - Val{"^%{z}%{z}", "^%{z}%{z}", OsTypeWindows}, // stupid user check Val{"quoted", 0, OsTypeWindows}, Val{"\"%{a}\"", "\"hi\"", OsTypeWindows}, @@ -440,7 +435,6 @@ private slots: Val{"%{e}", "'h\"i'", OsTypeLinux}, Val{"%{f}", "'h'\\''i'", OsTypeLinux}, Val{"%{z}", "''", OsTypeLinux}, - Val{"\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux}, // stupid user check Val{"single-quoted", 0, OsTypeLinux}, Val{"'%{a}'", "'hi'", OsTypeLinux}, @@ -495,7 +489,6 @@ private slots: MacroExpander expander; if (os == Utils::OsTypeWindows) { - expander.registerVariable("a", "", [] { return "hi"; }); expander.registerVariable("aa", "", [] { return "hi ho"; }); expander.registerVariable("b", "", [] { return "h\\i"; }); expander.registerVariable("c", "", [] { return "\\hi"; }); @@ -512,20 +505,32 @@ private slots: expander.registerVariable("k", "", [] { return "&special;"; }); expander.registerVariable("x", "", [] { return "\\"; }); expander.registerVariable("y", "", [] { return "\""; }); - expander.registerVariable("z", "", [] { return ""; }); } else { - expander.registerVariable("a", "", [] { return "hi"; }); expander.registerVariable("b", "", [] { return "hi ho"; }); expander.registerVariable("c", "", [] { return "&special;"; }); expander.registerVariable("d", "", [] { return "h\\i"; }); expander.registerVariable("e", "", [] { return "h\"i"; }); expander.registerVariable("f", "", [] { return "h'i"; }); - expander.registerVariable("z", "", [] { return ""; }); } + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("z", "", [] { return ""; }); QCOMPARE(expander.expandProcessArgs(in, os), out); } + void testProcessArgsFails() + { + MacroExpander expander; + + expander.registerVariable("z", "", [] { return ""; }); + expander.registerVariable("file", "", [] { return "foo.txt"; }); + + QVERIFY(!expander.expandProcessArgs("\\%{z}%{z}", OsTypeLinux)); + QVERIFY(!expander.expandProcessArgs("echo \\%{file}", OsTypeLinux)); + + QVERIFY(!expander.expandProcessArgs("^%{z}%{z}", OsTypeWindows)); + } + void testMacroExpander_data() { QTest::addColumn("in"); From d2dccfa440445d598f1c26a7151ebf9dd701c2ab Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 22 Oct 2024 16:51:54 +0200 Subject: [PATCH 47/47] iOS: Fix unintentional fall-through Amends 907c0a14010c1bfc130fdef96e7a855fa93a6603 Change-Id: Ie58b6cfeaba83bcc0236e0aad594f9ac8042e1b2 Reviewed-by: Christian Stenger --- src/plugins/ios/iostoolhandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index 7e90da0d633..88a6aa022f8 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -467,6 +467,7 @@ void IosDeviceToolHandlerPrivate::processXml() break; case ParserState::Error: errorMsg(p.chars); + break; case ParserState::DeviceId: if (m_deviceId.isEmpty()) m_deviceId = p.chars;