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/dist/changelog/changes-15.0.0.md b/dist/changelog/changes-15.0.0.md index c4cf156e50e..e016757125d 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 @@ -296,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: -------------------------------- diff --git a/doc/qtcreator/images/qtcreator-welcome-session.webp b/doc/qtcreator/images/qtcreator-welcome-session.webp index 7a67042f1cb..3911bd66e65 100644 Binary files a/doc/qtcreator/images/qtcreator-welcome-session.webp and b/doc/qtcreator/images/qtcreator-welcome-session.webp differ 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..51c786a2d53 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 @@ -233,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} */ 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. 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 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(); 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/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 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 diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index b4fa60481ed..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,18 +774,15 @@ 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, AbstractMacroExpander *mx, OsType osType) +bool ProcessArgs::expandMacros(QString *cmd, const FindMacro &findMacro, OsType osType) { QString str = *cmd; if (str.isEmpty()) @@ -775,7 +791,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 +855,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 +970,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..78f756a9f61 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -59,9 +59,11 @@ 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); /*! 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/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 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") diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index c7231a9976a..9ec36e09fb0 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -35,9 +35,24 @@ namespace Utils { -static DeviceFileHooks s_deviceHooks; -inline bool isWindowsDriveLetter(QChar ch); +static DeviceFileHooks &deviceFileHooks() +{ + static DeviceFileHooks theDeviceHooks; + return theDeviceHooks; +} +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 @@ -328,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. @@ -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..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 DeviceFileHooks &instance(); - std::function(const FilePath &)> fileAccess; std::function deviceDisplayName; std::function ensureReachable; @@ -359,8 +358,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/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 09bd95bc7bd..fdaf4c7aebc 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -14,12 +14,11 @@ #include #include #include +#include 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"; @@ -27,12 +26,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 +328,7 @@ MacroExpander::~MacroExpander() */ bool MacroExpander::resolveMacro(const QString &name, QString *ret) const { - QSet seen; + QSet seen; return d->resolveMacro(name, ret, seen); } @@ -242,6 +341,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 +370,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,11 +411,19 @@ QVariant MacroExpander::expandVariant(const QVariant &v) const return v; } -QString MacroExpander::expandProcessArgs(const QString &argsWithVariables) const +expected_str MacroExpander::expandProcessArgs( + const QString &argsWithVariables, Utils::OsType osType) const { QString result = argsWithVariables; - const bool ok = ProcessArgs::expandMacros(&result, d); - QTC_ASSERT(ok, qCDebug(expanderLog) << "Expanding failed: " << argsWithVariables); + const bool ok = ProcessArgs::expandMacros( + &result, + [this](const QString &str, int *pos, QString *ret) { return d->findMacro(str, pos, ret); }, + osType); + + 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 ad9a21a938b..d080a9942ee 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; + expected_str 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/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; }; 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)); 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 diff --git a/src/plugins/axivion/axivionperspective.cpp b/src/plugins/axivion/axivionperspective.cpp index b3dfdee5122..b58c4cce684 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); @@ -253,6 +254,7 @@ IssuesWidget::IssuesWidget(QWidget *parent) m_dashboardProjects->clear(); } updateBasicProjectInfo(std::nullopt); + m_issuesView->hideProgressIndicator(); } }); @@ -450,14 +452,23 @@ 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->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); + if (m_overlay) + m_overlay->hide(); + m_issuesView->showProgressIndicator(); fetchDashboardAndProjectInfo(onDashboardInfoFetched, currentProject); } @@ -668,7 +679,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; } @@ -874,6 +889,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( @@ -888,6 +904,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({}); }); 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; 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/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)) 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}}); 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); diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 2413c94ddcb..07a0c39c772 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -4,6 +4,8 @@ #include "effectshaderscodeeditor.h" #include "effectcodeeditorwidget.h" +#include + #include #include 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..69ce63268f2 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 {}; } @@ -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; 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 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..88a6aa022f8 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,6 +463,9 @@ void IosDeviceToolHandlerPrivate::processXml() stack.removeLast(); switch (p.kind) { case ParserState::Msg: + emit q->message(p.chars); + break; + case ParserState::Error: errorMsg(p.chars); break; case ParserState::DeviceId: 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/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 * { diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 508a80736ec..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++"); @@ -1626,7 +1632,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)}; 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; } diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 74d9d2b7b0f..e497963815d 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 @@ -331,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; } /*! @@ -956,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); @@ -965,7 +963,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/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); 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"; 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, 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; } diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 705891e3900..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; @@ -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 { 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); } 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(); 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..eb886e25ec7 --- /dev/null +++ b/tests/auto/utils/expander/tst_expander.cpp @@ -0,0 +1,610 @@ +// 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("os"); + 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() << 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") << Utils::OsTypeLinux << file; + + QTest::newRow("with-ticks") + << 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\"'") << Utils::OsTypeLinux << file; + + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") + << Utils::OsTypeLinux << 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("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") + << Utils::OsTypeLinux << 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; + + // Windows tests + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") + << Utils::OsTypeWindows << 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; + + if (setup) + setup(expander); + + 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{"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{"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("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 "\""; }); + } else { + 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("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"); + 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); + } +}; + +QTEST_GUILESS_MAIN(tst_expander) + +#include "tst_expander.moc" 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"); 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", 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"