From 53a81600348fb53f14ee83ee6c6f0ec0cebd9dd4 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Wed, 25 May 2022 20:27:25 +0200 Subject: [PATCH 01/58] Utils: Report arm64 as 64bit Windows version Change-Id: Ied30bde01d269a0f0299a7b849b2deaf4c009ab5 Reviewed-by: David Schulz --- src/libs/utils/winutils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/winutils.cpp b/src/libs/utils/winutils.cpp index afc642d5a8c..bda4c197336 100644 --- a/src/libs/utils/winutils.cpp +++ b/src/libs/utils/winutils.cpp @@ -145,7 +145,8 @@ QTCREATOR_UTILS_EXPORT bool is64BitWindowsSystem() SYSTEM_INFO systemInfo; GetNativeSystemInfo(&systemInfo); return systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 - || systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64; + || systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 + || systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64; #else return false; #endif From e3af694ad9a4e5dfe7f2f445ca4e4d75d1cb4c3d Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 23 May 2022 18:55:37 +0200 Subject: [PATCH 02/58] ProjectExplorer: Fix Windows Arm64 MSVC compilers detection Microsoft offers cross-compilers for the Arm64 architecture. Windows 11 Arm64 can emulate the x86 and x64 architecture so the binaries that can be run should be taken into consideration. Change-Id: I330c15a2c543eada9f7c939887ce13a1dd7559fd Reviewed-by: Reviewed-by: David Schulz --- src/libs/utils/hostosinfo.cpp | 1 + src/plugins/projectexplorer/msvctoolchain.cpp | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/hostosinfo.cpp b/src/libs/utils/hostosinfo.cpp index 78ed7f22867..48518f8fc1a 100644 --- a/src/libs/utils/hostosinfo.cpp +++ b/src/libs/utils/hostosinfo.cpp @@ -65,6 +65,7 @@ HostOsInfo::HostArchitecture HostOsInfo::hostArchitecture() case PROCESSOR_ARCHITECTURE_IA64: return HostOsInfo::HostArchitectureItanium; case PROCESSOR_ARCHITECTURE_ARM: + case PROCESSOR_ARCHITECTURE_ARM64: return HostOsInfo::HostArchitectureArm; default: return HostOsInfo::HostArchitectureUnknown; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index c4eae8b7903..bfafaa83f65 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -158,12 +158,23 @@ static bool hostSupportsPlatform(MsvcToolChain::Platform platform) { if (hostPrefersPlatform(platform)) return true; + + switch (HostOsInfo::hostArchitecture()) { // The x86 host toolchains are not the preferred toolchains on amd64 but they are still // supported by that host - return HostOsInfo::hostArchitecture() == HostOsInfo::HostArchitectureAMD64 - && (platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64 + case HostOsInfo::HostArchitectureAMD64: + return platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64 || platform == MsvcToolChain::x86_ia64 || platform == MsvcToolChain::x86_arm - || platform == MsvcToolChain::x86_arm64); + || platform == MsvcToolChain::x86_arm64; + // The Arm64 host can run the cross-compilers via emulation of x86 and amd64 + case HostOsInfo::HostArchitectureArm: + return platform == MsvcToolChain::x86_arm || platform == MsvcToolChain::x86_arm64 + || platform == MsvcToolChain::amd64_arm || platform == MsvcToolChain::amd64_arm64 + || platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64 + || platform == MsvcToolChain::amd64 || platform == MsvcToolChain::amd64_x86; + default: + return false; + } } static QString fixRegistryPath(const QString &path) From 603ea1c6799daa631665f09911873137b103499f Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 24 May 2022 10:11:08 +0200 Subject: [PATCH 03/58] ProjectExplorer: Introduce DeviceKitAspect::deviceFilePath() A convenience method redirecting to IDevice::filePath. Change-Id: I1bd4a6500fa051641873020244d97e307f579e72 Reviewed-by: Jarek Kobus Reviewed-by: Christian Stenger Reviewed-by: Qt CI Bot --- src/plugins/debugger/analyzer/startremotedialog.cpp | 7 ++----- src/plugins/projectexplorer/kitinformation.cpp | 7 +++++++ src/plugins/projectexplorer/kitinformation.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/debugger/analyzer/startremotedialog.cpp b/src/plugins/debugger/analyzer/startremotedialog.cpp index e148c0f5bff..700b688fad8 100644 --- a/src/plugins/debugger/analyzer/startremotedialog.cpp +++ b/src/plugins/debugger/analyzer/startremotedialog.cpp @@ -131,11 +131,8 @@ void StartRemoteDialog::validate() Runnable StartRemoteDialog::runnable() const { - Kit *kit = d->kitChooser->currentKit(); - IDevice::ConstPtr device = DeviceKitAspect::device(kit); - FilePath filePath = FilePath::fromString(d->executable->text()); - if (device) - filePath = device->filePath(d->arguments->text()); + const Kit *kit = d->kitChooser->currentKit(); + const FilePath filePath = DeviceKitAspect::deviceFilePath(kit, d->executable->text()); Runnable r; r.command = {filePath, d->arguments->text(), CommandLine::Raw}; diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index 994a21465c9..a39c091dba3 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -1110,6 +1110,13 @@ void DeviceKitAspect::setDeviceId(Kit *k, Utils::Id id) k->setValue(DeviceKitAspect::id(), id.toSetting()); } +FilePath DeviceKitAspect::deviceFilePath(const Kit *k, const QString &pathOnDevice) +{ + if (IDevice::ConstPtr dev = device(k)) + return dev->filePath(pathOnDevice); + return FilePath::fromString(pathOnDevice); +} + void DeviceKitAspect::kitsWereLoaded() { const QList kits = KitManager::kits(); diff --git a/src/plugins/projectexplorer/kitinformation.h b/src/plugins/projectexplorer/kitinformation.h index de0939b8388..a925d84ba47 100644 --- a/src/plugins/projectexplorer/kitinformation.h +++ b/src/plugins/projectexplorer/kitinformation.h @@ -161,6 +161,7 @@ public: static Utils::Id deviceId(const Kit *k); static void setDevice(Kit *k, IDeviceConstPtr dev); static void setDeviceId(Kit *k, Utils::Id dataId); + static Utils::FilePath deviceFilePath(const Kit *k, const QString &pathOnDevice); private: QVariant defaultValue(const Kit *k) const; From c6c919e6719db3b100ee11b2c28ec11d8bdca528 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 25 May 2022 17:33:01 +0200 Subject: [PATCH 04/58] ClangCodeModel: Properly parse function types for outline Fixes: QTCREATORBUG-27587 Change-Id: Icf663e386fa90c209aa998d2d7ab7ae0fcb40792 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 26 +++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 8d18164090f..64f8b014261 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -1550,11 +1550,29 @@ QString ClangdClient::displayNameFromDocumentSymbol(SymbolKind kind, const QStri case SymbolKind::Constructor: return name + detail; case SymbolKind::Method: - case LanguageServerProtocol::SymbolKind::Function: { - const int parenOffset = detail.indexOf(" ("); - if (parenOffset == -1) + case SymbolKind::Function: { + const int lastParenOffset = detail.lastIndexOf(')'); + if (lastParenOffset == -1) return name; - return name + detail.mid(parenOffset + 1) + " -> " + detail.mid(0, parenOffset); + int leftParensNeeded = 1; + int i = -1; + for (i = lastParenOffset - 1; i >= 0; --i) { + switch (detail.at(i).toLatin1()) { + case ')': + ++leftParensNeeded; + break; + case '(': + --leftParensNeeded; + break; + default: + break; + } + if (leftParensNeeded == 0) + break; + } + if (leftParensNeeded > 0) + return name; + return name + detail.mid(i) + " -> " + detail.left(i); } case SymbolKind::Variable: case SymbolKind::Field: From 78d8dd8997717f55ff13b826d0c2aab717264d78 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 25 May 2022 15:25:22 +0200 Subject: [PATCH 05/58] LinuxDevice: Fix starting a shell with SSH_ASKPASS Change-Id: I11a8a477a1f9796ceb021037b781da2ca8d87f43 Reviewed-by: Qt CI Bot Reviewed-by: Christian Kandeler --- src/plugins/remotelinux/linuxdevice.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index b0881625023..f75f94c9991 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -804,11 +804,30 @@ public: m_shell->setWriteData("echo\n"); m_shell->start(); - if (!m_shell->waitForStarted() || !m_shell->waitForReadyRead() - || m_shell->readAllStandardOutput() != "\n") { + auto failed = [this] { closeShell(); qCDebug(linuxDeviceLog) << "Failed to connect to" << m_displaylessSshParameters.host(); return false; + }; + + QDeadlineTimer timer(30000); + if (!m_shell->waitForStarted(timer.remainingTime())) + return failed(); + + while (true) { + if (!m_shell->waitForReadyRead(timer.remainingTime())) + return failed(); + + const QByteArray output = m_shell->readAllStandardOutput(); + if (output == "\n") + break; // expected output from echo + if (output.size() > 0) + return failed(); // other unidentified output + + // In case of trying to run a shell using SSH_ASKPASS, it may happen + // that we receive ready read signal but for error channel, while output + // channel still is empty. In this case we wait in loop until the user + // provides the right password, otherwise we timeout after 30 seconds. } return true; } From 6b07b84bcd8c024fd9cf572cb43d2c9349b4ceb2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 17 May 2022 00:04:48 +0200 Subject: [PATCH 06/58] FileTransfer: Handle shared ssh connection Change-Id: I251bdc4e8c9e8dd47fca24ecdb80239315d9e854 Reviewed-by: Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot --- src/plugins/remotelinux/linuxdevice.cpp | 112 ++++++++++++++++++------ src/plugins/remotelinux/linuxdevice.h | 1 + 2 files changed, 84 insertions(+), 29 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index f75f94c9991..2507e241db3 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1480,14 +1480,37 @@ class FileTransferInterface : public QObject { Q_OBJECT public: - void setDevice(const ProjectExplorer::IDeviceConstPtr &device) { m_device = device; } + void setDevice(const ProjectExplorer::IDeviceConstPtr &device) + { + m_device = device; + if (m_device) { + const LinuxDevice *linuxDevice = m_device.dynamicCast().get(); + QTC_ASSERT(linuxDevice, return); + m_devicePrivate = linuxDevice->d; + } + } void setFilesToTransfer(const FilesToTransfer &files, TransferDirection direction) { m_files = files; m_direction = direction; } - void start() { startImpl(); } + void start() + { + m_sshParameters = displayless(m_devicePrivate->q->sshParameters()); + if (SshSettings::connectionSharingEnabled()) { + m_connecting = true; + m_connectionHandle.reset(new SshConnectionHandle(m_device->sharedFromThis())); + m_connectionHandle->setParent(this); + connect(m_connectionHandle.get(), &SshConnectionHandle::connected, + this, &FileTransferInterface::handleConnected); + connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected, + this, &FileTransferInterface::handleDisconnected); + m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters); + } else { + startImpl(); + } + } signals: void progress(const QString &progressMessage); @@ -1534,15 +1557,55 @@ protected: emit done(m_process.resultData()); } - FileTransferMethod m_method = FileTransferMethod::Default; - IDevice::ConstPtr m_device; + QStringList fullConnectionOptions() const + { + QStringList options = m_sshParameters.connectionOptions(SshSettings::sshFilePath()); + if (!m_socketFilePath.isEmpty()) + options << "-o" << ("ControlPath=" + m_socketFilePath); + return options; + } + + QString host() const { return m_sshParameters.host(); } + QString userAtHost() const { return m_sshParameters.userName() + '@' + m_sshParameters.host(); } + + QtcProcess &process() { return m_process; } + FilesToTransfer m_files; - QtcProcess m_process; TransferDirection m_direction = TransferDirection::Invalid; private: virtual void startImpl() = 0; virtual void doneImpl() = 0; + + void handleConnected(const QString &socketFilePath) + { + m_connecting = false; + m_socketFilePath = socketFilePath; + startImpl(); + } + + void handleDisconnected(const ProcessResultData &result) + { + ProcessResultData resultData = result; + if (m_connecting) + resultData.m_error = QProcess::FailedToStart; + + m_connecting = false; + if (m_connectionHandle) // TODO: should it disconnect from signals first? + m_connectionHandle.release()->deleteLater(); + + if (resultData.m_error != QProcess::UnknownError || m_process.state() != QProcess::NotRunning) + emit done(resultData); // TODO: don't emit done() on process finished afterwards + } + + FileTransferMethod m_method = FileTransferMethod::Default; + IDevice::ConstPtr m_device; + LinuxDevicePrivate *m_devicePrivate = nullptr; + std::unique_ptr m_connectionHandle; + QtcProcess m_process; + QString m_socketFilePath; + SshParameters m_sshParameters; + bool m_connecting = false; }; class SftpTransferImpl : public FileTransferInterface @@ -1551,7 +1614,7 @@ public: SftpTransferImpl() : FileTransferInterface(FileTransferMethod::Sftp) { } private: - void startImpl() + void startImpl() final { const FilePath sftpBinary = SshSettings::sftpFilePath(); if (!sftpBinary.exists()) { @@ -1597,13 +1660,9 @@ private: + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n'); } m_batchFile->close(); - m_process.setStandardInputFile(m_batchFile->fileName()); - - // TODO: Add support for shared ssh connection - const SshParameters params = displayless(m_device->sshParameters()); - m_process.setCommand(CommandLine(sftpBinary, - params.connectionOptions(sftpBinary) << params.host())); - m_process.start(); + process().setStandardInputFile(m_batchFile->fileName()); + process().setCommand(CommandLine(sftpBinary, fullConnectionOptions() << host())); + process().start(); } void doneImpl() final { handleDone(); } @@ -1620,13 +1679,13 @@ public: { } private: - void startImpl() + void startImpl() final { m_currentIndex = 0; startNextFile(); } - void doneImpl() + void doneImpl() final { if (m_files.size() == 0 || m_currentIndex == m_files.size() - 1) return handleDone(); @@ -1640,30 +1699,25 @@ private: void startNextFile() { - m_process.close(); + process().close(); - const SshParameters parameters = displayless(m_device->sshParameters()); - const QStringList connectionOptions // TODO: add shared connection here - = parameters.connectionOptions(SshSettings::sshFilePath()); const QString sshCmdLine = ProcessArgs::joinArgs( - QStringList{SshSettings::sshFilePath().toUserOutput()} << connectionOptions, - OsTypeLinux); - const QStringList options{"-e", sshCmdLine, m_flags}; - const QString remoteHost = parameters.userName() + '@' + parameters.host(); + QStringList{SshSettings::sshFilePath().toUserOutput()} + << fullConnectionOptions(), OsTypeLinux); + QStringList options{"-e", sshCmdLine, m_flags}; - QStringList args = QStringList(options); if (!m_files.isEmpty()) { // NormalRun const FileToTransfer file = m_files.at(m_currentIndex); const FileToTransfer fixedFile = fixLocalFileOnWindows(file, options); - const auto fixedPaths = fixPaths(fixedFile, remoteHost); + const auto fixedPaths = fixPaths(fixedFile, userAtHost()); - args << fixedPaths.first << fixedPaths.second; + options << fixedPaths.first << fixedPaths.second; } else { // TestRun - args << "-n" << "--exclude=*" << (remoteHost + ":/tmp"); + options << "-n" << "--exclude=*" << (userAtHost() + ":/tmp"); } // TODO: Get rsync location from settings? - m_process.setCommand(CommandLine("rsync", args)); - m_process.start(); + process().setCommand(CommandLine("rsync", options)); + process().start(); } // On Windows, rsync is either from msys or cygwin. Neither work with the other's ssh.exe. diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index 3272e46e575..8a0406c9424 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -92,6 +92,7 @@ protected: class LinuxDevicePrivate *d; friend class SshProcessInterface; + friend class FileTransferInterface; }; namespace Internal { From 08350cb8aeb126ca5fb88ef0feacf79c56bcc811 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 11 May 2022 17:58:24 +0200 Subject: [PATCH 07/58] SshSharedConnection: Get rid of connectionOptions() Change-Id: Ieb7da550183aa57db3dd6d0b714c1e66e46d38e6 Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/remotelinux/linuxdevice.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 2507e241db3..24d4fb6f09f 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -101,10 +101,6 @@ public: QTC_ASSERT(m_masterSocketDir, return QString()); return m_masterSocketDir->path() + "/cs"; } - QStringList connectionOptions(const Utils::FilePath &binary) const - { - return m_sshParameters.connectionOptions(binary) << "-o" << ("ControlPath=" + socketFilePath()); - } signals: void connected(const QString &socketFilePath); @@ -117,8 +113,7 @@ private: void emitError(QProcess::ProcessError processError, const QString &errorString); void emitDisconnected(); QString fullProcessError() const; - QStringList connectionArgs(const FilePath &binary) const - { return connectionOptions(binary) << m_sshParameters.host(); } + QStringList connectionArgs(const FilePath &binary) const; const SshParameters m_sshParameters; std::unique_ptr m_masterProcess; @@ -272,6 +267,12 @@ QString SshSharedConnection::fullProcessError() const return allErrors.join('\n'); } +QStringList SshSharedConnection::connectionArgs(const FilePath &binary) const +{ + return m_sshParameters.connectionOptions(binary) << "-o" << ("ControlPath=" + socketFilePath()) + << m_sshParameters.host(); +} + // SshConnectionHandle class SshConnectionHandle : public QObject From c0333b2b6832d12352f90dfc2e14ea7b4066b17d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 17 May 2022 01:37:46 +0200 Subject: [PATCH 08/58] FileToTransfer: Hide transferDirection from public API Change-Id: I3df5a7f7d156385b8a6ba1cf0c9b4a216508dc20 Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/remotelinux/filetransfer.h | 7 ------- src/plugins/remotelinux/linuxdevice.cpp | 22 ++++++++++++++-------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/plugins/remotelinux/filetransfer.h b/src/plugins/remotelinux/filetransfer.h index 8560bc2a00a..9459bac1277 100644 --- a/src/plugins/remotelinux/filetransfer.h +++ b/src/plugins/remotelinux/filetransfer.h @@ -35,18 +35,11 @@ namespace Utils { class ProcessResultData; } namespace RemoteLinux { -enum class TransferDirection { - Upload, - Download, - Invalid -}; - class REMOTELINUX_EXPORT FileToTransfer { public: Utils::FilePath m_source; Utils::FilePath m_target; - TransferDirection transferDirection() const; }; using FilesToTransfer = QList; diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 24d4fb6f09f..23db2aa9fab 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1417,23 +1417,29 @@ bool LinuxDevice::writeFileContents(const FilePath &filePath, const QByteArray & return d->runInShell({"dd", {"of=" + filePath.path()}}, data); } -TransferDirection FileToTransfer::transferDirection() const +enum class TransferDirection { + Upload, + Download, + Invalid +}; + +static TransferDirection transferDirection(const FileToTransfer &file) { - if (m_source.needsDevice() == m_target.needsDevice()) + if (file.m_source.needsDevice() == file.m_target.needsDevice()) return TransferDirection::Invalid; - return m_source.needsDevice() ? TransferDirection::Download : TransferDirection::Upload; + return file.m_source.needsDevice() ? TransferDirection::Download : TransferDirection::Upload; } static TransferDirection transferDirection(const FilesToTransfer &files) { if (files.isEmpty()) return TransferDirection::Invalid; - const TransferDirection transferDirection = files.first().transferDirection(); + const TransferDirection direction = transferDirection(files.first()); for (const FileToTransfer &file : files) { - if (file.transferDirection() != transferDirection) + if (transferDirection(file) != direction) return TransferDirection::Invalid; } - return transferDirection; + return direction; } static bool isDeviceMatched(const FilePath &file, const QString &id) @@ -1444,9 +1450,9 @@ static bool isDeviceMatched(const FilePath &file, const QString &id) static bool isDeviceMatched(const FilesToTransfer &files, const QString &id) { for (const FileToTransfer &file : files) { - if (file.transferDirection() == TransferDirection::Upload && !isDeviceMatched(file.m_target, id)) + if (transferDirection(file) == TransferDirection::Upload && !isDeviceMatched(file.m_target, id)) return false; - if (file.transferDirection() == TransferDirection::Download && !isDeviceMatched(file.m_source, id)) + if (transferDirection(file) == TransferDirection::Download && !isDeviceMatched(file.m_source, id)) return false; } return true; From 576f49df24adcfc3b3f6b293d9ff63f0251a43be Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 20 May 2022 11:50:52 +0200 Subject: [PATCH 09/58] FileTransfer: Don't crash when used for non-linux device Don't crash when start() was called for non-linux device. Change-Id: I1722ea420ed0fe74418c1c9a6f8702ae079b7bd7 Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/remotelinux/linuxdevice.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 23db2aa9fab..8183039ad4e 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1490,6 +1490,7 @@ public: void setDevice(const ProjectExplorer::IDeviceConstPtr &device) { m_device = device; + m_devicePrivate = nullptr; if (m_device) { const LinuxDevice *linuxDevice = m_device.dynamicCast().get(); QTC_ASSERT(linuxDevice, return); @@ -1504,6 +1505,12 @@ public: void start() { + if (!m_devicePrivate) { + startFailed(tr("Transferring files to/from non-linux device " + "isn't supported currently.")); + return; + } + m_sshParameters = displayless(m_devicePrivate->q->sshParameters()); if (SshSettings::connectionSharingEnabled()) { m_connecting = true; @@ -1822,9 +1829,12 @@ void FileTransferPrivate::run(RunMode mode) if (m_files.isEmpty()) return startFailed(tr("No files to transfer.")); + if (!m_device) + return startFailed(tr("No device set for transfer.")); + direction = transferDirection(m_files); if (direction == TransferDirection::Invalid) - return startFailed(tr("Mixing different types on transfer in one go.")); + return startFailed(tr("Mixing different types of transfer in one go.")); if (!isDeviceMatched(m_files, m_device->id().toString())) return startFailed(tr("Trying to transfer into / from not matching device.")); From adecdb4f2a6ad6a4a03401f34a45545497fbf644 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 20 May 2022 19:21:39 +0200 Subject: [PATCH 10/58] AbstractRemoteLinuxDeployService: Remove SettingUpDevice phase No subclass was using it currently, so simply internals. Change-Id: Ie9574fdb2d6a26d089f3059acca407d65c59b2cc Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot --- .../abstractremotelinuxdeployservice.cpp | 29 +++---------------- .../abstractremotelinuxdeployservice.h | 5 ---- ...abstractuploadandinstallpackageservice.cpp | 12 -------- .../abstractuploadandinstallpackageservice.h | 2 -- .../genericdirectuploadservice.cpp | 12 -------- .../remotelinux/genericdirectuploadservice.h | 3 -- .../remotelinuxcheckforfreediskspacestep.cpp | 10 +++---- 7 files changed, 9 insertions(+), 64 deletions(-) diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp index cdecdaf6c63..f4ff1cef251 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp @@ -43,7 +43,7 @@ namespace RemoteLinux { namespace Internal { namespace { -enum State { Inactive, SettingUpDevice, Deploying }; +enum State { Inactive, Deploying }; } // anonymous namespace class AbstractRemoteLinuxDeployServicePrivate @@ -131,8 +131,8 @@ void AbstractRemoteLinuxDeployService::start() return; } - d->state = SettingUpDevice; - doDeviceSetup(); + d->state = Deploying; + doDeploy(); } void AbstractRemoteLinuxDeployService::stop() @@ -140,17 +140,9 @@ void AbstractRemoteLinuxDeployService::stop() if (d->stopRequested) return; - switch (d->state) { - case Inactive: - break; - case SettingUpDevice: - d->stopRequested = true; - stopDeviceSetup(); - break; - case Deploying: + if (d->state == Deploying) { d->stopRequested = true; stopDeployment(); - break; } } @@ -171,19 +163,6 @@ void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map) d->deployTimes.importDeployTimes(map); } -void AbstractRemoteLinuxDeployService::handleDeviceSetupDone(bool success) -{ - QTC_ASSERT(d->state == SettingUpDevice, return); - - if (!success || d->stopRequested) { - setFinished(); - return; - } - - d->state = Deploying; - doDeploy(); -} - void AbstractRemoteLinuxDeployService::handleDeploymentDone() { QTC_ASSERT(d->state == Deploying, return); diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.h b/src/plugins/remotelinux/abstractremotelinuxdeployservice.h index c4192d10a5e..5590d879d03 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.h +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.h @@ -96,13 +96,8 @@ protected: bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const; - void handleDeviceSetupDone(bool success); void handleDeploymentDone(); - // Should do things needed *before* connecting. Call default implementation afterwards. - virtual void doDeviceSetup() { handleDeviceSetupDone(true); } - virtual void stopDeviceSetup() { handleDeviceSetupDone(false); } - void setFinished(); private: diff --git a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp index 1326c484301..05c1e3e7269 100644 --- a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp +++ b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp @@ -85,18 +85,6 @@ bool AbstractUploadAndInstallPackageService::isDeploymentNecessary() const return hasLocalFileChanged(DeployableFile(d->packageFilePath, QString())); } -void AbstractUploadAndInstallPackageService::doDeviceSetup() -{ - QTC_ASSERT(d->state == Inactive, return); - AbstractRemoteLinuxDeployService::doDeviceSetup(); -} - -void AbstractUploadAndInstallPackageService::stopDeviceSetup() -{ - QTC_ASSERT(d->state == Inactive, return); - AbstractRemoteLinuxDeployService::stopDeviceSetup(); -} - void AbstractUploadAndInstallPackageService::doDeploy() { QTC_ASSERT(d->state == Inactive, return); diff --git a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h index 82d9e6cd175..6cfab8d89ad 100644 --- a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h +++ b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h @@ -57,8 +57,6 @@ private: virtual QString uploadDir() const; // Defaults to remote user's home directory. bool isDeploymentNecessary() const override; - void doDeviceSetup() override; - void stopDeviceSetup() override; void doDeploy() override; void stopDeployment() override; diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp index 076a9027012..6743657c0d3 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.cpp +++ b/src/plugins/remotelinux/genericdirectuploadservice.cpp @@ -129,18 +129,6 @@ bool GenericDirectUploadService::isDeploymentNecessary() const return !d->deployableFiles.isEmpty(); } -void GenericDirectUploadService::doDeviceSetup() -{ - QTC_ASSERT(d->state == Inactive, return); - AbstractRemoteLinuxDeployService::doDeviceSetup(); -} - -void GenericDirectUploadService::stopDeviceSetup() -{ - QTC_ASSERT(d->state == Inactive, return); - AbstractRemoteLinuxDeployService::stopDeviceSetup(); -} - void GenericDirectUploadService::doDeploy() { QTC_ASSERT(d->state == Inactive, setFinished(); return); diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h index be7eda62610..42af64e3823 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.h +++ b/src/plugins/remotelinux/genericdirectuploadservice.h @@ -55,9 +55,6 @@ public: protected: bool isDeploymentNecessary() const override; - void doDeviceSetup() override; - void stopDeviceSetup() override; - void doDeploy() override; void stopDeployment() override; diff --git a/src/plugins/remotelinux/remotelinuxcheckforfreediskspacestep.cpp b/src/plugins/remotelinux/remotelinuxcheckforfreediskspacestep.cpp index 2c3650206c6..a332c8183d8 100644 --- a/src/plugins/remotelinux/remotelinuxcheckforfreediskspacestep.cpp +++ b/src/plugins/remotelinux/remotelinuxcheckforfreediskspacestep.cpp @@ -54,14 +54,11 @@ public: void setRequiredSpaceInBytes(quint64 sizeInBytes); private: - void deployAndFinish(); bool isDeploymentNecessary() const override { return true; } CheckResult isDeploymentPossible() const override; - void doDeviceSetup() final { deployAndFinish(); } - void stopDeviceSetup() final { } - void doDeploy() final {} + void doDeploy() final; void stopDeployment() final {} QString m_pathToCheck; @@ -78,7 +75,7 @@ void RemoteLinuxCheckForFreeDiskSpaceService::setRequiredSpaceInBytes(quint64 si m_requiredSpaceInBytes = sizeInBytes; } -void RemoteLinuxCheckForFreeDiskSpaceService::deployAndFinish() +void RemoteLinuxCheckForFreeDiskSpaceService::doDeploy() { auto cleanup = qScopeGuard([this] { setFinished(); }); const FilePath path = deviceConfiguration()->filePath(m_pathToCheck); @@ -86,6 +83,7 @@ void RemoteLinuxCheckForFreeDiskSpaceService::deployAndFinish() if (freeSpace < 0) { emit errorMessage(tr("Cannot get info about free disk space for \"%1\"") .arg(path.toUserOutput())); + handleDeploymentDone(); return; } @@ -97,11 +95,13 @@ void RemoteLinuxCheckForFreeDiskSpaceService::deployAndFinish() emit errorMessage(tr("The remote file system has only %n megabytes of free space, " "but %1 megabytes are required.", nullptr, freeSpaceMB) .arg(requiredSpaceMB)); + handleDeploymentDone(); return; } emit progressMessage(tr("The remote file system has %n megabytes of free space, going ahead.", nullptr, freeSpaceMB)); + handleDeploymentDone(); } CheckResult RemoteLinuxCheckForFreeDiskSpaceService::isDeploymentPossible() const From 49ab0e0ddaef767d16b8364759b8053a8ec040d3 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 23 May 2022 09:33:44 +0200 Subject: [PATCH 11/58] Get rid of AbstractUploadAndInstallPackageService We had only one class derived from it (UploadAndInstallTarPackageService), so glue both classes together. Don't export UploadAndInstallTarPackageService class, as it's not used outside. Move the implementation into cpp file. Change-Id: I1521d1badb559e510e11337ace309a867196b251 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Christian Kandeler --- src/plugins/remotelinux/CMakeLists.txt | 1 - ...abstractuploadandinstallpackageservice.cpp | 163 ------------------ .../abstractuploadandinstallpackageservice.h | 68 -------- src/plugins/remotelinux/remotelinux.qbs | 2 - .../uploadandinstalltarpackagestep.cpp | 156 ++++++++++++++--- .../uploadandinstalltarpackagestep.h | 18 -- 6 files changed, 135 insertions(+), 273 deletions(-) delete mode 100644 src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp delete mode 100644 src/plugins/remotelinux/abstractuploadandinstallpackageservice.h diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt index 9087867ccec..6599adac26d 100644 --- a/src/plugins/remotelinux/CMakeLists.txt +++ b/src/plugins/remotelinux/CMakeLists.txt @@ -5,7 +5,6 @@ add_qtc_plugin(RemoteLinux abstractpackagingstep.cpp abstractpackagingstep.h abstractremotelinuxdeployservice.cpp abstractremotelinuxdeployservice.h abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h - abstractuploadandinstallpackageservice.cpp abstractuploadandinstallpackageservice.h deploymenttimeinfo.cpp deploymenttimeinfo.h filetransfer.h genericdirectuploadservice.cpp genericdirectuploadservice.h diff --git a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp deleted file mode 100644 index 05c1e3e7269..00000000000 --- a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "abstractuploadandinstallpackageservice.h" - -#include "filetransfer.h" -#include "remotelinuxpackageinstaller.h" - -#include -#include -#include -#include - -#include - -using namespace ProjectExplorer; -using namespace Utils; - -namespace RemoteLinux { -namespace Internal { -namespace { -enum State { Inactive, Uploading, Installing }; -} // anonymous namespace - -class AbstractUploadAndInstallPackageServicePrivate -{ -public: - State state = Inactive; - FileTransfer uploader; - Utils::FilePath packageFilePath; -}; - -} // namespace Internal - -using namespace Internal; - -AbstractUploadAndInstallPackageService::AbstractUploadAndInstallPackageService() - : d(new AbstractUploadAndInstallPackageServicePrivate) -{ - connect(&d->uploader, &FileTransfer::done, this, - &AbstractUploadAndInstallPackageService::handleUploadFinished); - connect(&d->uploader, &FileTransfer::progress, this, - &AbstractUploadAndInstallPackageService::progressMessage); -} - -AbstractUploadAndInstallPackageService::~AbstractUploadAndInstallPackageService() -{ - delete d; -} - -void AbstractUploadAndInstallPackageService::setPackageFilePath(const FilePath &filePath) -{ - d->packageFilePath = filePath; -} - -QString AbstractUploadAndInstallPackageService::uploadDir() const -{ - return QLatin1String("/tmp"); -} - -bool AbstractUploadAndInstallPackageService::isDeploymentNecessary() const -{ - return hasLocalFileChanged(DeployableFile(d->packageFilePath, QString())); -} - -void AbstractUploadAndInstallPackageService::doDeploy() -{ - QTC_ASSERT(d->state == Inactive, return); - - d->state = Uploading; - - const QString remoteFilePath = uploadDir() + QLatin1Char('/') + d->packageFilePath.fileName(); - const FilesToTransfer files {{d->packageFilePath, - deviceConfiguration()->filePath(remoteFilePath)}}; - d->uploader.setDevice(deviceConfiguration()); - d->uploader.setFilesToTransfer(files); - d->uploader.start(); -} - -void AbstractUploadAndInstallPackageService::stopDeployment() -{ - switch (d->state) { - case Inactive: - qWarning("%s: Unexpected state 'Inactive'.", Q_FUNC_INFO); - break; - case Uploading: - d->uploader.stop(); - setFinished(); - break; - case Installing: - packageInstaller()->cancelInstallation(); - setFinished(); - break; - } -} - -void AbstractUploadAndInstallPackageService::handleUploadFinished(const ProcessResultData &resultData) -{ - QTC_ASSERT(d->state == Uploading, return); - - if (resultData.m_error != QProcess::UnknownError) { - emit errorMessage(resultData.m_errorString); - setFinished(); - return; - } - - emit progressMessage(tr("Successfully uploaded package file.")); - const QString remoteFilePath = uploadDir() + '/' + d->packageFilePath.fileName(); - d->state = Installing; - emit progressMessage(tr("Installing package to device...")); - connect(packageInstaller(), &AbstractRemoteLinuxPackageInstaller::stdoutData, - this, &AbstractRemoteLinuxDeployService::stdOutData); - connect(packageInstaller(), &AbstractRemoteLinuxPackageInstaller::stderrData, - this, &AbstractRemoteLinuxDeployService::stdErrData); - connect(packageInstaller(), &AbstractRemoteLinuxPackageInstaller::finished, - this, &AbstractUploadAndInstallPackageService::handleInstallationFinished); - packageInstaller()->installPackage(deviceConfiguration(), remoteFilePath, true); -} - -void AbstractUploadAndInstallPackageService::handleInstallationFinished(const QString &errorMsg) -{ - QTC_ASSERT(d->state == Installing, return); - - if (errorMsg.isEmpty()) { - saveDeploymentTimeStamp(DeployableFile(d->packageFilePath, QString()), QDateTime()); - emit progressMessage(tr("Package installed.")); - } else { - emit errorMessage(errorMsg); - } - setFinished(); -} - -void AbstractUploadAndInstallPackageService::setFinished() -{ - d->state = Inactive; - d->uploader.stop(); - disconnect(packageInstaller(), nullptr, this, nullptr); - handleDeploymentDone(); -} - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h deleted file mode 100644 index 6cfab8d89ad..00000000000 --- a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.h +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "abstractremotelinuxdeployservice.h" -#include "remotelinux_export.h" - -namespace Utils { -class FilePath; -class ProcessResultData; -} - -namespace RemoteLinux { -class AbstractRemoteLinuxPackageInstaller; - -namespace Internal { class AbstractUploadAndInstallPackageServicePrivate; } - -class REMOTELINUX_EXPORT AbstractUploadAndInstallPackageService : public AbstractRemoteLinuxDeployService -{ - Q_OBJECT - -public: - void setPackageFilePath(const Utils::FilePath &filePath); - -protected: - AbstractUploadAndInstallPackageService(); - ~AbstractUploadAndInstallPackageService() override; - -private: - void handleUploadFinished(const Utils::ProcessResultData &resultData); - void handleInstallationFinished(const QString &errorMsg); - - virtual AbstractRemoteLinuxPackageInstaller *packageInstaller() const = 0; - virtual QString uploadDir() const; // Defaults to remote user's home directory. - - bool isDeploymentNecessary() const override; - void doDeploy() override; - void stopDeployment() override; - - void setFinished(); - - Internal::AbstractUploadAndInstallPackageServicePrivate * const d; -}; - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs index d91d412ee3a..2d24a699508 100644 --- a/src/plugins/remotelinux/remotelinux.qbs +++ b/src/plugins/remotelinux/remotelinux.qbs @@ -19,8 +19,6 @@ Project { "abstractremotelinuxdeployservice.h", "abstractremotelinuxdeploystep.cpp", "abstractremotelinuxdeploystep.h", - "abstractuploadandinstallpackageservice.cpp", - "abstractuploadandinstallpackageservice.h", "deploymenttimeinfo.cpp", "deploymenttimeinfo.h", "filetransfer.h", diff --git a/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp b/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp index 06edf9f867c..4fe48cd066a 100644 --- a/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp +++ b/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp @@ -25,43 +25,155 @@ #include "uploadandinstalltarpackagestep.h" +#include "filetransfer.h" #include "remotelinux_constants.h" -#include "remotelinuxdeployconfiguration.h" #include "remotelinuxpackageinstaller.h" #include "tarpackagecreationstep.h" +#include +#include + +#include + +#include + using namespace ProjectExplorer; +using namespace Utils; namespace RemoteLinux { namespace Internal { -class UploadAndInstallTarPackageServicePrivate +class UploadAndInstallTarPackageService : public AbstractRemoteLinuxDeployService { + Q_OBJECT + public: - RemoteLinuxTarPackageInstaller installer; + UploadAndInstallTarPackageService(); + void setPackageFilePath(const FilePath &filePath); + +private: + enum State { Inactive, Uploading, Installing }; + + void handleUploadFinished(const ProcessResultData &resultData); + void handleInstallationFinished(const QString &errorMsg); + + QString uploadDir() const; // Defaults to remote user's home directory. + + bool isDeploymentNecessary() const override; + void doDeploy() override; + void stopDeployment() override; + + void setFinished(); + + State m_state = Inactive; + FileTransfer m_uploader; + FilePath m_packageFilePath; + RemoteLinuxTarPackageInstaller m_installer; }; +UploadAndInstallTarPackageService::UploadAndInstallTarPackageService() +{ + connect(&m_uploader, &FileTransfer::done, this, + &UploadAndInstallTarPackageService::handleUploadFinished); + connect(&m_uploader, &FileTransfer::progress, this, + &UploadAndInstallTarPackageService::progressMessage); +} + +void UploadAndInstallTarPackageService::setPackageFilePath(const FilePath &filePath) +{ + m_packageFilePath = filePath; +} + +QString UploadAndInstallTarPackageService::uploadDir() const +{ + return QLatin1String("/tmp"); +} + +bool UploadAndInstallTarPackageService::isDeploymentNecessary() const +{ + return hasLocalFileChanged(DeployableFile(m_packageFilePath, {})); +} + +void UploadAndInstallTarPackageService::doDeploy() +{ + QTC_ASSERT(m_state == Inactive, return); + + m_state = Uploading; + + const QString remoteFilePath = uploadDir() + QLatin1Char('/') + m_packageFilePath.fileName(); + const FilesToTransfer files {{m_packageFilePath, + deviceConfiguration()->filePath(remoteFilePath)}}; + m_uploader.setDevice(deviceConfiguration()); + m_uploader.setFilesToTransfer(files); + m_uploader.start(); +} + +void UploadAndInstallTarPackageService::stopDeployment() +{ + switch (m_state) { + case Inactive: + qWarning("%s: Unexpected state 'Inactive'.", Q_FUNC_INFO); + break; + case Uploading: + m_uploader.stop(); + setFinished(); + break; + case Installing: + m_installer.cancelInstallation(); + setFinished(); + break; + } +} + +void UploadAndInstallTarPackageService::handleUploadFinished(const ProcessResultData &resultData) +{ + QTC_ASSERT(m_state == Uploading, return); + + if (resultData.m_error != QProcess::UnknownError) { + emit errorMessage(resultData.m_errorString); + setFinished(); + return; + } + + emit progressMessage(tr("Successfully uploaded package file.")); + const QString remoteFilePath = uploadDir() + '/' + m_packageFilePath.fileName(); + m_state = Installing; + emit progressMessage(tr("Installing package to device...")); + connect(&m_installer, &AbstractRemoteLinuxPackageInstaller::stdoutData, + this, &AbstractRemoteLinuxDeployService::stdOutData); + connect(&m_installer, &AbstractRemoteLinuxPackageInstaller::stderrData, + this, &AbstractRemoteLinuxDeployService::stdErrData); + connect(&m_installer, &AbstractRemoteLinuxPackageInstaller::finished, + this, &UploadAndInstallTarPackageService::handleInstallationFinished); + m_installer.installPackage(deviceConfiguration(), remoteFilePath, true); +} + +void UploadAndInstallTarPackageService::handleInstallationFinished(const QString &errorMsg) +{ + QTC_ASSERT(m_state == Installing, return); + + if (errorMsg.isEmpty()) { + saveDeploymentTimeStamp(DeployableFile(m_packageFilePath, {}), {}); + emit progressMessage(tr("Package installed.")); + } else { + emit errorMessage(errorMsg); + } + setFinished(); +} + +void UploadAndInstallTarPackageService::setFinished() +{ + m_state = Inactive; + m_uploader.stop(); + disconnect(&m_installer, nullptr, this, nullptr); + handleDeploymentDone(); +} + } // namespace Internal using namespace Internal; -UploadAndInstallTarPackageService::UploadAndInstallTarPackageService() - : d(new UploadAndInstallTarPackageServicePrivate) -{ -} - -UploadAndInstallTarPackageService::~UploadAndInstallTarPackageService() -{ - delete d; -} - -AbstractRemoteLinuxPackageInstaller *UploadAndInstallTarPackageService::packageInstaller() const -{ - return &d->installer; -} - - -UploadAndInstallTarPackageStep::UploadAndInstallTarPackageStep(BuildStepList *bsl, Utils::Id id) +UploadAndInstallTarPackageStep::UploadAndInstallTarPackageStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { auto service = createDeployService(); @@ -85,7 +197,7 @@ UploadAndInstallTarPackageStep::UploadAndInstallTarPackageStep(BuildStepList *bs }); } -Utils::Id UploadAndInstallTarPackageStep::stepId() +Id UploadAndInstallTarPackageStep::stepId() { return Constants::UploadAndInstallTarPackageStepId; } @@ -96,3 +208,5 @@ QString UploadAndInstallTarPackageStep::displayName() } } //namespace RemoteLinux + +#include "uploadandinstalltarpackagestep.moc" diff --git a/src/plugins/remotelinux/uploadandinstalltarpackagestep.h b/src/plugins/remotelinux/uploadandinstalltarpackagestep.h index f67e768baf4..5d120dc7271 100644 --- a/src/plugins/remotelinux/uploadandinstalltarpackagestep.h +++ b/src/plugins/remotelinux/uploadandinstalltarpackagestep.h @@ -25,29 +25,11 @@ #pragma once -#include "abstractuploadandinstallpackageservice.h" #include "abstractremotelinuxdeploystep.h" namespace RemoteLinux { class AbstractRemoteLinuxPackageInstaller; -namespace Internal { class UploadAndInstallTarPackageServicePrivate; } - -class REMOTELINUX_EXPORT UploadAndInstallTarPackageService : public AbstractUploadAndInstallPackageService -{ - Q_OBJECT - -public: - UploadAndInstallTarPackageService(); - ~UploadAndInstallTarPackageService() override; - -private: - AbstractRemoteLinuxPackageInstaller *packageInstaller() const override; - - Internal::UploadAndInstallTarPackageServicePrivate *d; -}; - - class REMOTELINUX_EXPORT UploadAndInstallTarPackageStep : public AbstractRemoteLinuxDeployStep { Q_OBJECT From faa9ddbcfda596a01d4cd609c06896210134ce9d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 26 May 2022 15:10:06 +0200 Subject: [PATCH 12/58] Hide RemoteLinuxKillAppService There is not need to export this class as it's not used outside. Rename RemoteLinuxKillAppStep into KillAppStep, as the "RemoteLinux" prefix is redundant inside RemoteLinux plugin. Change-Id: I46800c84fd326bb0631b77e48243443d79bc2fb2 Reviewed-by: Qt CI Bot Reviewed-by: Christian Kandeler --- src/plugins/remotelinux/CMakeLists.txt | 3 +- src/plugins/remotelinux/killappstep.cpp | 153 ++++++++++++++++++ ...remotelinuxkillappstep.h => killappstep.h} | 6 +- src/plugins/remotelinux/remotelinux.qbs | 6 +- .../remotelinuxdeployconfiguration.cpp | 4 +- .../remotelinux/remotelinuxkillappservice.cpp | 103 ------------ .../remotelinux/remotelinuxkillappservice.h | 58 ------- .../remotelinux/remotelinuxkillappstep.cpp | 66 -------- src/plugins/remotelinux/remotelinuxplugin.cpp | 4 +- 9 files changed, 163 insertions(+), 240 deletions(-) create mode 100644 src/plugins/remotelinux/killappstep.cpp rename src/plugins/remotelinux/{remotelinuxkillappstep.h => killappstep.h} (87%) delete mode 100644 src/plugins/remotelinux/remotelinuxkillappservice.cpp delete mode 100644 src/plugins/remotelinux/remotelinuxkillappservice.h delete mode 100644 src/plugins/remotelinux/remotelinuxkillappstep.cpp diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt index 6599adac26d..26d9e8da625 100644 --- a/src/plugins/remotelinux/CMakeLists.txt +++ b/src/plugins/remotelinux/CMakeLists.txt @@ -13,6 +13,7 @@ add_qtc_plugin(RemoteLinux genericlinuxdeviceconfigurationwizard.cpp genericlinuxdeviceconfigurationwizard.h genericlinuxdeviceconfigurationwizardpages.cpp genericlinuxdeviceconfigurationwizardpages.h genericlinuxdeviceconfigurationwizardsetuppage.ui + killappstep.cpp killappstep.h linuxdevice.cpp linuxdevice.h linuxdevicetester.cpp linuxdevicetester.h linuxprocessinterface.h @@ -29,8 +30,6 @@ add_qtc_plugin(RemoteLinux remotelinuxenvironmentaspect.cpp remotelinuxenvironmentaspect.h remotelinuxenvironmentaspectwidget.cpp remotelinuxenvironmentaspectwidget.h remotelinuxenvironmentreader.cpp remotelinuxenvironmentreader.h - remotelinuxkillappservice.cpp remotelinuxkillappservice.h - remotelinuxkillappstep.cpp remotelinuxkillappstep.h remotelinuxpackageinstaller.cpp remotelinuxpackageinstaller.h remotelinuxplugin.cpp remotelinuxplugin.h remotelinuxqmltoolingsupport.cpp remotelinuxqmltoolingsupport.h diff --git a/src/plugins/remotelinux/killappstep.cpp b/src/plugins/remotelinux/killappstep.cpp new file mode 100644 index 00000000000..cb5bc06a79a --- /dev/null +++ b/src/plugins/remotelinux/killappstep.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "killappstep.h" + +#include "remotelinux_constants.h" + +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace RemoteLinux { +namespace Internal { + +class KillAppService : public AbstractRemoteLinuxDeployService +{ + Q_OBJECT +public: + ~KillAppService() override; + + void setRemoteExecutable(const QString &filePath); + +private: + void handleStdErr(); + void handleProcessFinished(); + + bool isDeploymentNecessary() const override; + + void doDeploy() override; + void stopDeployment() override; + + void handleSignalOpFinished(const QString &errorMessage); + void cleanup(); + void finishDeployment(); + + QString m_remoteExecutable; + ProjectExplorer::DeviceProcessSignalOperation::Ptr m_signalOperation; +}; + +KillAppService::~KillAppService() +{ + cleanup(); +} + +void KillAppService::setRemoteExecutable(const QString &filePath) +{ + m_remoteExecutable = filePath; +} + +bool KillAppService::isDeploymentNecessary() const +{ + return !m_remoteExecutable.isEmpty(); +} + +void KillAppService::doDeploy() +{ + m_signalOperation = deviceConfiguration()->signalOperation(); + if (!m_signalOperation) { + handleDeploymentDone(); + return; + } + connect(m_signalOperation.data(), &ProjectExplorer::DeviceProcessSignalOperation::finished, + this, &KillAppService::handleSignalOpFinished); + emit progressMessage(tr("Trying to kill \"%1\" on remote device...").arg(m_remoteExecutable)); + m_signalOperation->killProcess(m_remoteExecutable); +} + +void KillAppService::cleanup() +{ + if (m_signalOperation) { + disconnect(m_signalOperation.data(), nullptr, this, nullptr); + m_signalOperation.clear(); + } +} + +void KillAppService::finishDeployment() +{ + cleanup(); + handleDeploymentDone(); +} + +void KillAppService::stopDeployment() +{ + finishDeployment(); +} + +void KillAppService::handleSignalOpFinished(const QString &errorMessage) +{ + if (errorMessage.isEmpty()) + emit progressMessage(tr("Remote application killed.")); + else + emit progressMessage(tr("Failed to kill remote application. Assuming it was not running.")); + finishDeployment(); +} + +} // namespace Internal + +KillAppStep::KillAppStep(BuildStepList *bsl, Id id) + : AbstractRemoteLinuxDeployStep(bsl, id) +{ + auto service = createDeployService(); + + setWidgetExpandedByDefault(false); + + setInternalInitializer([this, service] { + Target * const theTarget = target(); + QTC_ASSERT(theTarget, return CheckResult::failure()); + RunConfiguration * const rc = theTarget->activeRunConfiguration(); + const QString remoteExe = rc ? rc->runnable().command.executable().toString() : QString(); + service->setRemoteExecutable(remoteExe); + return CheckResult::success(); + }); +} + +Id KillAppStep::stepId() +{ + return Constants::KillAppStepId; +} + +QString KillAppStep::displayName() +{ + return tr("Kill current application instance"); +} + +} // namespace RemoteLinux + +#include "killappstep.moc" diff --git a/src/plugins/remotelinux/remotelinuxkillappstep.h b/src/plugins/remotelinux/killappstep.h similarity index 87% rename from src/plugins/remotelinux/remotelinuxkillappstep.h rename to src/plugins/remotelinux/killappstep.h index 506bc99d817..62fbc81643c 100644 --- a/src/plugins/remotelinux/remotelinuxkillappstep.h +++ b/src/plugins/remotelinux/killappstep.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -29,11 +29,11 @@ namespace RemoteLinux { -class REMOTELINUX_EXPORT RemoteLinuxKillAppStep : public AbstractRemoteLinuxDeployStep +class REMOTELINUX_EXPORT KillAppStep : public AbstractRemoteLinuxDeployStep { Q_OBJECT public: - explicit RemoteLinuxKillAppStep(ProjectExplorer::BuildStepList *bsl, + explicit KillAppStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id = stepId()); static Utils::Id stepId(); diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs index 2d24a699508..f6ff0706439 100644 --- a/src/plugins/remotelinux/remotelinux.qbs +++ b/src/plugins/remotelinux/remotelinux.qbs @@ -34,6 +34,8 @@ Project { "genericlinuxdeviceconfigurationwizardpages.cpp", "genericlinuxdeviceconfigurationwizardpages.h", "genericlinuxdeviceconfigurationwizardsetuppage.ui", + "killappstep.cpp", + "killappstep.h", "linuxdevice.cpp", "linuxdevice.h", "linuxdevicetester.cpp", @@ -62,10 +64,6 @@ Project { "remotelinuxenvironmentaspectwidget.h", "remotelinuxenvironmentreader.cpp", "remotelinuxenvironmentreader.h", - "remotelinuxkillappservice.cpp", - "remotelinuxkillappservice.h", - "remotelinuxkillappstep.cpp", - "remotelinuxkillappstep.h", "remotelinuxpackageinstaller.cpp", "remotelinuxpackageinstaller.h", "remotelinuxplugin.cpp", diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp index 8f311a6c424..b248d53f918 100644 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp @@ -28,7 +28,7 @@ #include "genericdirectuploadstep.h" #include "makeinstallstep.h" #include "remotelinuxcheckforfreediskspacestep.h" -#include "remotelinuxkillappstep.h" +#include "killappstep.h" #include "remotelinux_constants.h" #include "rsyncdeploystep.h" @@ -77,7 +77,7 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() addInitialStep(MakeInstallStep::stepId(), needsMakeInstall); addInitialStep(RemoteLinuxCheckForFreeDiskSpaceStep::stepId()); - addInitialStep(RemoteLinuxKillAppStep::stepId()); + addInitialStep(KillAppStep::stepId()); addInitialStep(RsyncDeployStep::stepId(), [](Target *target) { auto device = DeviceKitAspect::device(target->kit()); return device && device->extraData(Constants::SupportsRSync).toBool(); diff --git a/src/plugins/remotelinux/remotelinuxkillappservice.cpp b/src/plugins/remotelinux/remotelinuxkillappservice.cpp deleted file mode 100644 index a6c446d9c59..00000000000 --- a/src/plugins/remotelinux/remotelinuxkillappservice.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "remotelinuxkillappservice.h" - -#include -#include - -namespace RemoteLinux { -namespace Internal { -class RemoteLinuxKillAppServicePrivate -{ -public: - QString remoteExecutable; - ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOp; -}; -} // namespace Internal - -RemoteLinuxKillAppService::RemoteLinuxKillAppService() - : d(new Internal::RemoteLinuxKillAppServicePrivate) -{ -} - -RemoteLinuxKillAppService::~RemoteLinuxKillAppService() -{ - cleanup(); - delete d; -} - -void RemoteLinuxKillAppService::setRemoteExecutable(const QString &filePath) -{ - d->remoteExecutable = filePath; -} - -bool RemoteLinuxKillAppService::isDeploymentNecessary() const -{ - return !d->remoteExecutable.isEmpty(); -} - -void RemoteLinuxKillAppService::doDeploy() -{ - d->signalOp = deviceConfiguration()->signalOperation(); - if (!d->signalOp) { - handleDeploymentDone(); - return; - } - connect(d->signalOp.data(), &ProjectExplorer::DeviceProcessSignalOperation::finished, - this, &RemoteLinuxKillAppService::handleSignalOpFinished); - emit progressMessage(tr("Trying to kill \"%1\" on remote device...").arg(d->remoteExecutable)); - d->signalOp->killProcess(d->remoteExecutable); -} - -void RemoteLinuxKillAppService::cleanup() -{ - if (d->signalOp) { - disconnect(d->signalOp.data(), nullptr, this, nullptr); - d->signalOp.clear(); - } -} - -void RemoteLinuxKillAppService::finishDeployment() -{ - cleanup(); - handleDeploymentDone(); -} - -void RemoteLinuxKillAppService::stopDeployment() -{ - finishDeployment(); -} - -void RemoteLinuxKillAppService::handleSignalOpFinished(const QString &errorMessage) -{ - if (errorMessage.isEmpty()) - emit progressMessage(tr("Remote application killed.")); - else - emit progressMessage(tr("Failed to kill remote application. Assuming it was not running.")); - finishDeployment(); -} - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/remotelinuxkillappservice.h b/src/plugins/remotelinux/remotelinuxkillappservice.h deleted file mode 100644 index 042f4bac0ea..00000000000 --- a/src/plugins/remotelinux/remotelinuxkillappservice.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "abstractremotelinuxdeployservice.h" - -namespace RemoteLinux { -namespace Internal { class RemoteLinuxKillAppServicePrivate; } - -class REMOTELINUX_EXPORT RemoteLinuxKillAppService : public AbstractRemoteLinuxDeployService -{ - Q_OBJECT -public: - RemoteLinuxKillAppService(); - ~RemoteLinuxKillAppService() override; - - void setRemoteExecutable(const QString &filePath); - -private: - void handleStdErr(); - void handleProcessFinished(); - - bool isDeploymentNecessary() const override; - - void doDeploy() override; - void stopDeployment() override; - - void handleSignalOpFinished(const QString &errorMessage); - void cleanup(); - void finishDeployment(); - - Internal::RemoteLinuxKillAppServicePrivate * const d; -}; - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/remotelinuxkillappstep.cpp b/src/plugins/remotelinux/remotelinuxkillappstep.cpp deleted file mode 100644 index daa451d97e0..00000000000 --- a/src/plugins/remotelinux/remotelinuxkillappstep.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "remotelinuxkillappstep.h" - -#include "remotelinux_constants.h" -#include "remotelinuxkillappservice.h" - -#include -#include -#include - -using namespace ProjectExplorer; - -namespace RemoteLinux { - -RemoteLinuxKillAppStep::RemoteLinuxKillAppStep(BuildStepList *bsl, Utils::Id id) - : AbstractRemoteLinuxDeployStep(bsl, id) -{ - auto service = createDeployService(); - - setWidgetExpandedByDefault(false); - - setInternalInitializer([this, service] { - Target * const theTarget = target(); - QTC_ASSERT(theTarget, return CheckResult::failure()); - RunConfiguration * const rc = theTarget->activeRunConfiguration(); - const QString remoteExe = rc ? rc->runnable().command.executable().toString() : QString(); - service->setRemoteExecutable(remoteExe); - return CheckResult::success(); - }); -} - -Utils::Id RemoteLinuxKillAppStep::stepId() -{ - return Constants::KillAppStepId; -} - -QString RemoteLinuxKillAppStep::displayName() -{ - return tr("Kill current application instance"); -} - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp index ba3286be0ba..86a55970d27 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.cpp +++ b/src/plugins/remotelinux/remotelinuxplugin.cpp @@ -38,7 +38,7 @@ #include "remotelinuxcheckforfreediskspacestep.h" #include "remotelinuxdeployconfiguration.h" #include "remotelinuxcustomcommanddeploymentstep.h" -#include "remotelinuxkillappstep.h" +#include "killappstep.h" #include "rsyncdeploystep.h" #include "tarpackagecreationstep.h" #include "uploadandinstalltarpackagestep.h" @@ -83,7 +83,7 @@ public: customCommandDeploymentStepFactory; GenericDeployStepFactory checkForFreeDiskSpaceStepFactory; - GenericDeployStepFactory remoteLinuxKillAppStepFactory; + GenericDeployStepFactory killAppStepFactory; GenericDeployStepFactory makeInstallStepFactory; const QList supportedRunConfigs { From ea47190ed6164580dfecd094b114f136dfb337b2 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 30 May 2022 10:49:07 +0200 Subject: [PATCH 13/58] Debugger: Use arm64 cdb only on Arm64 host Otherwise on x86/x64 there will be a machine type mismatch warning displayed. Amends 21ed15fcc930738116f26ab4c230702e319df7af Change-Id: I7faee1a6f9eaa7abdabcd92a26de763b27a0e085 Reviewed-by: David Schulz Reviewed-by: --- src/plugins/debugger/debuggeritemmanager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index 9efc8df4a25..4f0d4702589 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -669,7 +669,9 @@ void DebuggerItemManagerPrivate::autoDetectCdbDebuggers() for (const QFileInfo &kitFolderFi : kitFolders) { const QString path = kitFolderFi.absoluteFilePath(); - const QStringList abis = {"x86", "x64", "arm64"}; + QStringList abis = {"x86", "x64"}; + if (HostOsInfo::hostArchitecture() == HostOsInfo::HostArchitectureArm) + abis << "arm64"; for (const QString &abi: abis) { const QFileInfo cdbBinary(path + "/Debuggers/" + abi + "/cdb.exe"); if (cdbBinary.isExecutable()) From 48e2f79e8b65994e787ebc24b540ad7485ec1ff5 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Wed, 25 May 2022 15:46:50 +0200 Subject: [PATCH 14/58] SdkTool: Remove foreach / Q_FOREACH usage Task-number: QTCREATORBUG-27464 Change-Id: I231597f60c7ddad00f7166e7d09c1c5b2ff48657 Reviewed-by: hjk Reviewed-by: --- src/tools/sdktool/addcmakeoperation.cpp | 4 ++-- src/tools/sdktool/adddebuggeroperation.cpp | 6 +++--- src/tools/sdktool/adddeviceoperation.cpp | 4 ++-- src/tools/sdktool/addkeysoperation.cpp | 2 +- src/tools/sdktool/addkitoperation.cpp | 2 +- src/tools/sdktool/addqtoperation.cpp | 6 +++--- src/tools/sdktool/addtoolchainoperation.cpp | 8 ++++---- src/tools/sdktool/findvalueoperation.cpp | 4 ++-- src/tools/sdktool/getoperation.cpp | 6 +++--- src/tools/sdktool/operation.cpp | 2 +- src/tools/sdktool/rmkeysoperation.cpp | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/tools/sdktool/addcmakeoperation.cpp b/src/tools/sdktool/addcmakeoperation.cpp index 7a7304bc282..9b89433a42b 100644 --- a/src/tools/sdktool/addcmakeoperation.cpp +++ b/src/tools/sdktool/addcmakeoperation.cpp @@ -229,7 +229,7 @@ QVariantMap AddCMakeData::addCMake(const QVariantMap &map) const data << KeyValuePair({cm, AUTODETECTED_KEY}, QVariant(true)); data << KeyValuePair({cm, PATH_KEY}, Utils::FilePath::fromUserInput(m_path).toVariant()); KeyValuePairList extraList; - foreach (const KeyValuePair &pair, m_extra) + for (const KeyValuePair &pair : qAsConst(m_extra)) extraList << KeyValuePair(QStringList({cm}) << pair.key, pair.value); data.append(extraList); data << KeyValuePair(COUNT, QVariant(count + 1)); @@ -251,7 +251,7 @@ bool AddCMakeData::exists(const QVariantMap &map, const QString &id) // support old settings using QByteArray for id's valueKeys.append(FindValueOperation::findValue(map, id.toUtf8())); - foreach (const QString &k, valueKeys) { + for (const QString &k : qAsConst(valueKeys)) { if (k.endsWith(QString('/') + ID_KEY)) { return true; } diff --git a/src/tools/sdktool/adddebuggeroperation.cpp b/src/tools/sdktool/adddebuggeroperation.cpp index 9adfc6d89d2..24020003e46 100644 --- a/src/tools/sdktool/adddebuggeroperation.cpp +++ b/src/tools/sdktool/adddebuggeroperation.cpp @@ -208,9 +208,9 @@ void AddDebuggerOperation::unittest() QVariantMap AddDebuggerData::addDebugger(const QVariantMap &map) const { // Sanity check: Make sure autodetection source is not in use already: - QStringList valueKeys = FindValueOperation::findValue(map, QVariant(m_id)); + const QStringList valueKeys = FindValueOperation::findValue(map, QVariant(m_id)); bool hasId = false; - foreach (const QString &k, valueKeys) { + for (const QString &k : valueKeys) { if (k.endsWith(QString(QLatin1Char('/')) + QLatin1String(ID))) { hasId = true; break; @@ -250,7 +250,7 @@ QVariantMap AddDebuggerData::addDebugger(const QVariantMap &map) const data << KeyValuePair(QStringList() << QLatin1String(COUNT), QVariant(count + 1)); KeyValuePairList qtExtraList; - foreach (const KeyValuePair &pair, m_extra) + for (const KeyValuePair &pair : qAsConst(m_extra)) qtExtraList << KeyValuePair(QStringList() << debugger << pair.key, pair.value); data.append(qtExtraList); diff --git a/src/tools/sdktool/adddeviceoperation.cpp b/src/tools/sdktool/adddeviceoperation.cpp index 002932f7e9f..d20636fb33e 100644 --- a/src/tools/sdktool/adddeviceoperation.cpp +++ b/src/tools/sdktool/adddeviceoperation.cpp @@ -412,8 +412,8 @@ bool AddDeviceData::exists(const QVariantMap &map, const QString &id) if (id == QLatin1String(INTERNAL_DSEKTOP_DEVICE_ID)) return true; QVariantMap dmMap = map.value(QLatin1String(DEVICEMANAGER_ID)).toMap(); - QVariantList devList = dmMap.value(QLatin1String(DEVICE_LIST_ID)).toList(); - foreach (const QVariant &dev, devList) { + const QVariantList devList = dmMap.value(QLatin1String(DEVICE_LIST_ID)).toList(); + for (const QVariant &dev : devList) { QVariantMap devData = dev.toMap(); QString current = devData.value(QLatin1String(DEVICE_ID_ID)).toString(); if (current == id) diff --git a/src/tools/sdktool/addkeysoperation.cpp b/src/tools/sdktool/addkeysoperation.cpp index 5f471dfc3ed..2d100f315e4 100644 --- a/src/tools/sdktool/addkeysoperation.cpp +++ b/src/tools/sdktool/addkeysoperation.cpp @@ -229,7 +229,7 @@ QVariantMap AddKeysData::addKeys(const QVariantMap &map) const // Insert data: QVariantMap result = map; - foreach (const KeyValuePair &p, m_data) { + for (const KeyValuePair &p : m_data) { QList stack; // Set up a stack of QVariantMaps along the path we take: diff --git a/src/tools/sdktool/addkitoperation.cpp b/src/tools/sdktool/addkitoperation.cpp index 3fbed7a74a3..2befb340c4a 100644 --- a/src/tools/sdktool/addkitoperation.cpp +++ b/src/tools/sdktool/addkitoperation.cpp @@ -737,7 +737,7 @@ QVariantMap AddKitData::addKit(const QVariantMap &map, data << KeyValuePair(COUNT, QVariant(count + 1)); KeyValuePairList qtExtraList; - foreach (const KeyValuePair &pair, m_extra) + for (const KeyValuePair &pair : qAsConst(m_extra)) qtExtraList << KeyValuePair(QStringList() << kit << pair.key, pair.value); data.append(qtExtraList); diff --git a/src/tools/sdktool/addqtoperation.cpp b/src/tools/sdktool/addqtoperation.cpp index 1c4ff7d7808..9a3e8c79496 100644 --- a/src/tools/sdktool/addqtoperation.cpp +++ b/src/tools/sdktool/addqtoperation.cpp @@ -318,7 +318,7 @@ QVariantMap AddQtData::addQt(const QVariantMap &map) const data << KeyValuePair(QStringList() << qt << ABIS, QVariant(m_abis)); KeyValuePairList qtExtraList; - foreach (const KeyValuePair &pair, m_extra) + for (const KeyValuePair &pair : qAsConst(m_extra)) qtExtraList << KeyValuePair(QStringList() << qt << pair.key, pair.value); data.append(qtExtraList); @@ -343,8 +343,8 @@ bool AddQtData::exists(const QVariantMap &map, const QString &id) QString sdkId = extendId(id); // Sanity check: Make sure autodetection source is not in use already: - QStringList valueKeys = FindValueOperation::findValue(map, sdkId); - foreach (const QString &k, valueKeys) { + const QStringList valueKeys = FindValueOperation::findValue(map, sdkId); + for (const QString &k : valueKeys) { if (k.endsWith(QString(QLatin1Char('/')) + QLatin1String(AUTODETECTION_SOURCE))) return true; } diff --git a/src/tools/sdktool/addtoolchainoperation.cpp b/src/tools/sdktool/addtoolchainoperation.cpp index 50991259f55..f5ee2c51f49 100644 --- a/src/tools/sdktool/addtoolchainoperation.cpp +++ b/src/tools/sdktool/addtoolchainoperation.cpp @@ -308,12 +308,12 @@ QVariantMap AddToolChainData::addToolChain(const QVariantMap &map) const data << KeyValuePair({tc, PATH}, Utils::FilePath::fromUserInput(m_path).toVariant()); data << KeyValuePair({tc, TARGET_ABI}, QVariant(m_targetAbi)); QVariantList abis; - QStringList abiStrings = m_supportedAbis.split(','); - foreach (const QString &s, abiStrings) + const QStringList abiStrings = m_supportedAbis.split(','); + for (const QString &s : abiStrings) abis << QVariant(s); data << KeyValuePair({tc, SUPPORTED_ABIS}, QVariant(abis)); KeyValuePairList tcExtraList; - foreach (const KeyValuePair &pair, m_extra) + for (const KeyValuePair &pair : qAsConst(m_extra)) tcExtraList << KeyValuePair(QStringList({tc}) << pair.key, pair.value); data.append(tcExtraList); data << KeyValuePair(COUNT, QVariant(count + 1)); @@ -335,7 +335,7 @@ bool AddToolChainData::exists(const QVariantMap &map, const QString &id) // support old settings using QByteArray for id's valueKeys.append(FindValueOperation::findValue(map, id.toUtf8())); - foreach (const QString &k, valueKeys) { + for (const QString &k : qAsConst(valueKeys)) { if (k.endsWith(QString('/') + ID)) { return true; } diff --git a/src/tools/sdktool/findvalueoperation.cpp b/src/tools/sdktool/findvalueoperation.cpp index 913fc27a42c..51d1139454a 100644 --- a/src/tools/sdktool/findvalueoperation.cpp +++ b/src/tools/sdktool/findvalueoperation.cpp @@ -82,9 +82,9 @@ int FindValueOperation::execute() const Q_ASSERT(!m_values.isEmpty()); QVariantMap map = load(m_file); - foreach (const QVariant &v, m_values) { + for (const QVariant &v : qAsConst(m_values)) { const QStringList result = findValue(map, v); - foreach (const QString &r, result) + for (const QString &r : result) std::cout << qPrintable(r) << std::endl; } diff --git a/src/tools/sdktool/getoperation.cpp b/src/tools/sdktool/getoperation.cpp index 335d34529d9..5f8692f756e 100644 --- a/src/tools/sdktool/getoperation.cpp +++ b/src/tools/sdktool/getoperation.cpp @@ -91,10 +91,10 @@ static QString toString(const QVariant &variant, int indentation = 0) return res; } case QVariant::List: { - QVariantList list = variant.toList(); + const QVariantList list = variant.toList(); QString res; int counter = 0; - foreach (const QVariant &item, list) + for (const QVariant &item : list) res += indent + QString::number(counter++) + QLatin1String(":\n") + toString(item, indentation + 1); return res; } @@ -108,7 +108,7 @@ int GetOperation::execute() const Q_ASSERT(!m_keys.isEmpty()); QVariantMap map = load(m_file); - foreach (const QString &key, m_keys) { + for (const QString &key : qAsConst(m_keys)) { const QVariant result = get(map, key); if (!result.isValid()) std::cout << "" << std::endl; diff --git a/src/tools/sdktool/operation.cpp b/src/tools/sdktool/operation.cpp index 4c1eec03d96..cb6b1d66eb1 100644 --- a/src/tools/sdktool/operation.cpp +++ b/src/tools/sdktool/operation.cpp @@ -55,7 +55,7 @@ QVariant valueFromString(const QString &v) } else if (type == QLatin1String("QVariantList")) { QVariantList list; const QStringList elements = value.split(QLatin1Char(',')); - foreach (const QString &e, elements) + for (const QString &e : elements) list << QVariant(e); return QVariant(list); } diff --git a/src/tools/sdktool/rmkeysoperation.cpp b/src/tools/sdktool/rmkeysoperation.cpp index 9c7f282790e..e567b3baa68 100644 --- a/src/tools/sdktool/rmkeysoperation.cpp +++ b/src/tools/sdktool/rmkeysoperation.cpp @@ -172,7 +172,7 @@ QVariantMap RmKeysOperation::rmKeys(const QVariantMap &map, const QStringList &r { QVariantMap result = map; - foreach (const QString &r, removals) { + for (const QString &r : removals) { QList stack; const QStringList keys = r.split(QLatin1Char('/')); From 7d848364ffe9b920bbe0c62ed04c15a22f398eee Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 20 May 2022 17:53:20 +0200 Subject: [PATCH 15/58] Move FileTransfer into ProjectExplorer plugin Make it ready for providing implementations for other devices. Change-Id: I14eaf167a7b2c1189f4d23f2e9f556204295b9b3 Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/CMakeLists.txt | 2 + .../devicesupport/filetransfer.cpp | 227 ++++++++++++ .../devicesupport}/filetransfer.h | 36 +- .../devicesupport/filetransferinterface.h | 93 +++++ .../projectexplorer/devicesupport/idevice.cpp | 8 + .../projectexplorer/devicesupport/idevice.h | 4 + .../projectexplorer/projectexplorer.qbs | 2 + src/plugins/remotelinux/CMakeLists.txt | 1 - .../remotelinux/filesystemaccess_test.cpp | 5 +- .../genericdirectuploadservice.cpp | 4 +- src/plugins/remotelinux/linuxdevice.cpp | 342 ++++-------------- src/plugins/remotelinux/linuxdevice.h | 4 +- src/plugins/remotelinux/linuxdevicetester.cpp | 5 +- src/plugins/remotelinux/linuxdevicetester.h | 5 +- src/plugins/remotelinux/remotelinux.qbs | 1 - src/plugins/remotelinux/rsyncdeploystep.cpp | 5 +- .../uploadandinstalltarpackagestep.cpp | 3 +- 17 files changed, 425 insertions(+), 322 deletions(-) create mode 100644 src/plugins/projectexplorer/devicesupport/filetransfer.cpp rename src/plugins/{remotelinux => projectexplorer/devicesupport}/filetransfer.h (74%) create mode 100644 src/plugins/projectexplorer/devicesupport/filetransferinterface.h diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 94e26e39432..23f8e926372 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -60,6 +60,8 @@ add_qtc_plugin(ProjectExplorer devicesupport/devicesettingswidget.cpp devicesupport/devicesettingswidget.h devicesupport/devicesettingswidget.ui devicesupport/devicetestdialog.cpp devicesupport/devicetestdialog.h devicesupport/devicetestdialog.ui devicesupport/deviceusedportsgatherer.cpp devicesupport/deviceusedportsgatherer.h + devicesupport/filetransfer.cpp devicesupport/filetransfer.h + devicesupport/filetransferinterface.h devicesupport/idevice.cpp devicesupport/idevice.h devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h devicesupport/idevicefwd.h diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp new file mode 100644 index 00000000000..e16db6bc08b --- /dev/null +++ b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "filetransfer.h" + +#include "devicemanager.h" +#include "idevice.h" + +#include +#include + +#include + +using namespace Utils; + +namespace ProjectExplorer { + +FileTransferDirection FileToTransfer::direction() const +{ + if (m_source.needsDevice() == m_target.needsDevice()) + return FileTransferDirection::Invalid; + return m_source.needsDevice() ? FileTransferDirection::Download : FileTransferDirection::Upload; +} + +QString FileTransferSetupData::defaultRsyncFlags() +{ + return "-av"; +} + +static FileTransferDirection transferDirection(const FilesToTransfer &files) +{ + if (files.isEmpty()) + return FileTransferDirection::Invalid; + + const FileTransferDirection direction = files.first().direction(); + for (const FileToTransfer &file : files) { + if (file.direction() != direction) + return FileTransferDirection::Invalid; + } + return direction; +} + +static const FilePath &remoteFile(FileTransferDirection direction, const FileToTransfer &file) +{ + return direction == FileTransferDirection::Upload ? file.m_target : file.m_source; +} + +static bool isSameDevice(const FilePath &first, const FilePath &second) +{ + return (first.scheme() == second.scheme()) && (first.host() == second.host()); +} + +static IDeviceConstPtr matchedDevice(FileTransferDirection direction, const FilesToTransfer &files) +{ + if (files.isEmpty()) + return {}; + const FilePath &filePath = remoteFile(direction, files.first()); + for (const FileToTransfer &file : files) { + if (!isSameDevice(filePath, remoteFile(direction, file))) + return {}; + } + return DeviceManager::deviceForPath(filePath); +} + +void FileTransferInterface::startFailed(const QString &errorString) +{ + emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString}); +} + +class FileTransferPrivate : public QObject +{ + Q_OBJECT + +public: + void test(const ProjectExplorer::IDeviceConstPtr &onDevice); + void start(); + void stop(); + + FileTransferSetupData m_setup; + +signals: + void progress(const QString &progressMessage); + void done(const ProcessResultData &resultData); + +private: + void startFailed(const QString &errorString); + void run(const FileTransferSetupData &setup, const IDeviceConstPtr &device); + + std::unique_ptr m_transfer; +}; + +void FileTransferPrivate::test(const IDeviceConstPtr &onDevice) +{ + if (!onDevice) + return startFailed(tr("No device set for test transfer.")); + + run({{}, m_setup.m_method, m_setup.m_rsyncFlags}, onDevice); +} + +void FileTransferPrivate::start() +{ + if (m_setup.m_files.isEmpty()) + return startFailed(tr("No files to transfer.")); + + const FileTransferDirection direction = transferDirection(m_setup.m_files); + if (direction == FileTransferDirection::Invalid) + return startFailed(tr("Mixing different types of transfer in one go.")); + + const IDeviceConstPtr device = matchedDevice(direction, m_setup.m_files); + if (!device) + return startFailed(tr("Trying to transfer into / from not matching device.")); + + run(m_setup, device); +} + +void FileTransferPrivate::stop() +{ + if (!m_transfer) + return; + m_transfer->disconnect(); + m_transfer.release()->deleteLater(); +} + +void FileTransferPrivate::run(const FileTransferSetupData &setup, const IDeviceConstPtr &device) +{ + stop(); + + m_transfer.reset(device->createFileTransferInterface(setup)); + QTC_ASSERT(m_transfer, startFailed(tr("Missing transfer implementation.")); return); + + m_transfer->setParent(this); + connect(m_transfer.get(), &FileTransferInterface::progress, + this, &FileTransferPrivate::progress); + connect(m_transfer.get(), &FileTransferInterface::done, + this, &FileTransferPrivate::done); + m_transfer->start(); +} + +void FileTransferPrivate::startFailed(const QString &errorString) +{ + emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString}); +} + +FileTransfer::FileTransfer() + : d(new FileTransferPrivate) +{ + d->setParent(this); + connect(d, &FileTransferPrivate::progress, this, &FileTransfer::progress); + connect(d, &FileTransferPrivate::done, this, &FileTransfer::done); +} + +FileTransfer::~FileTransfer() +{ + stop(); + delete d; +} + +void FileTransfer::setFilesToTransfer(const FilesToTransfer &files) +{ + d->m_setup.m_files = files; +} + +void FileTransfer::setTransferMethod(FileTransferMethod method) +{ + d->m_setup.m_method = method; +} + +void FileTransfer::setRsyncFlags(const QString &flags) +{ + d->m_setup.m_rsyncFlags = flags; +} + +void FileTransfer::test(const ProjectExplorer::IDeviceConstPtr &onDevice) +{ + d->test(onDevice); +} + +FileTransferMethod FileTransfer::transferMethod() const +{ + return d->m_setup.m_method; +} + +void FileTransfer::start() +{ + d->start(); +} + +void FileTransfer::stop() +{ + d->stop(); +} + +QString FileTransfer::transferMethodName(FileTransferMethod method) +{ + switch (method) { + case FileTransferMethod::Sftp: return FileTransfer::tr("sftp"); + case FileTransferMethod::Rsync: return FileTransfer::tr("rsync"); + } + QTC_CHECK(false); + return {}; +} + +} // namespace ProjectExplorer + +#include "filetransfer.moc" diff --git a/src/plugins/remotelinux/filetransfer.h b/src/plugins/projectexplorer/devicesupport/filetransfer.h similarity index 74% rename from src/plugins/remotelinux/filetransfer.h rename to src/plugins/projectexplorer/devicesupport/filetransfer.h index 9459bac1277..a40de415ded 100644 --- a/src/plugins/remotelinux/filetransfer.h +++ b/src/plugins/projectexplorer/devicesupport/filetransfer.h @@ -25,33 +25,17 @@ #pragma once -#include "remotelinux_export.h" - -#include - -#include +#include "../projectexplorer_export.h" +#include "filetransferinterface.h" +#include "idevicefwd.h" namespace Utils { class ProcessResultData; } -namespace RemoteLinux { - -class REMOTELINUX_EXPORT FileToTransfer -{ -public: - Utils::FilePath m_source; - Utils::FilePath m_target; -}; -using FilesToTransfer = QList; - -enum class FileTransferMethod { - Sftp, - Rsync, - Default = Sftp -}; +namespace ProjectExplorer { class FileTransferPrivate; -class REMOTELINUX_EXPORT FileTransfer : public QObject +class PROJECTEXPLORER_EXPORT FileTransfer : public QObject { Q_OBJECT @@ -59,19 +43,17 @@ public: FileTransfer(); ~FileTransfer(); - void setDevice(const ProjectExplorer::IDeviceConstPtr &device); - void setTransferMethod(FileTransferMethod method); void setFilesToTransfer(const FilesToTransfer &files); + void setTransferMethod(FileTransferMethod method); void setRsyncFlags(const QString &flags); FileTransferMethod transferMethod() const; - void test(); + void test(const ProjectExplorer::IDeviceConstPtr &onDevice); void start(); void stop(); static QString transferMethodName(FileTransferMethod method); - static QString defaultRsyncFlags(); signals: void progress(const QString &progressMessage); @@ -81,6 +63,4 @@ private: FileTransferPrivate *d; }; -} // namespace RemoteLinux - -Q_DECLARE_METATYPE(RemoteLinux::FileTransferMethod) +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h new file mode 100644 index 00000000000..530f2868c7d --- /dev/null +++ b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "../projectexplorer_export.h" + +#include + +namespace Utils { class ProcessResultData; } + +namespace ProjectExplorer { + +enum class FileTransferDirection { + Invalid, + Upload, + Download +}; + +enum class FileTransferMethod { + Sftp, + Rsync, + Default = Sftp +}; + +class PROJECTEXPLORER_EXPORT FileToTransfer +{ +public: + Utils::FilePath m_source; + Utils::FilePath m_target; + + FileTransferDirection direction() const; +}; + +using FilesToTransfer = QList; + +class PROJECTEXPLORER_EXPORT FileTransferSetupData +{ +public: + FilesToTransfer m_files; // When empty, do test instead of a real transfer + FileTransferMethod m_method = FileTransferMethod::Default; + QString m_rsyncFlags = defaultRsyncFlags(); + + static QString defaultRsyncFlags(); +}; + +class PROJECTEXPLORER_EXPORT FileTransferInterface : public QObject +{ + Q_OBJECT + +signals: + void progress(const QString &progressMessage); + void done(const Utils::ProcessResultData &resultData); + +protected: + FileTransferInterface(const FileTransferSetupData &setupData) + : m_setup(setupData) {} + + void startFailed(const QString &errorString); + + const FileTransferSetupData m_setup; + +private: + FileTransferInterface() = delete; + + virtual void start() = 0; + + friend class FileTransferPrivate; +}; + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 2d63f6bbc55..69344b44a2c 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -434,6 +434,14 @@ ProcessInterface *IDevice::createProcessInterface() const return nullptr; } +FileTransferInterface *IDevice::createFileTransferInterface( + const FileTransferSetupData &setup) const +{ + Q_UNUSED(setup) + QTC_CHECK(false); + return nullptr; +} + Environment IDevice::systemEnvironment() const { QTC_CHECK(false); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 132e8c27194..22e125ddd9d 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -60,6 +60,8 @@ class QtcProcess; namespace ProjectExplorer { class DeviceProcessList; +class FileTransferInterface; +class FileTransferSetupData; class Kit; class SshParameters; class Task; @@ -265,6 +267,8 @@ public: virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const; virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const; virtual Utils::ProcessInterface *createProcessInterface() const; + virtual FileTransferInterface *createFileTransferInterface( + const FileTransferSetupData &setup) const; virtual Utils::Environment systemEnvironment() const; virtual qint64 fileSize(const Utils::FilePath &filePath) const; virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const; diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 4e89ad84d9b..1843cf63719 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -219,6 +219,8 @@ Project { "devicesettingswidget.cpp", "devicesettingswidget.h", "devicesettingswidget.ui", "devicetestdialog.cpp", "devicetestdialog.h", "devicetestdialog.ui", "deviceusedportsgatherer.cpp", "deviceusedportsgatherer.h", + "filetransfer.cpp", "filetransfer.h", + "filetransferinterface.h", "idevice.cpp", "idevice.h", "idevicefactory.cpp", "idevicefactory.h", "idevicefwd.h", diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt index 26d9e8da625..1572a39d54d 100644 --- a/src/plugins/remotelinux/CMakeLists.txt +++ b/src/plugins/remotelinux/CMakeLists.txt @@ -6,7 +6,6 @@ add_qtc_plugin(RemoteLinux abstractremotelinuxdeployservice.cpp abstractremotelinuxdeployservice.h abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h deploymenttimeinfo.cpp deploymenttimeinfo.h - filetransfer.h genericdirectuploadservice.cpp genericdirectuploadservice.h genericdirectuploadstep.cpp genericdirectuploadstep.h genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h genericlinuxdeviceconfigurationwidget.ui diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp index ca27238b997..c0bb80cf995 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.cpp +++ b/src/plugins/remotelinux/filesystemaccess_test.cpp @@ -25,10 +25,10 @@ #include "filesystemaccess_test.h" -#include "filetransfer.h" #include "linuxdevice.h" #include +#include #include #include #include @@ -39,6 +39,8 @@ #include #include +Q_DECLARE_METATYPE(ProjectExplorer::FileTransferMethod) + using namespace ProjectExplorer; using namespace Utils; @@ -230,7 +232,6 @@ void FileSystemAccessTest::testFileTransfer() FileTransfer fileTransfer; fileTransfer.setTransferMethod(fileTransferMethod); - fileTransfer.setDevice(m_device); // Create and upload 1000 small files and one big file QTemporaryDir dirForFilesToUpload; diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp index 6743657c0d3..c784e346b9a 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.cpp +++ b/src/plugins/remotelinux/genericdirectuploadservice.cpp @@ -25,9 +25,8 @@ #include "genericdirectuploadservice.h" -#include "filetransfer.h" - #include +#include #include #include #include @@ -310,7 +309,6 @@ void GenericDirectUploadService::uploadFiles() deviceConfiguration()->filePath(file.remoteFilePath())}); } - d->uploader.setDevice(deviceConfiguration()); d->uploader.setFilesToTransfer(files); d->uploader.start(); } diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 8183039ad4e..b2982feb109 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -25,7 +25,6 @@ #include "linuxdevice.h" -#include "filetransfer.h" #include "genericlinuxdeviceconfigurationwidget.h" #include "genericlinuxdeviceconfigurationwizard.h" #include "linuxdevicetester.h" @@ -39,6 +38,8 @@ #include #include +#include +#include #include #include #include @@ -1417,47 +1418,6 @@ bool LinuxDevice::writeFileContents(const FilePath &filePath, const QByteArray & return d->runInShell({"dd", {"of=" + filePath.path()}}, data); } -enum class TransferDirection { - Upload, - Download, - Invalid -}; - -static TransferDirection transferDirection(const FileToTransfer &file) -{ - if (file.m_source.needsDevice() == file.m_target.needsDevice()) - return TransferDirection::Invalid; - return file.m_source.needsDevice() ? TransferDirection::Download : TransferDirection::Upload; -} - -static TransferDirection transferDirection(const FilesToTransfer &files) -{ - if (files.isEmpty()) - return TransferDirection::Invalid; - const TransferDirection direction = transferDirection(files.first()); - for (const FileToTransfer &file : files) { - if (transferDirection(file) != direction) - return TransferDirection::Invalid; - } - return direction; -} - -static bool isDeviceMatched(const FilePath &file, const QString &id) -{ - return (file.scheme() == "device") && (file.host() == id); -} - -static bool isDeviceMatched(const FilesToTransfer &files, const QString &id) -{ - for (const FileToTransfer &file : files) { - if (transferDirection(file) == TransferDirection::Upload && !isDeviceMatched(file.m_target, id)) - return false; - if (transferDirection(file) == TransferDirection::Download && !isDeviceMatched(file.m_source, id)) - return false; - } - return true; -} - static FilePaths dirsToCreate(const FilesToTransfer &files) { FilePaths dirs; @@ -1474,77 +1434,33 @@ static FilePaths dirsToCreate(const FilesToTransfer &files) return dirs; } -static QByteArray transferCommand(const TransferDirection transferDirection, bool link) +static QByteArray transferCommand(const FileTransferDirection direction, bool link) { - if (transferDirection == TransferDirection::Upload) + if (direction == FileTransferDirection::Upload) return link ? "ln -s" : "put"; - if (transferDirection == TransferDirection::Download) + if (direction == FileTransferDirection::Download) return "get"; return {}; } -class FileTransferInterface : public QObject +class SshTransferInterface : public FileTransferInterface { Q_OBJECT -public: - void setDevice(const ProjectExplorer::IDeviceConstPtr &device) - { - m_device = device; - m_devicePrivate = nullptr; - if (m_device) { - const LinuxDevice *linuxDevice = m_device.dynamicCast().get(); - QTC_ASSERT(linuxDevice, return); - m_devicePrivate = linuxDevice->d; - } - } - void setFilesToTransfer(const FilesToTransfer &files, TransferDirection direction) - { - m_files = files; - m_direction = direction; - } - - void start() - { - if (!m_devicePrivate) { - startFailed(tr("Transferring files to/from non-linux device " - "isn't supported currently.")); - return; - } - - m_sshParameters = displayless(m_devicePrivate->q->sshParameters()); - if (SshSettings::connectionSharingEnabled()) { - m_connecting = true; - m_connectionHandle.reset(new SshConnectionHandle(m_device->sharedFromThis())); - m_connectionHandle->setParent(this); - connect(m_connectionHandle.get(), &SshConnectionHandle::connected, - this, &FileTransferInterface::handleConnected); - connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected, - this, &FileTransferInterface::handleDisconnected); - m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters); - } else { - startImpl(); - } - } - -signals: - void progress(const QString &progressMessage); - void done(const Utils::ProcessResultData &resultData); protected: - FileTransferInterface(FileTransferMethod method) - : m_method(method) + SshTransferInterface(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate) + : FileTransferInterface(setup) + , m_device(devicePrivate->q->sharedFromThis()) + , m_devicePrivate(devicePrivate) , m_process(this) { + m_direction = m_setup.m_files.isEmpty() ? FileTransferDirection::Invalid + : m_setup.m_files.first().direction(); SshParameters::setupSshEnvironment(&m_process); connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] { emit progress(QString::fromLocal8Bit(m_process.readAllStandardOutput())); }); - connect(&m_process, &QtcProcess::done, this, &FileTransferInterface::doneImpl); - } - - void startFailed(const QString &errorString) - { - emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString}); + connect(&m_process, &QtcProcess::done, this, &SshTransferInterface::doneImpl); } bool handleError() @@ -1552,10 +1468,10 @@ protected: ProcessResultData resultData = m_process.resultData(); if (resultData.m_error == QProcess::FailedToStart) { resultData.m_errorString = tr("\"%1\" failed to start: %2") - .arg(FileTransfer::transferMethodName(m_method), resultData.m_errorString); + .arg(FileTransfer::transferMethodName(m_setup.m_method), resultData.m_errorString); } else if (resultData.m_exitStatus != QProcess::NormalExit) { resultData.m_errorString = tr("\"%1\" crashed.") - .arg(FileTransfer::transferMethodName(m_method)); + .arg(FileTransfer::transferMethodName(m_setup.m_method)); } else if (resultData.m_exitCode != 0) { resultData.m_errorString = QString::fromLocal8Bit(m_process.readAllStandardError()); } else { @@ -1583,14 +1499,29 @@ protected: QString userAtHost() const { return m_sshParameters.userName() + '@' + m_sshParameters.host(); } QtcProcess &process() { return m_process; } - - FilesToTransfer m_files; - TransferDirection m_direction = TransferDirection::Invalid; + FileTransferDirection direction() const { return m_direction; } private: virtual void startImpl() = 0; virtual void doneImpl() = 0; + void start() final + { + m_sshParameters = displayless(m_device->sshParameters()); + if (SshSettings::connectionSharingEnabled()) { + m_connecting = true; + m_connectionHandle.reset(new SshConnectionHandle(m_device)); + m_connectionHandle->setParent(this); + connect(m_connectionHandle.get(), &SshConnectionHandle::connected, + this, &SshTransferInterface::handleConnected); + connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected, + this, &SshTransferInterface::handleDisconnected); + m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters); + } else { + startImpl(); + } + } + void handleConnected(const QString &socketFilePath) { m_connecting = false; @@ -1612,20 +1543,24 @@ private: emit done(resultData); // TODO: don't emit done() on process finished afterwards } - FileTransferMethod m_method = FileTransferMethod::Default; IDevice::ConstPtr m_device; LinuxDevicePrivate *m_devicePrivate = nullptr; - std::unique_ptr m_connectionHandle; - QtcProcess m_process; - QString m_socketFilePath; SshParameters m_sshParameters; + FileTransferDirection m_direction = FileTransferDirection::Invalid; // helper + + // ssh shared connection related + std::unique_ptr m_connectionHandle; + QString m_socketFilePath; bool m_connecting = false; + + QtcProcess m_process; }; -class SftpTransferImpl : public FileTransferInterface +class SftpTransferImpl : public SshTransferInterface { public: - SftpTransferImpl() : FileTransferInterface(FileTransferMethod::Sftp) { } + SftpTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate) + : SshTransferInterface(setup, devicePrivate) { } private: void startImpl() final @@ -1642,12 +1577,12 @@ private: return; } - const FilePaths dirs = dirsToCreate(m_files); + const FilePaths dirs = dirsToCreate(m_setup.m_files); for (const FilePath &dir : dirs) { - if (m_direction == TransferDirection::Upload) { + if (direction() == FileTransferDirection::Upload) { m_batchFile->write("-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() + '\n'); - } else if (m_direction == TransferDirection::Download) { + } else if (direction() == FileTransferDirection::Download) { if (!QDir::root().mkpath(dir.path())) { startFailed(tr("Failed to create local directory \"%1\".") .arg(QDir::toNativeSeparators(dir.path()))); @@ -1656,10 +1591,10 @@ private: } } - for (const FileToTransfer &file : m_files) { + for (const FileToTransfer &file : m_setup.m_files) { FilePath sourceFileOrLinkTarget = file.m_source; bool link = false; - if (m_direction == TransferDirection::Upload) { + if (direction() == FileTransferDirection::Upload) { const QFileInfo fi(file.m_source.toFileInfo()); if (fi.isSymLink()) { link = true; @@ -1669,7 +1604,7 @@ private: sourceFileOrLinkTarget.setPath(fi.dir().relativeFilePath(fi.symLinkTarget())); } } - m_batchFile->write(transferCommand(m_direction, link) + ' ' + m_batchFile->write(transferCommand(direction(), link) + ' ' + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' ' + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n'); } @@ -1684,12 +1619,11 @@ private: std::unique_ptr m_batchFile; }; -class RsyncTransferImpl : public FileTransferInterface +class RsyncTransferImpl : public SshTransferInterface { public: - RsyncTransferImpl(const QString &flags) - : FileTransferInterface(FileTransferMethod::Rsync) - , m_flags(flags) + RsyncTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate) + : SshTransferInterface(setup, devicePrivate) { } private: @@ -1701,7 +1635,7 @@ private: void doneImpl() final { - if (m_files.size() == 0 || m_currentIndex == m_files.size() - 1) + if (m_setup.m_files.size() == 0 || m_currentIndex == m_setup.m_files.size() - 1) return handleDone(); if (handleError()) @@ -1718,10 +1652,10 @@ private: const QString sshCmdLine = ProcessArgs::joinArgs( QStringList{SshSettings::sshFilePath().toUserOutput()} << fullConnectionOptions(), OsTypeLinux); - QStringList options{"-e", sshCmdLine, m_flags}; + QStringList options{"-e", sshCmdLine, m_setup.m_rsyncFlags}; - if (!m_files.isEmpty()) { // NormalRun - const FileToTransfer file = m_files.at(m_currentIndex); + if (!m_setup.m_files.isEmpty()) { // NormalRun + const FileToTransfer file = m_setup.m_files.at(m_currentIndex); const FileToTransfer fixedFile = fixLocalFileOnWindows(file, options); const auto fixedPaths = fixPaths(fixedFile, userAtHost()); @@ -1740,7 +1674,7 @@ private: if (!HostOsInfo::isWindowsHost()) return file; - QString localFilePath = m_direction == TransferDirection::Upload + QString localFilePath = direction() == FileTransferDirection::Upload ? file.m_source.path() : file.m_target.path(); localFilePath = '/' + localFilePath.at(0) + localFilePath.mid(2); if (anyOf(options, [](const QString &opt) { @@ -1749,8 +1683,8 @@ private: } FileToTransfer fixedFile = file; - (m_direction == TransferDirection::Upload) ? fixedFile.m_source.setPath(localFilePath) - : fixedFile.m_target.setPath(localFilePath); + (direction() == FileTransferDirection::Upload) ? fixedFile.m_source.setPath(localFilePath) + : fixedFile.m_target.setPath(localFilePath); return fixedFile; } @@ -1758,7 +1692,7 @@ private: { FilePath localPath; FilePath remotePath; - if (m_direction == TransferDirection::Upload) { + if (direction() == FileTransferDirection::Upload) { localPath = file.m_source; remotePath = file.m_target; } else { @@ -1769,166 +1703,24 @@ private: ? localPath.path() + '/' : localPath.path(); const QString remote = remoteHost + ':' + remotePath.path(); - return m_direction == TransferDirection::Upload ? qMakePair(local, remote) - : qMakePair(remote, local); + return direction() == FileTransferDirection::Upload ? qMakePair(local, remote) + : qMakePair(remote, local); } - QString m_flags; int m_currentIndex = 0; }; -class FileTransferPrivate : public QObject +FileTransferInterface *LinuxDevice::createFileTransferInterface( + const FileTransferSetupData &setup) const { - Q_OBJECT - -public: - void test() { run(TestRun); } - void start() { run(NormalRun); } - void stop(); - - FileTransferMethod m_method = FileTransferMethod::Default; - IDevice::ConstPtr m_device; - FilesToTransfer m_files; - QString m_rsyncFlags = FileTransfer::defaultRsyncFlags(); - -signals: - void progress(const QString &progressMessage); - void done(const Utils::ProcessResultData &resultData); - -private: - enum RunMode { - NormalRun, - TestRun - }; - - void startFailed(const QString &errorString); - void run(RunMode mode); - - std::unique_ptr m_transfer; -}; - -void FileTransferPrivate::stop() -{ - if (!m_transfer) - return; - m_transfer->disconnect(); - m_transfer.release()->deleteLater(); -} - -void FileTransferPrivate::startFailed(const QString &errorString) -{ - emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString}); -} - -void FileTransferPrivate::run(RunMode mode) -{ - stop(); - - TransferDirection direction = TransferDirection::Invalid; - if (mode == NormalRun) { - if (m_files.isEmpty()) - return startFailed(tr("No files to transfer.")); - - if (!m_device) - return startFailed(tr("No device set for transfer.")); - - direction = transferDirection(m_files); - if (direction == TransferDirection::Invalid) - return startFailed(tr("Mixing different types of transfer in one go.")); - - if (!isDeviceMatched(m_files, m_device->id().toString())) - return startFailed(tr("Trying to transfer into / from not matching device.")); - } - - switch (m_method) { - case FileTransferMethod::Sftp: - m_transfer.reset(new SftpTransferImpl()); - break; - case FileTransferMethod::Rsync: - m_transfer.reset(new RsyncTransferImpl(m_rsyncFlags)); - break; - } - QTC_ASSERT(m_transfer, startFailed(tr("Missing transfer implementation.")); return); - m_transfer->setParent(this); - m_transfer->setDevice(m_device); - if (mode == NormalRun) - m_transfer->setFilesToTransfer(m_files, direction); - connect(m_transfer.get(), &FileTransferInterface::progress, - this, &FileTransferPrivate::progress); - connect(m_transfer.get(), &FileTransferInterface::done, - this, &FileTransferPrivate::done); - m_transfer->start(); -} - -FileTransfer::FileTransfer() - : d(new FileTransferPrivate) -{ - d->setParent(this); - connect(d, &FileTransferPrivate::progress, this, &FileTransfer::progress); - connect(d, &FileTransferPrivate::done, this, &FileTransfer::done); -} - -FileTransfer::~FileTransfer() -{ - stop(); - delete d; -} - -void FileTransfer::setDevice(const ProjectExplorer::IDeviceConstPtr &device) -{ - d->m_device = device; -} - -void FileTransfer::setTransferMethod(FileTransferMethod method) -{ - d->m_method = method; -} - -void FileTransfer::setFilesToTransfer(const FilesToTransfer &files) -{ - d->m_files = files; -} - -void FileTransfer::setRsyncFlags(const QString &flags) -{ - d->m_rsyncFlags = flags; -} - -void FileTransfer::test() -{ - d->test(); -} - -FileTransferMethod FileTransfer::transferMethod() const -{ - return d->m_method; -} - -void FileTransfer::start() -{ - d->start(); -} - -void FileTransfer::stop() -{ - d->stop(); -} - -QString FileTransfer::transferMethodName(FileTransferMethod method) -{ - switch (method) { - case FileTransferMethod::Sftp: return FileTransfer::tr("sftp"); - case FileTransferMethod::Rsync: return FileTransfer::tr("rsync"); + switch (setup.m_method) { + case FileTransferMethod::Sftp: return new SftpTransferImpl(setup, d); + case FileTransferMethod::Rsync: return new RsyncTransferImpl(setup, d); } QTC_CHECK(false); return {}; } -QString FileTransfer::defaultRsyncFlags() -{ - return "-av"; -} - namespace Internal { // Factory diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index 8a0406c9424..1b286ac1080 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -81,6 +81,8 @@ public: bool writeFileContents(const Utils::FilePath &filePath, const QByteArray &data) const override; QDateTime lastModified(const Utils::FilePath &filePath) const override; Utils::ProcessInterface *createProcessInterface() const override; + ProjectExplorer::FileTransferInterface *createFileTransferInterface( + const ProjectExplorer::FileTransferSetupData &setup) const override; Utils::Environment systemEnvironment() const override; qint64 fileSize(const Utils::FilePath &filePath) const override; qint64 bytesAvailable(const Utils::FilePath &filePath) const override; @@ -92,7 +94,7 @@ protected: class LinuxDevicePrivate *d; friend class SshProcessInterface; - friend class FileTransferInterface; + friend class SshTransferInterface; }; namespace Internal { diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index fb84ebc636e..4c7ac4302b5 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -25,10 +25,10 @@ #include "linuxdevicetester.h" -#include "filetransfer.h" #include "remotelinux_constants.h" #include +#include #include #include #include @@ -84,7 +84,6 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguratio QTC_ASSERT(d->state == Inactive, return); d->device = deviceConfiguration; - d->fileTransfer.setDevice(d->device); testEcho(); } @@ -215,7 +214,7 @@ void GenericLinuxDeviceTester::testFileTransfer(FileTransferMethod method) .arg(FileTransfer::transferMethodName(method))); d->fileTransfer.setTransferMethod(method); - d->fileTransfer.test(); + d->fileTransfer.test(d->device); } void GenericLinuxDeviceTester::handleFileTransferDone(const ProcessResultData &resultData) diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h index 57dabb94570..2906a51b67d 100644 --- a/src/plugins/remotelinux/linuxdevicetester.h +++ b/src/plugins/remotelinux/linuxdevicetester.h @@ -27,8 +27,7 @@ #include "remotelinux_export.h" -#include "filetransfer.h" - +#include #include namespace Utils { class ProcessResultData; } @@ -59,7 +58,7 @@ private: void handlePortsGathererError(const QString &message); void handlePortsGathererDone(); - void testFileTransfer(FileTransferMethod method); + void testFileTransfer(ProjectExplorer::FileTransferMethod method); void handleFileTransferDone(const Utils::ProcessResultData &resultData); void setFinished(ProjectExplorer::DeviceTester::TestResult result); diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs index f6ff0706439..5bf01c2c49f 100644 --- a/src/plugins/remotelinux/remotelinux.qbs +++ b/src/plugins/remotelinux/remotelinux.qbs @@ -21,7 +21,6 @@ Project { "abstractremotelinuxdeploystep.h", "deploymenttimeinfo.cpp", "deploymenttimeinfo.h", - "filetransfer.h", "genericdirectuploadservice.cpp", "genericdirectuploadservice.h", "genericdirectuploadstep.cpp", diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 421f4998f81..99eeaf413cf 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -26,10 +26,10 @@ #include "rsyncdeploystep.h" #include "abstractremotelinuxdeployservice.h" -#include "filetransfer.h" #include "remotelinux_constants.h" #include +#include #include #include #include @@ -136,7 +136,6 @@ void RsyncDeployService::createRemoteDirectories() void RsyncDeployService::deployFiles() { - m_fileTransfer.setDevice(deviceConfiguration()); m_fileTransfer.setTransferMethod(FileTransferMethod::Rsync); m_fileTransfer.setRsyncFlags(m_flags); m_fileTransfer.setFilesToTransfer(m_files); @@ -161,7 +160,7 @@ RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Utils::Id id) flags->setDisplayStyle(StringAspect::LineEditDisplay); flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); flags->setLabelText(tr("Flags:")); - flags->setValue(FileTransfer::defaultRsyncFlags()); + flags->setValue(FileTransferSetupData::defaultRsyncFlags()); auto ignoreMissingFiles = addAspect(); ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles"); diff --git a/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp b/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp index 4fe48cd066a..a1bcfe58a6e 100644 --- a/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp +++ b/src/plugins/remotelinux/uploadandinstalltarpackagestep.cpp @@ -25,12 +25,12 @@ #include "uploadandinstalltarpackagestep.h" -#include "filetransfer.h" #include "remotelinux_constants.h" #include "remotelinuxpackageinstaller.h" #include "tarpackagecreationstep.h" #include +#include #include #include @@ -103,7 +103,6 @@ void UploadAndInstallTarPackageService::doDeploy() const QString remoteFilePath = uploadDir() + QLatin1Char('/') + m_packageFilePath.fileName(); const FilesToTransfer files {{m_packageFilePath, deviceConfiguration()->filePath(remoteFilePath)}}; - m_uploader.setDevice(deviceConfiguration()); m_uploader.setFilesToTransfer(files); m_uploader.start(); } From fc7634d7307939aeb79008a0a0d689c3dccfa1b6 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 09:11:51 +0200 Subject: [PATCH 16/58] Debugger: Remove DebuggerRunTool::setInferiorDevice() Handled by the device implicit in FilePath nowadays. Change-Id: I45b0e1b03de486df678e239143e2bdd995bee380 Reviewed-by: Jarek Kobus Reviewed-by: David Schulz Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger --- src/plugins/debugger/debuggerplugin.cpp | 3 +-- src/plugins/debugger/debuggerruncontrol.cpp | 5 ----- src/plugins/debugger/debuggerruncontrol.h | 1 - src/plugins/valgrind/memchecktool.cpp | 1 - 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 8d8c013c206..3accce69ebb 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1736,8 +1736,7 @@ RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit, runControl->setDisplayName(tr("Process %1").arg(processInfo.processId)); auto debugger = new DebuggerRunTool(runControl); debugger->setAttachPid(ProcessHandle(processInfo.processId)); - debugger->setInferiorExecutable(FilePath::fromString(processInfo.executable)); - debugger->setInferiorDevice(device); + debugger->setInferiorExecutable(device->filePath(processInfo.executable)); debugger->setStartMode(AttachToLocalProcess); debugger->setCloseMode(DetachAtClose); debugger->setContinueAfterAttach(contAfterAttach); diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 832f024c85a..25a6d1acc57 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -395,11 +395,6 @@ void DebuggerRunTool::setInferiorEnvironment(const Utils::Environment &env) m_runParameters.inferior.environment = env; } -void DebuggerRunTool::setInferiorDevice(IDevice::ConstPtr device) -{ - m_runParameters.inferior.device = device; -} - void DebuggerRunTool::setRunControlName(const QString &name) { m_runParameters.displayName = name; diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index a0664a267da..4781a059366 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -76,7 +76,6 @@ public: void setInferior(const ProjectExplorer::Runnable &runnable); void setInferiorExecutable(const Utils::FilePath &executable); void setInferiorEnvironment(const Utils::Environment &env); // Used by GammaRay plugin - void setInferiorDevice(ProjectExplorer::IDeviceConstPtr device); // Used by cdbengine void setRunControlName(const QString &name); void setStartMessage(const QString &msg); void addQmlServerInferiorCommandLineArgumentIfNeeded(); diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 99cbf51d72e..e232726e04c 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -1672,7 +1672,6 @@ void HeobData::processFinished() auto debugger = new DebuggerRunTool(m_runControl); debugger->setAttachPid(ProcessHandle(m_data[1])); debugger->setRunControlName(tr("Process %1").arg(m_data[1])); - debugger->setInferiorDevice(DeviceKitAspect::device(m_kit)); debugger->setStartMode(AttachToLocalProcess); debugger->setCloseMode(DetachAtClose); debugger->setContinueAfterAttach(true); From b47b95394b16690cc0cd9113d690dcfd730c86b2 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 13:46:23 +0200 Subject: [PATCH 17/58] Valgrind: Replace one use of Runnable::device Change-Id: I8b4ae375ec2835b8d04d08eab528cac9755ee811 Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/valgrind/memchecktool.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index e232726e04c..183daa80256 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -28,7 +28,6 @@ #include "memcheckerrorview.h" #include "valgrindsettings.h" -#include "valgrindplugin.h" #include "valgrindengine.h" #include "valgrindsettings.h" #include "valgrindrunner.h" @@ -38,9 +37,6 @@ #include "xmlprotocol/errorlistmodel.h" #include "xmlprotocol/frame.h" #include "xmlprotocol/stack.h" -#include "xmlprotocol/stackmodel.h" -#include "xmlprotocol/status.h" -#include "xmlprotocol/suppression.h" #include "xmlprotocol/threadedparser.h" #include @@ -51,7 +47,7 @@ #include #include -#include +#include #include #include #include @@ -746,10 +742,9 @@ void MemcheckToolPrivate::heobAction() kit = target->kit(); if (kit) { abi = ToolChainKitAspect::targetAbi(kit); - - const Runnable runnable = rc->runnable(); - sr = runnable; - const IDevice::ConstPtr device = sr.device; + sr = rc->runnable(); + const IDevice::ConstPtr device + = DeviceManager::deviceForPath(sr.command.executable()); hasLocalRc = device && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; if (!hasLocalRc) hasLocalRc = DeviceTypeKitAspect::deviceTypeId(kit) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; From 3653456c4f65a6b02a6952aaf3369c3c0e3836c2 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 14:08:24 +0200 Subject: [PATCH 18/58] ProjectExplorer: Remove unused Runnable::displayName Change-Id: Iae9377f1d4f555a666fb523dda0972ac541c2aa2 Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/projectexplorer/runcontrol.cpp | 5 ----- src/plugins/projectexplorer/runcontrol.h | 3 --- 2 files changed, 8 deletions(-) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 5a1d6b6ccc2..f44ee1d6eeb 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1906,11 +1906,6 @@ void RunWorker::stop() reportStopped(); } -QString Runnable::displayName() const -{ - return command.executable().toString(); -} - // OutputFormatterFactory static QList g_outputFormatterFactories; diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 12230ae8bc3..5f66840d4a7 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -73,9 +73,6 @@ public: Utils::Environment environment; IDeviceConstPtr device; // Override the kit's device. Keep unset by default. QVariantHash extraData; - - // FIXME: Not necessarily a display name - QString displayName() const; }; class PROJECTEXPLORER_EXPORT RunWorker : public QObject From 4520f93f2e6ffb4ed1de915aebe2e68f772f6ffd Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 14:19:27 +0200 Subject: [PATCH 19/58] ProjectExplorer: Replace remaining uses of Runnable::device Change-Id: I2d42269eb6ad971fcedb118acb7e6e1dfbff83b2 Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/projectexplorer/projectexplorer.cpp | 2 +- src/plugins/projectexplorer/runcontrol.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 0eb12650735..df8a4b35c97 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -3935,7 +3935,7 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() QTC_ASSERT(runConfig, return); const Runnable runnable = runConfig->runnable(); - IDevice::ConstPtr device = runnable.device; + IDevice::ConstPtr device = DeviceManager::deviceForPath(runnable.command.executable()); if (!device) device = DeviceKitAspect::device(target->kit()); QTC_ASSERT(device && device->canOpenTerminal(), return); diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index f44ee1d6eeb..d9352444b5f 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -447,8 +447,8 @@ void RunControl::setKit(Kit *kit) d->kit = kit; d->macroExpander = kit->macroExpander(); - if (d->runnable.device) - setDevice(d->runnable.device); + if (!d->runnable.command.isEmpty()) + setDevice(DeviceManager::deviceForPath(d->runnable.command.executable())); else setDevice(DeviceKitAspect::device(kit)); } From 554c10ed41ca344c0b17209e83e7232830ee7d1d Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 May 2022 12:51:53 +0200 Subject: [PATCH 20/58] Compile fix, add missing #include Amends 4520f93f2e6ffb4e. Change-Id: I4bde1bd6c8842053150854735aa70fcf5bbf0da7 Reviewed-by: hjk --- src/plugins/projectexplorer/runcontrol.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index d9352444b5f..23588f7e099 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -28,6 +28,7 @@ #include "buildconfiguration.h" #include "customparser.h" #include "devicesupport/desktopdevice.h" +#include "devicesupport/devicemanager.h" #include "devicesupport/idevice.h" #include "devicesupport/sshsettings.h" #include "kitinformation.h" From 67fdb23001553b73568e21c4abd389af81b1cafa Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 13:43:05 +0200 Subject: [PATCH 21/58] ProjectExplorer: Remove one use of Runnable::device Change-Id: I0cbaf3ab73687adf11966bc291be27171e50b232 Reviewed-by: Jarek Kobus Reviewed-by: Qt CI Bot --- src/plugins/projectexplorer/projectexplorer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index df8a4b35c97..99b8f691f04 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -333,7 +333,8 @@ static bool canOpenTerminalWithRunEnv(const Project *project, const ProjectNode const RunConfiguration * const runConfig = runConfigForNode(target, node); if (!runConfig) return false; - IDevice::ConstPtr device = runConfig->runnable().device; + IDevice::ConstPtr device + = DeviceManager::deviceForPath(runConfig->runnable().command.executable()); if (!device) device = DeviceKitAspect::device(target->kit()); return device && device->canOpenTerminal(); From f7097cef1946d64f4cba50cb6973e4bfba2ea136 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Mon, 30 May 2022 12:08:21 +0200 Subject: [PATCH 22/58] CPlusPlus: Remove foreach / Q_FOREACH usage Task-number: QTCREATORBUG-27464 Change-Id: I62e27bca141a529ac220211f8b31e78be0f7e855 Reviewed-by: hjk --- src/libs/cplusplus/LookupContext.cpp | 4 +- src/libs/cplusplus/LookupContext.h | 4 +- .../cplusplus-ast2png/cplusplus-ast2png.cpp | 14 +++--- .../cplusplus-frontend/cplusplus-frontend.cpp | 2 +- .../cplusplus-mkvisitor.cpp | 45 ++++++++++--------- .../cplusplus-update-frontend.cpp | 4 +- .../qtcreatorcrashhandler/crashhandler.cpp | 6 +-- src/tools/qtpromaker/main.cpp | 11 ++--- 8 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 2ca935144f0..3aa39ea8678 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -629,7 +629,7 @@ ClassOrNamespace *ClassOrNamespace::parent() const return _parent; } -QList ClassOrNamespace::usings() const +const QList ClassOrNamespace::usings() const { const_cast(this)->flush(); return _usings; @@ -641,7 +641,7 @@ QList ClassOrNamespace::unscopedEnums() const return _enums; } -QList ClassOrNamespace::symbols() const +const QList ClassOrNamespace::symbols() const { const_cast(this)->flush(); return _symbols; diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h index ce52feec426..cb6fbb3bfa8 100644 --- a/src/libs/cplusplus/LookupContext.h +++ b/src/libs/cplusplus/LookupContext.h @@ -70,9 +70,9 @@ public: ClassOrNamespace *instantiationOrigin() const; ClassOrNamespace *parent() const; - QList usings() const; + const QList usings() const; QList unscopedEnums() const; - QList symbols() const; + const QList symbols() const; ClassOrNamespace *globalNamespace() const; diff --git a/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp b/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp index 29a2111880a..ecf281ac908 100644 --- a/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp +++ b/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp @@ -97,7 +97,7 @@ public: typedef QPair Pair; - foreach (const Pair &conn, _connections) + for (const Pair &conn : qAsConst(_connections)) out << conn.first.constData() << " -> " << conn.second.constData() << std::endl; alignTerminals(); @@ -113,7 +113,7 @@ public: protected: void alignTerminals() { out<<"{ rank=same;" << std::endl; - foreach (const QByteArray &terminalShape, _terminalShapes) { + for (const QByteArray &terminalShape : qAsConst(_terminalShapes)) { out << " " << std::string(terminalShape.constData(), terminalShape.size()).c_str() << ";" << std::endl; } out<<"}"< parseModes, QByteArray *errors, + const QList parseModes, QByteArray *errors, bool verbose = false) { - foreach (const Document::ParseMode parseMode, parseModes) { + for (const Document::ParseMode parseMode : parseModes) { ErrorHandler *errorHandler = new ErrorHandler(parseMode, errors); // Deleted by ~Document. if (verbose) std::cout << "Parsing as " << qPrintable(parseModeToString(parseMode)) << "..."; @@ -466,7 +466,7 @@ static Document::Ptr parse(const QString &fileName, const QByteArray &source, /// Convenience function static Document::Ptr parse(const QString &fileName, const QByteArray &source, - Document::ParseMode parseMode, QByteArray *errors, + const Document::ParseMode parseMode, QByteArray *errors, bool verbose = false) { QList parseModes = QList() << parseMode; @@ -576,7 +576,7 @@ int main(int argc, char *argv[]) // Process files const QStringList files = args; - foreach (const QString &fileName, files) { + for (const QString &fileName : files) { if (! QFile::exists(fileName)) { std::cerr << "Error: File \"" << qPrintable(fileName) << "\" does not exist." << std::endl; @@ -638,7 +638,7 @@ int main(int argc, char *argv[]) QString(fileName + QLatin1String(".ast.png")))); inputOutputFiles.append(qMakePair(QString(fileName + QLatin1String(".symbols.dot")), QString(fileName + QLatin1String(".symbols.png")))); - foreach (const Pair &pair, inputOutputFiles) { + for (const Pair &pair : qAsConst(inputOutputFiles)) { createImageFromDot(pair.first, pair.second, optionVerbose); std::cout << qPrintable(QDir::toNativeSeparators(pair.second)) << std::endl; } diff --git a/src/tools/cplusplus-frontend/cplusplus-frontend.cpp b/src/tools/cplusplus-frontend/cplusplus-frontend.cpp index 3701a1759c0..5f9bef2cff8 100644 --- a/src/tools/cplusplus-frontend/cplusplus-frontend.cpp +++ b/src/tools/cplusplus-frontend/cplusplus-frontend.cpp @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) // Process files const QStringList files = args; - foreach (const QString &fileName, files) { + for (const QString &fileName : files) { // Run preprocessor const QString fileNamePreprocessed = fileName + QLatin1String(".preprocessed"); CplusplusToolsUtils::SystemPreprocessor preprocessor(optionVerbose); diff --git a/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp b/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp index d834a62db0f..fe3fadd4f7d 100644 --- a/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp +++ b/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp @@ -65,7 +65,7 @@ class MkVisitor: protected SymbolVisitor bool isMiscNode(ClassOrNamespace *b) const { - foreach (ClassOrNamespace *u, b->usings()) { + for (const ClassOrNamespace *u : b->usings()) { if (oo(u->symbols().first()->name()) == QLatin1String("AST")) return true; } @@ -126,11 +126,11 @@ public: << " Semantic(TranslationUnit *unit): ASTVisitor(unit) { translationUnit(unit->ast()->asTranslationUnit()); }" << std::endl << std::endl; - foreach (ClassOrNamespace *b, interfaces) { + for (ClassOrNamespace *b : qAsConst(interfaces)) { Q_ASSERT(! b->symbols().isEmpty()); Class *klass = 0; - foreach (Symbol *s, b->symbols()) + for (Symbol *s : b->symbols()) if ((klass = s->asClass()) != 0) break; @@ -158,9 +158,9 @@ public: << std::endl; QHash > implements; - foreach (ClassOrNamespace *b, nodes) { + for (ClassOrNamespace *b : qAsConst(nodes)) { ClassOrNamespace *iface = 0; - foreach (ClassOrNamespace *u, b->usings()) { + for (ClassOrNamespace *u : b->usings()) { if (interfaces.contains(u)) { iface = u; break; @@ -170,13 +170,14 @@ public: implements[iface].append(b); } - foreach (ClassOrNamespace *iface, interfaces) { - foreach (ClassOrNamespace *b, implements.value(iface)) { + for (ClassOrNamespace *iface : qAsConst(interfaces)) { + const QList values = implements.value(iface); + for (ClassOrNamespace *b : values) { if (! isMiscNode(b)) continue; Class *klass = 0; - foreach (Symbol *s, b->symbols()) + for (Symbol *s : b->symbols()) if ((klass = s->asClass()) != 0) break; @@ -190,11 +191,12 @@ public: std::cout << std::endl; - foreach (ClassOrNamespace *iface, interfaces) { + for (ClassOrNamespace *iface : qAsConst(interfaces)) { std::cout << " // " << qPrintable(oo(iface->symbols().first()->name())) << std::endl; - foreach (ClassOrNamespace *b, implements.value(iface)) { + const QList values = implements.value(iface); + for (ClassOrNamespace *b : values) { Class *klass = 0; - foreach (Symbol *s, b->symbols()) + for (Symbol *s : b->symbols()) if ((klass = s->asClass()) != 0) break; @@ -207,11 +209,11 @@ public: } std::cout << "private:" << std::endl; - foreach (ClassOrNamespace *b, interfaces) { + for (ClassOrNamespace *b : qAsConst(interfaces)) { Q_ASSERT(! b->symbols().isEmpty()); Class *klass = 0; - foreach (Symbol *s, b->symbols()) + for (Symbol *s : b->symbols()) if ((klass = s->asClass()) != 0) break; @@ -240,11 +242,11 @@ public: // implementation - foreach (ClassOrNamespace *b, interfaces) { + for (ClassOrNamespace *b : qAsConst(interfaces)) { Q_ASSERT(! b->symbols().isEmpty()); Class *klass = 0; - foreach (Symbol *s, b->symbols()) + for (Symbol *s : b->symbols()) if ((klass = s->asClass()) != 0) break; @@ -275,11 +277,12 @@ public: << std::endl; } - foreach (ClassOrNamespace *iface, interfaces) { + for (ClassOrNamespace *iface : qAsConst(interfaces)) { std::cout << "// " << qPrintable(oo(iface->symbols().first()->name())) << std::endl; - foreach (ClassOrNamespace *b, implements.value(iface)) { + const QList values = implements.value(iface); + for (ClassOrNamespace *b : values) { Class *klass = 0; - foreach (Symbol *s, b->symbols()) + for (Symbol *s : b->symbols()) if ((klass = s->asClass()) != 0) break; @@ -383,8 +386,8 @@ protected: QList baseClasses(ClassOrNamespace *b) { QList usings = b->usings(); - foreach (ClassOrNamespace *u, usings) - usings += baseClasses(u); + for (int length = usings.size(), i = 0; i < length; ++i) + usings += baseClasses(usings[i]); return usings; } @@ -400,7 +403,7 @@ protected: if (Symbol *s = klass->find(accept0)) { if (Function *meth = s->type()->asFunctionType()) { if (! meth->isPureVirtual()) { - foreach (ClassOrNamespace *u, b->usings()) { + for (const ClassOrNamespace *u : b->usings()) { if (interfaces.contains(u)) { // qDebug() << oo(klass->name()) << "implements" << oo(u->symbols().first()->name()); } else { diff --git a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp index df796046822..5fe66c57c1a 100644 --- a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp +++ b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp @@ -1294,7 +1294,7 @@ void generateASTMatcher_H(const Snapshot &, const QDir &cplusplusDir, " virtual ~ASTMatcher();\n" "\n"; - foreach (const QByteArray &klass, classes) { + for (const QByteArray &klass : classes) { out << " virtual bool match(" << klass << " *node, " << klass << " *pattern);\n"; } @@ -1340,7 +1340,7 @@ QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir, co Overview oo; QStringList castMethods; - foreach (ClassSpecifierAST *classAST, astNodes.deriveds) { + for (ClassSpecifierAST *classAST : qAsConst(astNodes.deriveds)) { cursors[classAST] = removeCastMethods(classAST); const QString className = oo(classAST->symbol->name()); const QString methodName = QLatin1String("as") + className.mid(0, className.length() - 3); diff --git a/src/tools/qtcreatorcrashhandler/crashhandler.cpp b/src/tools/qtcreatorcrashhandler/crashhandler.cpp index 621361499eb..fa0f4009266 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandler.cpp +++ b/src/tools/qtcreatorcrashhandler/crashhandler.cpp @@ -70,7 +70,7 @@ class CExecList : public QVector public: CExecList(const QStringList &list) { - foreach (const QString &item, list) + for (const QString &item : list) append(qstrdup(item.toLatin1().data())); append(0); } @@ -195,7 +195,7 @@ bool CrashHandler::collectRestartAppData() return false; } commandLine.removeLast(); - foreach (const QByteArray &item, commandLine) + for (const QByteArray &item : qAsConst(commandLine)) d->restartAppCommandLine.append(QString::fromLatin1(item)); // Get environment. @@ -209,7 +209,7 @@ bool CrashHandler::collectRestartAppData() } if (environment.last().isEmpty()) environment.removeLast(); - foreach (const QByteArray &item, environment) + for (const QByteArray &item : qAsConst(environment)) d->restartAppEnvironment.append(QString::fromLatin1(item)); return true; diff --git a/src/tools/qtpromaker/main.cpp b/src/tools/qtpromaker/main.cpp index 60aba5476e9..4fddd6bdd48 100644 --- a/src/tools/qtpromaker/main.cpp +++ b/src/tools/qtpromaker/main.cpp @@ -159,7 +159,7 @@ private: void Project::setPaths(const QStringList &paths) { - foreach (const QString &path, paths) + for (const QString &path : paths) m_items.append(path); } @@ -220,7 +220,8 @@ void Project::handleBinary(const QString &item) // "}] (gdb) int first = input.indexOf(QLatin1Char('{')); input = input.mid(first, input.lastIndexOf(QLatin1Char('}')) - first); - foreach (QString item, input.split(QLatin1String("},{"))) { + const QStringList items = input.split(QLatin1String("},{")); + for (QString item : items) { //qDebug() << "ITEM: " << item; int full = item.indexOf(QLatin1String(",fullname=\"")); if (full != -1) @@ -299,17 +300,17 @@ void Project::writeProFile() if (m_subdirs.isEmpty()) { ts << "TEMPLATE = app\n"; ts << "TARGET = " << QFileInfo(m_outputFileName).baseName() << "\n"; - foreach (const FileClass &fc, m_fileClasses) + for (const FileClass &fc : qAsConst(m_fileClasses)) fc.writeProBlock(ts); ts << "\nPATHS *="; - foreach (const QDir &dir, m_items) + for (const QDir dir : qAsConst(m_items)) ts << " \\\n " << dir.path(); ts << "\n\nDEPENDPATH *= $$PATHS\n"; ts << "\nINCLUDEPATH *= $$PATHS\n"; } else { ts << "TEMPLATE = subdirs\n"; ts << "SUBDIRS = "; - foreach (const QString &subdir, m_subdirs) + for (const QString &subdir : qAsConst(m_subdirs)) ts << " \\\n " << subdir; ts << "\n"; } From 1a81551b9d9232f3200ada8567ac732865cab08c Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 14:39:09 +0200 Subject: [PATCH 23/58] ProjectExplorer: Cleanup runcontrol.h Change-Id: I8dbf14af221831ce3f55522c5e33c736e8748566 Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/projectexplorer/runcontrol.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 5f66840d4a7..afc1ba703a1 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -27,7 +27,6 @@ #include "buildconfiguration.h" #include "devicesupport/idevicefwd.h" -#include "projectexplorerconstants.h" #include "runconfiguration.h" #include @@ -50,8 +49,6 @@ class OutputLineParser; } // Utils namespace ProjectExplorer { -class GlobalOrProjectAspect; -class Node; class RunConfiguration; class RunControl; class Target; From 8badc4669e92cfe8c61d4f2162c03b54f35f1ec3 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 11:18:25 +0200 Subject: [PATCH 24/58] ProjectExplorer: Avoid use of Runnable::device Derive it from the command's filepath instead. Change-Id: Icb37bc0cbf449459902f24e53df9f81473e97b7b Reviewed-by: Jarek Kobus Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/projectexplorer/buildmanager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 44cea723817..51b82f4565b 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -30,6 +30,7 @@ #include "buildsystem.h" #include "compileoutputwindow.h" #include "deployconfiguration.h" +#include "devicesupport/devicemanager.h" #include "kit.h" #include "kitinformation.h" #include "project.h" @@ -46,8 +47,9 @@ #include #include #include + #include -#include + #include #include #include @@ -118,7 +120,8 @@ static int queue(const QList &projects, const QList &stepIds, return projects.contains(rc->project()); case StopBeforeBuild::SameBuildDir: return Utils::contains(projects, [rc, configSelection](Project *p) { - IDevice::ConstPtr device = rc->runnable().device; + const FilePath executable = rc->runnable().command.executable(); + IDevice::ConstPtr device = DeviceManager::deviceForPath(executable); for (const Target * const t : targetsForSelection(p, configSelection)) { if (device.isNull()) device = DeviceKitAspect::device(t->kit()); @@ -126,7 +129,7 @@ static int queue(const QList &projects, const QList &stepIds, continue; for (const BuildConfiguration * const bc : buildConfigsForSelection(t, configSelection)) { - if (rc->runnable().command.executable().isChildOf(bc->buildDirectory())) + if (executable.isChildOf(bc->buildDirectory())) return true; } } From 2af3b41ad60e7266fc2c27d237aa4792dba01d5b Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 13:53:21 +0200 Subject: [PATCH 25/58] ProjectExplorer: Replace some uses of Runnable::device Change-Id: I76ccbe9a56b9ecdc07fc85885b34f2a31c69fc07 Reviewed-by: Jarek Kobus --- src/plugins/projectexplorer/runcontrol.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 23588f7e099..41ab1af5bb8 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1399,7 +1399,7 @@ void SimpleTargetRunnerPrivate::handleStandardError() void SimpleTargetRunnerPrivate::start() { - m_isLocal = m_runnable.device.isNull() || m_runnable.device.dynamicCast(); + m_isLocal = !m_runnable.command.executable().needsDevice(); m_resultData = {}; @@ -1501,8 +1501,7 @@ void SimpleTargetRunnerPrivate::forwardDone() void SimpleTargetRunnerPrivate::forwardStarted() { - const bool isDesktop = m_runnable.device.isNull() - || m_runnable.device.dynamicCast(); + const bool isDesktop = !m_runnable.command.executable().needsDevice(); if (isDesktop) { // Console processes only know their pid after being started ProcessHandle pid{privateApplicationPID()}; @@ -1536,10 +1535,9 @@ void SimpleTargetRunner::start() d->m_runAsRoot = runAsRoot; const QString msg = RunControl::tr("Starting %1...").arg(d->m_runnable.command.toUserOutput()); - appendMessage(msg, Utils::NormalMessageFormat); + appendMessage(msg, NormalMessageFormat); - const bool isDesktop = d->m_runnable.device.isNull() - || d->m_runnable.device.dynamicCast(); + const bool isDesktop = !d->m_runnable.command.executable().needsDevice(); if (isDesktop && d->m_runnable.command.isEmpty()) { reportFailure(RunControl::tr("No executable specified.")); return; From 675bff46dad3d307d7b01e6c13b5e6e945e49225 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Mon, 30 May 2022 12:09:11 +0200 Subject: [PATCH 26/58] iostool: Remove foreach / Q_FOREACH usage Task-number: QTCREATORBUG-27464 Change-Id: I26decce7cc8748c9707fb5aae9566bab89e73e25 Reviewed-by: Eike Ziller --- src/tools/iostool/iosdevicemanager.cpp | 8 +++++--- src/tools/iostool/mobiledevicelib.cpp | 3 ++- src/tools/iostool/relayserver.cpp | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp index 0f24d58868f..2e6aa4ccf36 100644 --- a/src/tools/iostool/iosdevicemanager.cpp +++ b/src/tools/iostool/iosdevicemanager.cpp @@ -560,7 +560,7 @@ void IosDeviceManagerPrivate::addDevice(AMDeviceRef device) m_pendingLookups.remove(devId); devices << m_pendingLookups.values(QString()); m_pendingLookups.remove(QString()); - foreach (PendingDeviceLookup *devLookup, devices) { + for (PendingDeviceLookup *devLookup : qAsConst(devices)) { if (debugAll) qDebug() << "found pending op"; devLookup->timer.stop(); devLookup->callback(devId, device, devLookup->userData); @@ -589,8 +589,10 @@ void IosDeviceManagerPrivate::removeDevice(AMDeviceRef device) void IosDeviceManagerPrivate::checkPendingLookups() { - foreach (const QString &deviceId, m_pendingLookups.keys()) { - foreach (PendingDeviceLookup *deviceLookup, m_pendingLookups.values(deviceId)) { + const QStringList keys = m_pendingLookups.keys(); + for (const QString &deviceId : keys) { + const QList values = m_pendingLookups.values(deviceId); + for (PendingDeviceLookup *deviceLookup : values) { if (!deviceLookup->timer.isActive()) { m_pendingLookups.remove(deviceId, deviceLookup); deviceLookup->callback(deviceId, 0, deviceLookup->userData); diff --git a/src/tools/iostool/mobiledevicelib.cpp b/src/tools/iostool/mobiledevicelib.cpp index 79c4054a0d4..025248262f1 100644 --- a/src/tools/iostool/mobiledevicelib.cpp +++ b/src/tools/iostool/mobiledevicelib.cpp @@ -44,7 +44,8 @@ MobileDeviceLib::MobileDeviceLib() if (!load()) addError(QLatin1String("Error loading MobileDevice.framework")); if (!errors().isEmpty()) { - foreach (const QString &msg, errors()) + const QStringList errs = errors(); + for (const QString &msg : errs) addError(msg); } setLogLevel(5); diff --git a/src/tools/iostool/relayserver.cpp b/src/tools/iostool/relayserver.cpp index 03181a10ac4..312b3bd66d4 100644 --- a/src/tools/iostool/relayserver.cpp +++ b/src/tools/iostool/relayserver.cpp @@ -284,8 +284,7 @@ bool RelayServer::startServer() void RelayServer::stopServer() { - foreach (Relayer *connection, m_connections) - delete connection; + qDeleteAll(m_connections); if (m_ipv4Server.isListening()) m_ipv4Server.close(); if (m_ipv6Server.isListening()) From b7131d34db47121efaf83b9b600c611767ff4c2c Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Thu, 26 May 2022 21:36:25 -0400 Subject: [PATCH 27/58] Docker: Set a default mount to the project directory Change-Id: I35a7d7f28ffc340cd2825a4ed355f0e94bb94e8b Reviewed-by: Marcus Tillmanns Reviewed-by: hjk --- src/plugins/docker/dockerdevice.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 4fb75dc198c..d7a59aa38c0 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -44,7 +45,7 @@ public: QString tag; QString size; bool useLocalUidGid = true; - QStringList mounts; + QStringList mounts = { Core::DocumentManager::projectsDirectory().toString() }; }; class DockerDevice : public ProjectExplorer::IDevice From ce2c072e8e6676cb0a2607960a533f492f8539d3 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 13:57:59 +0200 Subject: [PATCH 28/58] ProjectExplorer: Replace one more use of Runnable::device Change-Id: I58c7b28be9fa54637288099bfca3912d2b7f4935 Reviewed-by: Jarek Kobus --- src/plugins/projectexplorer/runcontrol.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 41ab1af5bb8..90554b559bf 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1427,7 +1427,9 @@ void SimpleTargetRunnerPrivate::start() } else { QTC_ASSERT(m_state == Inactive, return); - if (!m_runnable.device) { + const IDevice::ConstPtr device = + DeviceManager::deviceForPath(m_runnable.command.executable()); + if (!device) { m_resultData.m_errorString = tr("Cannot run: No device."); m_resultData.m_error = QProcess::FailedToStart; m_resultData.m_exitStatus = QProcess::CrashExit; @@ -1435,7 +1437,7 @@ void SimpleTargetRunnerPrivate::start() return; } - if (!m_runnable.device->isEmptyCommandAllowed() && m_runnable.command.isEmpty()) { + if (!device->isEmptyCommandAllowed() && m_runnable.command.isEmpty()) { m_resultData.m_errorString = tr("Cannot run: No command given."); m_resultData.m_error = QProcess::FailedToStart; m_resultData.m_exitStatus = QProcess::CrashExit; From 99c37ab7b7b0e1a02fbaf688bc6430bc9b203f89 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 14:45:29 +0200 Subject: [PATCH 29/58] ProjectExplorer: Remove RunControl::buildType() Apparently unused. Change-Id: I3aac506750735a465d787efeb9ea6a6de6e2c6a1 Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/android/androidrunnerworker.cpp | 2 +- src/plugins/autotest/ctest/ctesttreeitem.cpp | 1 + src/plugins/clangtools/clangtool.cpp | 1 + src/plugins/clangtools/documentclangtoolrunner.cpp | 6 +++++- src/plugins/projectexplorer/runcontrol.cpp | 7 ------- src/plugins/projectexplorer/runcontrol.h | 2 -- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index 71e92a1fde3..da28535d03d 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -28,11 +28,11 @@ #include "androidconfigurations.h" #include "androidconstants.h" #include "androidmanager.h" -#include "androidrunconfiguration.h" #include #include +#include #include #include #include diff --git a/src/plugins/autotest/ctest/ctesttreeitem.cpp b/src/plugins/autotest/ctest/ctesttreeitem.cpp index 5e08d62c488..c374754d652 100644 --- a/src/plugins/autotest/ctest/ctesttreeitem.cpp +++ b/src/plugins/autotest/ctest/ctesttreeitem.cpp @@ -32,6 +32,7 @@ #include "../itestframework.h" #include "../testsettings.h" +#include #include #include #include diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 24b2822559b..cfcad7f87a7 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -51,6 +51,7 @@ #include +#include #include #include #include diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index d62b882d4a2..b0794676d0a 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -26,7 +26,6 @@ #include "documentclangtoolrunner.h" #include "clangfileinfo.h" -#include "clangfixitsrefactoringchanges.h" #include "clangtidyclazyrunner.h" #include "clangtoolruncontrol.h" #include "clangtoolsconstants.h" @@ -39,13 +38,18 @@ #include #include #include + #include + +#include #include #include #include + #include #include #include + #include #include diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 90554b559bf..fba89d865be 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -324,7 +324,6 @@ public: QMap settingsData; Utils::Id runConfigId; BuildTargetInfo buildTargetInfo; - BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown; FilePath buildDirectory; Environment buildEnvironment; Kit *kit = nullptr; // Not owned. @@ -431,7 +430,6 @@ void RunControl::setTarget(Target *target) d->buildTargetInfo = target->buildTarget(d->buildKey); if (auto bc = target->activeBuildConfiguration()) { - d->buildType = bc->buildType(); d->buildDirectory = bc->buildDirectory(); d->buildEnvironment = bc->environment(); } @@ -976,11 +974,6 @@ QString RunControl::buildKey() const return d->buildKey; } -BuildConfiguration::BuildType RunControl::buildType() const -{ - return d->buildType; -} - FilePath RunControl::buildDirectory() const { return d->buildDirectory; diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index afc1ba703a1..ec7290a1947 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -25,7 +25,6 @@ #pragma once -#include "buildconfiguration.h" #include "devicesupport/idevicefwd.h" #include "runconfiguration.h" @@ -233,7 +232,6 @@ public: } QString buildKey() const; - BuildConfiguration::BuildType buildType() const; Utils::FilePath buildDirectory() const; Utils::Environment buildEnvironment() const; From 190988bd0b61c8434b46d5f8f302b83d49b02b30 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Fri, 27 May 2022 22:08:38 -0400 Subject: [PATCH 30/58] Docker: Remove the dependency on docker for the kit detector This is in preparation for perhaps moving the kit detector into another plugin such as project explorer for usage by remote linux devices for instance. Change-Id: Ie17ab3913aed9ccc895213882cd188c16affda15 Reviewed-by: hjk --- src/plugins/docker/kitdetector.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/docker/kitdetector.cpp b/src/plugins/docker/kitdetector.cpp index 35f1a52f23d..5e0c2e86598 100644 --- a/src/plugins/docker/kitdetector.cpp +++ b/src/plugins/docker/kitdetector.cpp @@ -25,8 +25,6 @@ #include "kitdetector.h" -#include "dockerconstants.h" - #include #include @@ -337,7 +335,7 @@ void KitDetectorPrivate::autoDetect() if (cmakeId.isValid()) k->setValue(CMakeProjectManager::Constants::TOOL_ID, cmakeId.toSetting()); - DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE); + DeviceTypeKitAspect::setDeviceTypeId(k, m_device->type()); DeviceKitAspect::setDevice(k, m_device); BuildDeviceKitAspect::setDevice(k, m_device); From aa5e39d7bfe817bc8169ad6e60709d4a9e7522b8 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 14:18:26 +0200 Subject: [PATCH 31/58] ProjectExplorer: Remove Runnable::device Not used anymore. Change-Id: Ic22037d67df890b8fb46bf0dcb6b6513dddddb14 Reviewed-by: Jarek Kobus --- src/plugins/projectexplorer/runcontrol.cpp | 2 -- src/plugins/projectexplorer/runcontrol.h | 1 - 2 files changed, 3 deletions(-) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index fba89d865be..251d20c2532 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1510,7 +1510,6 @@ void SimpleTargetRunnerPrivate::forwardStarted() void SimpleTargetRunner::start() { d->m_runnable = runControl()->runnable(); - d->m_runnable.device = runControl()->device(); if (d->m_startModifier) d->m_startModifier(); @@ -1573,7 +1572,6 @@ void SimpleTargetRunner::setWorkingDirectory(const FilePath &workingDirectory) void SimpleTargetRunner::forceRunOnHost() { - d->m_runnable.device = {}; const FilePath executable = d->m_runnable.command.executable(); if (executable.needsDevice()) { QTC_CHECK(false); diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index ec7290a1947..3cf9ffcfd39 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -67,7 +67,6 @@ public: Utils::CommandLine command; Utils::FilePath workingDirectory; Utils::Environment environment; - IDeviceConstPtr device; // Override the kit's device. Keep unset by default. QVariantHash extraData; }; From d325f56cc1c2fcea4cd338d8f4f30a522533446f Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 15:30:04 +0200 Subject: [PATCH 32/58] RemoteLinux: Prepare new aspect data extraction for X11Forwarding Following the ArgumentsAspect precedence for MacroExpander. Change-Id: Id3f58a994eb03911f7c864d66e06cf9571a1e3db Reviewed-by: Jarek Kobus --- .../remotelinuxcustomrunconfiguration.cpp | 4 ++-- .../remotelinux/remotelinuxrunconfiguration.cpp | 4 ++-- .../remotelinux/remotelinuxx11forwardingaspect.cpp | 13 ++++++++----- .../remotelinux/remotelinuxx11forwardingaspect.h | 12 ++++++++++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp index 421a1556550..51edcf7efea 100644 --- a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp @@ -76,11 +76,11 @@ RemoteLinuxCustomRunConfiguration::RemoteLinuxCustomRunConfiguration(Target *tar if (HostOsInfo::isAnyUnixHost()) addAspect(); if (HostOsInfo::isAnyUnixHost()) - addAspect(); + addAspect(macroExpander()); setRunnableModifier([this](Runnable &r) { if (const auto * const forwardingAspect = aspect()) - r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display(macroExpander())); + r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); }); setDefaultDisplayName(runConfigDefaultDisplayName()); diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp index ca16b4ddfc3..a0cccf4f6ef 100644 --- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp @@ -75,7 +75,7 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) if (HostOsInfo::isAnyUnixHost()) addAspect(); if (HostOsInfo::isAnyUnixHost()) - addAspect(); + addAspect(macroExpander()); setUpdater([this, target, exeAspect, symbolsAspect] { BuildTargetInfo bti = buildTargetInfo(); @@ -88,7 +88,7 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) setRunnableModifier([this](Runnable &r) { if (const auto * const forwardingAspect = aspect()) - r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display(macroExpander())); + r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); diff --git a/src/plugins/remotelinux/remotelinuxx11forwardingaspect.cpp b/src/plugins/remotelinux/remotelinuxx11forwardingaspect.cpp index 739a470321f..3fbeba70cc1 100644 --- a/src/plugins/remotelinux/remotelinuxx11forwardingaspect.cpp +++ b/src/plugins/remotelinux/remotelinuxx11forwardingaspect.cpp @@ -32,9 +32,10 @@ using namespace Utils; namespace RemoteLinux { -static QString defaultDisplay() { return QLatin1String(qgetenv("DISPLAY")); } +static QString defaultDisplay() { return qEnvironmentVariable("DISPLAY"); } -X11ForwardingAspect::X11ForwardingAspect() +X11ForwardingAspect::X11ForwardingAspect(const MacroExpander *expander) + : m_macroExpander(expander) { setLabelText(tr("X11 Forwarding:")); setDisplayStyle(LineEditDisplay); @@ -43,12 +44,14 @@ X11ForwardingAspect::X11ForwardingAspect() makeCheckable(CheckBoxPlacement::Right, tr("Forward to local display"), "RunConfiguration.UseX11Forwarding"); setValue(defaultDisplay()); + + addDataExtractor(this, &X11ForwardingAspect::display, &Data::display); } -QString X11ForwardingAspect::display(const Utils::MacroExpander *expander) const +QString X11ForwardingAspect::display() const { - QTC_ASSERT(expander, return value()); - return !isChecked() ? QString() : expander->expandProcessArgs(value()); + QTC_ASSERT(m_macroExpander, return value()); + return !isChecked() ? QString() : m_macroExpander->expandProcessArgs(value()); } } // namespace RemoteLinux diff --git a/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h b/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h index b518782fbc8..2f7fa402d74 100644 --- a/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h +++ b/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h @@ -38,9 +38,17 @@ class REMOTELINUX_EXPORT X11ForwardingAspect : public Utils::StringAspect Q_OBJECT public: - X11ForwardingAspect(); + X11ForwardingAspect(const Utils::MacroExpander *macroExpander); - QString display(const Utils::MacroExpander *expander) const; + struct Data : BaseAspect::Data + { + QString display; + }; + + QString display() const; + +private: + const Utils::MacroExpander *m_macroExpander; }; } // namespace RemoteLinux From 17ee38775647306e7b79549ec5941618bf52c8f1 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 15:57:20 +0200 Subject: [PATCH 33/58] ProjectExplorer: Dissolve a few uses of Runnable Change-Id: I71b8f1b00e488360e7f9c136912bc64b5a76609a Reviewed-by: Jarek Kobus --- src/plugins/android/androidrunnerworker.cpp | 5 +- .../clangtools/clangtoolruncontrol.cpp | 4 +- src/plugins/ios/iosrunner.cpp | 4 +- src/plugins/projectexplorer/appoutputpane.cpp | 28 ++++--- src/plugins/projectexplorer/buildmanager.cpp | 2 +- src/plugins/projectexplorer/runcontrol.cpp | 73 +++++++++++++------ src/plugins/projectexplorer/runcontrol.h | 5 ++ 7 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index da28535d03d..a4e5a3c61f9 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -274,9 +274,8 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa qCDebug(androidRunWorkerLog) << "Environment variables for the app" << m_extraEnvVars.toStringList(); - if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release) { - m_extraAppParams = runControl->runnable().command.arguments(); - } + if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release) + m_extraAppParams = runControl->commandLine().arguments(); if (auto aspect = runControl->aspect(Constants::ANDROID_AM_START_ARGS)) { QTC_CHECK(aspect->value.type() == QVariant::String); diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 231e75bf4b0..5751ee5c3b2 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -253,10 +253,10 @@ void ClangToolRunWorker::start() // Collect files const auto clangIncludeDirAndVersion = - getClangIncludeDirAndVersion(runControl()->runnable().command.executable()); + getClangIncludeDirAndVersion(runControl()->commandLine().executable()); const AnalyzeUnits unitsToProcess = unitsToAnalyze(clangIncludeDirAndVersion.first, clangIncludeDirAndVersion.second); - qCDebug(LOG) << Q_FUNC_INFO << runControl()->runnable().command.executable() + qCDebug(LOG) << Q_FUNC_INFO << runControl()->commandLine().executable() << clangIncludeDirAndVersion.first << clangIncludeDirAndVersion.second; qCDebug(LOG) << "Files to process:" << unitsToProcess; diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index d4881346ded..aea7a28b9ca 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -197,8 +197,8 @@ void IosRunner::start() connect(m_toolHandler, &IosToolHandler::finished, this, &IosRunner::handleFinished); - const Runnable runnable = runControl()->runnable(); - QStringList args = ProcessArgs::splitArgs(runnable.command.arguments(), OsTypeMac); + const CommandLine command = runControl()->commandLine(); + QStringList args = ProcessArgs::splitArgs(command.arguments(), OsTypeMac); if (m_qmlServerPort.isValid()) { QUrl qmlServer; qmlServer.setPort(m_qmlServerPort.number()); diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 380ec9f0716..bb96e200f7f 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -44,6 +44,7 @@ #include #include + #include #include #include @@ -66,6 +67,8 @@ static Q_LOGGING_CATEGORY(appOutputLog, "qtc.projectexplorer.appoutput", QtWarningMsg); +using namespace Utils; + namespace ProjectExplorer { namespace Internal { @@ -398,19 +401,20 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) connect(rc, &RunControl::applicationProcessHandleChanged, this, &AppOutputPane::enableDefaultButtons); connect(rc, &RunControl::appendMessage, - this, [this, rc](const QString &out, Utils::OutputFormat format) { + this, [this, rc](const QString &out, OutputFormat format) { appendMessage(rc, out, format); }); // First look if we can reuse a tab - const Runnable thisRunnable = rc->runnable(); + const CommandLine thisCommand = rc->commandLine(); + const FilePath thisWorkingDirectory = rc->workingDirectory(); + const Environment thisEnvironment = rc->environment(); const int tabIndex = Utils::indexOf(m_runControlTabs, [&](const RunControlTab &tab) { if (!tab.runControl || tab.runControl->isRunning()) return false; - const Runnable otherRunnable = tab.runControl->runnable(); - return thisRunnable.command == otherRunnable.command - && thisRunnable.workingDirectory == otherRunnable.workingDirectory - && thisRunnable.environment == otherRunnable.environment; + return thisCommand == tab.runControl->commandLine() + && thisWorkingDirectory == tab.runControl->workingDirectory() + && thisEnvironment == tab.runControl->environment(); }); if (tabIndex != -1) { RunControlTab &tab = m_runControlTabs[tabIndex]; @@ -433,7 +437,7 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) } // Create new static int counter = 0; - Utils::Id contextId = Utils::Id(C_APP_OUTPUT).withSuffix(counter++); + Id contextId = Id(C_APP_OUTPUT).withSuffix(counter++); Core::Context context(contextId); Core::OutputWindow *ow = new Core::OutputWindow(context, SETTINGS_KEY, m_tabWidget); ow->setWindowTitle(tr("Application Output Window")); @@ -486,19 +490,19 @@ void AppOutputPane::updateFromSettings() } } -void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::OutputFormat format) +void AppOutputPane::appendMessage(RunControl *rc, const QString &out, OutputFormat format) { const int index = indexOf(rc); if (index != -1) { Core::OutputWindow *window = m_runControlTabs.at(index).window; QString stringToWrite; - if (format == Utils::NormalMessageFormat || format == Utils::ErrorMessageFormat) { + if (format == NormalMessageFormat || format == ErrorMessageFormat) { stringToWrite = QTime::currentTime().toString(); stringToWrite += ": "; } stringToWrite += out; window->appendMessage(stringToWrite, format); - if (format != Utils::NormalMessageFormat) { + if (format != NormalMessageFormat) { RunControlTab &tab = m_runControlTabs[index]; switch (tab.behaviorOnOutput) { case AppOutputPaneMode::FlashOnOutput: @@ -530,7 +534,7 @@ const bool kWrapOutputDefault = true; void AppOutputPane::storeSettings() const { - Utils::QtcSettings *const s = Core::ICore::settings(); + QtcSettings *const s = Core::ICore::settings(); s->setValueWithDefault(POP_UP_FOR_RUN_OUTPUT_KEY, int(m_settings.runOutputMode), int(kRunOutputModeDefault)); @@ -706,7 +710,7 @@ void AppOutputPane::enableButtons(const RunControl *rc) m_stopAction->setEnabled(isRunning); if (isRunning && debuggerPlugin() && rc->applicationProcessHandle().isValid()) { m_attachButton->setEnabled(true); - Utils::ProcessHandle h = rc->applicationProcessHandle(); + ProcessHandle h = rc->applicationProcessHandle(); QString tip = h.isValid() ? RunControl::tr("PID %1").arg(h.pid()) : RunControl::tr("Invalid"); m_attachButton->setToolTip(msgAttachDebuggerTooltip(tip)); diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 51b82f4565b..7116792d77c 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -120,7 +120,7 @@ static int queue(const QList &projects, const QList &stepIds, return projects.contains(rc->project()); case StopBeforeBuild::SameBuildDir: return Utils::contains(projects, [rc, configSelection](Project *p) { - const FilePath executable = rc->runnable().command.executable(); + const FilePath executable = rc->commandLine().executable(); IDevice::ConstPtr device = DeviceManager::deviceForPath(executable); for (const Target * const t : targetsForSelection(p, configSelection)) { if (device.isNull()) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 251d20c2532..a5926c31134 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -909,6 +909,26 @@ void RunControl::setRunnable(const Runnable &runnable) d->runnable = runnable; } +const CommandLine &RunControl::commandLine() const +{ + return d->runnable.command; +} + +const FilePath &RunControl::workingDirectory() const +{ + return d->runnable.workingDirectory; +} + +const Environment &RunControl::environment() const +{ + return d->runnable.environment; +} + +const QVariantHash &RunControl::extraData() const +{ + return d->runnable.extraData; +} + QString RunControl::displayName() const { return d->displayName; @@ -1236,7 +1256,10 @@ public: State m_state = Inactive; bool m_stopRequested = false; - Runnable m_runnable; + Utils::CommandLine m_command; + Utils::FilePath m_workingDirectory; + Utils::Environment m_environment; + QVariantHash m_extraData; ProcessResultData m_resultData; @@ -1392,12 +1415,12 @@ void SimpleTargetRunnerPrivate::handleStandardError() void SimpleTargetRunnerPrivate::start() { - m_isLocal = !m_runnable.command.executable().needsDevice(); + m_isLocal = !m_command.executable().needsDevice(); m_resultData = {}; if (m_isLocal) { - Environment env = m_runnable.environment; + Environment env = m_environment; if (m_runAsRoot) RunControl::provideAskPassEntry(env); @@ -1407,7 +1430,7 @@ void SimpleTargetRunnerPrivate::start() WinDebugInterface::startIfNeeded(); - CommandLine cmdLine = m_runnable.command; + CommandLine cmdLine = m_command; if (HostOsInfo::isMacHost()) { CommandLine disclaim(Core::ICore::libexecPath("disclaim")); @@ -1420,8 +1443,7 @@ void SimpleTargetRunnerPrivate::start() } else { QTC_ASSERT(m_state == Inactive, return); - const IDevice::ConstPtr device = - DeviceManager::deviceForPath(m_runnable.command.executable()); + const IDevice::ConstPtr device = DeviceManager::deviceForPath(m_command.executable()); if (!device) { m_resultData.m_errorString = tr("Cannot run: No device."); m_resultData.m_error = QProcess::FailedToStart; @@ -1430,7 +1452,7 @@ void SimpleTargetRunnerPrivate::start() return; } - if (!device->isEmptyCommandAllowed() && m_runnable.command.isEmpty()) { + if (!device->isEmptyCommandAllowed() && m_command.isEmpty()) { m_resultData.m_errorString = tr("Cannot run: No command given."); m_resultData.m_error = QProcess::FailedToStart; m_resultData.m_exitStatus = QProcess::CrashExit; @@ -1441,12 +1463,12 @@ void SimpleTargetRunnerPrivate::start() m_state = Run; m_stopRequested = false; - m_process.setCommand(m_runnable.command); - m_process.setEnvironment(m_runnable.environment); - m_process.setExtraData(m_runnable.extraData); + m_process.setCommand(m_command); + m_process.setEnvironment(m_environment); + m_process.setExtraData(m_extraData); } - m_process.setWorkingDirectory(m_runnable.workingDirectory); + m_process.setWorkingDirectory(m_workingDirectory); if (m_isLocal) m_outputCodec = QTextCodec::codecForLocale(); @@ -1481,14 +1503,14 @@ void SimpleTargetRunnerPrivate::forwardDone() { if (m_stopReported) return; - const QString executable = m_runnable.command.executable().toUserOutput(); + const QString executable = m_command.executable().toUserOutput(); QString msg = tr("%1 exited with code %2").arg(executable).arg(m_resultData.m_exitCode); if (m_resultData.m_exitStatus == QProcess::CrashExit) msg = tr("%1 crashed.").arg(executable); else if (m_stopForced) msg = tr("The process was ended forcefully."); else if (m_resultData.m_error != QProcess::UnknownError) - msg = RunWorker::userMessageForProcessError(m_resultData.m_error, m_runnable.command.executable()); + msg = RunWorker::userMessageForProcessError(m_resultData.m_error, m_command.executable()); q->appendMessage(msg, NormalMessageFormat); m_stopReported = true; q->reportStopped(); @@ -1496,7 +1518,7 @@ void SimpleTargetRunnerPrivate::forwardDone() void SimpleTargetRunnerPrivate::forwardStarted() { - const bool isDesktop = !m_runnable.command.executable().needsDevice(); + const bool isDesktop = !m_command.executable().needsDevice(); if (isDesktop) { // Console processes only know their pid after being started ProcessHandle pid{privateApplicationPID()}; @@ -1509,7 +1531,10 @@ void SimpleTargetRunnerPrivate::forwardStarted() void SimpleTargetRunner::start() { - d->m_runnable = runControl()->runnable(); + d->m_command = runControl()->commandLine(); + d->m_workingDirectory = runControl()->workingDirectory(); + d->m_environment = runControl()->environment(); + d->m_extraData = runControl()->extraData(); if (d->m_startModifier) d->m_startModifier(); @@ -1528,11 +1553,11 @@ void SimpleTargetRunner::start() d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off); d->m_runAsRoot = runAsRoot; - const QString msg = RunControl::tr("Starting %1...").arg(d->m_runnable.command.toUserOutput()); + const QString msg = RunControl::tr("Starting %1...").arg(d->m_command.toUserOutput()); appendMessage(msg, NormalMessageFormat); - const bool isDesktop = !d->m_runnable.command.executable().needsDevice(); - if (isDesktop && d->m_runnable.command.isEmpty()) { + const bool isDesktop = !d->m_command.executable().needsDevice(); + if (isDesktop && d->m_command.isEmpty()) { reportFailure(RunControl::tr("No executable specified.")); return; } @@ -1552,30 +1577,30 @@ void SimpleTargetRunner::setStartModifier(const std::function &startMod CommandLine SimpleTargetRunner::commandLine() const { - return d->m_runnable.command; + return d->m_command; } void SimpleTargetRunner::setCommandLine(const Utils::CommandLine &commandLine) { - d->m_runnable.command = commandLine; + d->m_command = commandLine; } void SimpleTargetRunner::setEnvironment(const Environment &environment) { - d->m_runnable.environment = environment; + d->m_environment = environment; } void SimpleTargetRunner::setWorkingDirectory(const FilePath &workingDirectory) { - d->m_runnable.workingDirectory = workingDirectory; + d->m_workingDirectory = workingDirectory; } void SimpleTargetRunner::forceRunOnHost() { - const FilePath executable = d->m_runnable.command.executable(); + const FilePath executable = d->m_command.executable(); if (executable.needsDevice()) { QTC_CHECK(false); - d->m_runnable.command.setExecutable(FilePath::fromString(executable.path())); + d->m_command.setExecutable(FilePath::fromString(executable.path())); } } diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 3cf9ffcfd39..6df3f5d365b 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -245,6 +245,11 @@ public: const Runnable &runnable() const; void setRunnable(const Runnable &runnable); + const Utils::CommandLine &commandLine() const; + const Utils::FilePath &workingDirectory() const; + const Utils::Environment &environment() const; + const QVariantHash &extraData() const; + static bool showPromptToStopDialog(const QString &title, const QString &text, const QString &stopButtonText = QString(), const QString &cancelButtonText = QString(), From 2837f4d7772cc3edc6dff2efee4bc06eb47b0bfb Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 May 2022 14:56:20 +0200 Subject: [PATCH 34/58] ProjectExplorer: Pass macroexpander (again) to WorkingDirectoryAspect Broke with 8dacb123e0e71. Probably calls for a more centralized approach again. Later. Change-Id: Ibf52487d68d7938d4b5dfb38f1f0a5b0e1a97bc3 Reviewed-by: Reviewed-by: Christian Stenger --- src/libs/utils/pathchooser.cpp | 4 ++-- src/libs/utils/pathchooser.h | 2 +- src/plugins/baremetal/baremetalrunconfiguration.cpp | 4 ++-- src/plugins/boot2qt/qdbrunconfiguration.cpp | 2 +- .../mesonprojectmanager/project/mesonrunconfiguration.cpp | 2 +- src/plugins/nim/project/nimblerunconfiguration.cpp | 5 +++-- src/plugins/nim/project/nimrunconfiguration.cpp | 2 +- .../projectexplorer/customexecutablerunconfiguration.cpp | 2 +- src/plugins/projectexplorer/desktoprunconfiguration.cpp | 2 +- src/plugins/projectexplorer/runconfigurationaspects.cpp | 5 +++-- src/plugins/projectexplorer/runconfigurationaspects.h | 5 +++-- src/plugins/python/pythonrunconfiguration.cpp | 2 +- src/plugins/qnx/qnxrunconfiguration.cpp | 2 +- .../remotelinux/remotelinuxcustomrunconfiguration.cpp | 2 +- src/plugins/remotelinux/remotelinuxrunconfiguration.cpp | 2 +- 15 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 12bc2ac3095..27d08acb803 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -192,7 +192,7 @@ public: EnvironmentChange m_environmentChange; BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr; QList m_buttons; - MacroExpander *m_macroExpander = globalMacroExpander(); + const MacroExpander *m_macroExpander = globalMacroExpander(); std::function m_openTerminal; }; @@ -732,7 +732,7 @@ void PathChooser::setHistoryCompleter(const QString &historyKey, bool restoreLas d->m_lineEdit->setHistoryCompleter(historyKey, restoreLastItemFromHistory); } -void PathChooser::setMacroExpander(MacroExpander *macroExpander) +void PathChooser::setMacroExpander(const MacroExpander *macroExpander) { d->m_macroExpander = macroExpander; } diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 6e1760d3914..698dc48951a 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -136,7 +136,7 @@ public: // Sets a macro expander that is used when producing path and fileName. // By default, the global expander is used. // nullptr can be passed to disable macro expansion. - void setMacroExpander(MacroExpander *macroExpander); + void setMacroExpander(const MacroExpander *macroExpander); bool isReadOnly() const; void setReadOnly(bool b); diff --git a/src/plugins/baremetal/baremetalrunconfiguration.cpp b/src/plugins/baremetal/baremetalrunconfiguration.cpp index 8cfc8378641..f081520bebb 100644 --- a/src/plugins/baremetal/baremetalrunconfiguration.cpp +++ b/src/plugins/baremetal/baremetalrunconfiguration.cpp @@ -53,7 +53,7 @@ public: exeAspect->setPlaceHolderText(tr("Unknown")); addAspect(macroExpander()); - addAspect(nullptr); + addAspect(macroExpander(), nullptr); setUpdater([this, exeAspect] { const BuildTargetInfo bti = buildTargetInfo(); @@ -80,7 +80,7 @@ public: exeAspect->setExpectedKind(PathChooser::Any); addAspect(macroExpander()); - addAspect(nullptr); + addAspect(macroExpander(), nullptr); setDefaultDisplayName(RunConfigurationFactory::decoratedTargetName(tr("Custom Executable"), target)); } diff --git a/src/plugins/boot2qt/qdbrunconfiguration.cpp b/src/plugins/boot2qt/qdbrunconfiguration.cpp index 5706116445d..98cae5734bc 100644 --- a/src/plugins/boot2qt/qdbrunconfiguration.cpp +++ b/src/plugins/boot2qt/qdbrunconfiguration.cpp @@ -102,7 +102,7 @@ QdbRunConfiguration::QdbRunConfiguration(Target *target, Id id) auto envAspect = addAspect(target); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(this); setUpdater([this, target, exeAspect, symbolsAspect] { diff --git a/src/plugins/mesonprojectmanager/project/mesonrunconfiguration.cpp b/src/plugins/mesonprojectmanager/project/mesonrunconfiguration.cpp index cdba329df92..84f226ab6f7 100644 --- a/src/plugins/mesonprojectmanager/project/mesonrunconfiguration.cpp +++ b/src/plugins/mesonprojectmanager/project/mesonrunconfiguration.cpp @@ -46,7 +46,7 @@ MesonRunConfiguration::MesonRunConfiguration(ProjectExplorer::Target *target, Ut addAspect(target); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(); auto libAspect = addAspect(); diff --git a/src/plugins/nim/project/nimblerunconfiguration.cpp b/src/plugins/nim/project/nimblerunconfiguration.cpp index 8ff42f1aef7..e88e2c11ad6 100644 --- a/src/plugins/nim/project/nimblerunconfiguration.cpp +++ b/src/plugins/nim/project/nimblerunconfiguration.cpp @@ -54,7 +54,7 @@ public: auto envAspect = addAspect(target); addAspect(target); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(); setUpdater([this] { @@ -91,7 +91,8 @@ public: { addAspect(target)->setExecutable(Nim::nimblePathFromKit(target->kit())); addAspect(macroExpander())->setArguments("test"); - addAspect(nullptr)->setDefaultWorkingDirectory(project()->projectDirectory()); + addAspect(macroExpander(), nullptr) + ->setDefaultWorkingDirectory(project()->projectDirectory()); addAspect(); setDisplayName(tr("Nimble Test")); diff --git a/src/plugins/nim/project/nimrunconfiguration.cpp b/src/plugins/nim/project/nimrunconfiguration.cpp index 1e5e519548e..925fac47736 100644 --- a/src/plugins/nim/project/nimrunconfiguration.cpp +++ b/src/plugins/nim/project/nimrunconfiguration.cpp @@ -52,7 +52,7 @@ public: auto envAspect = addAspect(target); addAspect(target); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(); setDisplayName(tr("Current Build Target")); diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 67cdaa2965a..cbd8705f6ff 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -53,7 +53,7 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe exeAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(envAspect->environment())); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(); connect(envAspect, &EnvironmentAspect::environmentChanged, this, [exeAspect, envAspect] { diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.cpp b/src/plugins/projectexplorer/desktoprunconfiguration.cpp index cef277f53f3..663474afdc5 100644 --- a/src/plugins/projectexplorer/desktoprunconfiguration.cpp +++ b/src/plugins/projectexplorer/desktoprunconfiguration.cpp @@ -70,7 +70,7 @@ DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Id id, Kind kin addAspect(target); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(); auto libAspect = addAspect(); diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 22a4e3a99ef..ab2e00e7440 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -171,8 +171,9 @@ bool TerminalAspect::isUserSet() const working directory for running the executable. */ -WorkingDirectoryAspect::WorkingDirectoryAspect(EnvironmentAspect *envAspect) - : m_envAspect(envAspect) +WorkingDirectoryAspect::WorkingDirectoryAspect(const MacroExpander *expander, + EnvironmentAspect *envAspect) + : m_envAspect(envAspect), m_macroExpander(expander) { setDisplayName(tr("Working Directory")); setId("WorkingDirectoryAspect"); diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index 3c5058d40de..768d89a0430 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -81,7 +81,8 @@ class PROJECTEXPLORER_EXPORT WorkingDirectoryAspect : public Utils::BaseAspect Q_OBJECT public: - explicit WorkingDirectoryAspect(EnvironmentAspect *envAspect); + explicit WorkingDirectoryAspect(const Utils::MacroExpander *expander, + EnvironmentAspect *envAspect); void addToLayout(Utils::LayoutBuilder &builder) override; @@ -102,7 +103,7 @@ private: Utils::FilePath m_defaultWorkingDirectory; QPointer m_chooser; QPointer m_resetButton; - Utils::MacroExpander *m_macroExpander = nullptr; + const Utils::MacroExpander *m_macroExpander = nullptr; }; class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index cb5cb948cb5..12813f6829b 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -178,7 +178,7 @@ public: auto argumentsAspect = addAspect(macroExpander()); - addAspect(nullptr); + addAspect(macroExpander(), nullptr); addAspect(); setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] { diff --git a/src/plugins/qnx/qnxrunconfiguration.cpp b/src/plugins/qnx/qnxrunconfiguration.cpp index 88535ebd41d..39753f8afb4 100644 --- a/src/plugins/qnx/qnxrunconfiguration.cpp +++ b/src/plugins/qnx/qnxrunconfiguration.cpp @@ -61,7 +61,7 @@ QnxRunConfiguration::QnxRunConfiguration(Target *target, Id id) auto envAspect = addAspect(target); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); addAspect(); auto libAspect = addAspect(); diff --git a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp index 51edcf7efea..6009c502696 100644 --- a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp @@ -72,7 +72,7 @@ RemoteLinuxCustomRunConfiguration::RemoteLinuxCustomRunConfiguration(Target *tar symbolsAspect->setDisplayStyle(SymbolFileAspect::PathChooserDisplay); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); if (HostOsInfo::isAnyUnixHost()) addAspect(); if (HostOsInfo::isAnyUnixHost()) diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp index a0cccf4f6ef..44d1c677281 100644 --- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp @@ -71,7 +71,7 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) symbolsAspect->setDisplayStyle(SymbolFileAspect::LabelDisplay); addAspect(macroExpander()); - addAspect(envAspect); + addAspect(macroExpander(), envAspect); if (HostOsInfo::isAnyUnixHost()) addAspect(); if (HostOsInfo::isAnyUnixHost()) From a33594fa760ad228a6c502ba959003fe055c6a6e Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 May 2022 16:41:46 +0200 Subject: [PATCH 35/58] QmlDesigner: Sprinkle code with Q_UNUSED Less noise when compiling. Change-Id: I7cac44912d5d5165f25ce411db6c6de486688c45 Reviewed-by: Mahmoud Badri --- .../materialbrowser/materialbrowserview.cpp | 14 ++++++++++++++ .../materialeditor/materialeditorview.cpp | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 5ee0dc1888d..896ec7818c5 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -134,6 +134,8 @@ void MaterialBrowserView::modelAboutToBeDetached(Model *model) void MaterialBrowserView::selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) { + Q_UNUSED(lastSelectedNodeList) + ModelNode selectedModel; for (const ModelNode &node : selectedNodeList) { @@ -175,6 +177,8 @@ void MaterialBrowserView::modelNodePreviewPixmapChanged(const ModelNode &node, c void MaterialBrowserView::variantPropertiesChanged(const QList &propertyList, PropertyChangeFlags propertyChange) { + Q_UNUSED(propertyChange) + for (const VariantProperty &property : propertyList) { ModelNode node(property.parentModelNode()); @@ -188,6 +192,8 @@ void MaterialBrowserView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &oldPropertyParent, PropertyChangeFlags propertyChange) { + Q_UNUSED(propertyChange) + if (!isMaterial(node)) return; @@ -225,6 +231,9 @@ void MaterialBrowserView::nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) { + Q_UNUSED(removedNode) + Q_UNUSED(propertyChange) + if (parentProperty.parentModelNode().id() != Constants::MATERIAL_LIB_ID) return; @@ -233,6 +242,9 @@ void MaterialBrowserView::nodeRemoved(const ModelNode &removedNode, void MaterialBrowserView::importsChanged(const QList &addedImports, const QList &removedImports) { + Q_UNUSED(addedImports) + Q_UNUSED(removedImports) + bool hasQuick3DImport = model()->hasImport("QtQuick3D"); if (hasQuick3DImport == m_hasQuick3DImport) @@ -246,6 +258,8 @@ void MaterialBrowserView::importsChanged(const QList &addedImports, cons void MaterialBrowserView::customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) { + Q_UNUSED(data) + if (view == this) return; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 0bcefaec5fe..8257ae6857b 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -675,6 +675,8 @@ WidgetInfo MaterialEditorView::widgetInfo() void MaterialEditorView::selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) { + Q_UNUSED(lastSelectedNodeList) + m_selectedModels.clear(); for (const ModelNode &node : selectedNodeList) { @@ -730,6 +732,9 @@ void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, co void MaterialEditorView::importsChanged(const QList &addedImports, const QList &removedImports) { + Q_UNUSED(addedImports) + Q_UNUSED(removedImports) + m_hasQuick3DImport = model()->hasImport("QtQuick3D"); m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); @@ -787,6 +792,8 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) void MaterialEditorView::customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) { + Q_UNUSED(view) + if (identifier == "selected_material_changed") { m_selectedMaterial = nodeList.first(); QTimer::singleShot(0, this, &MaterialEditorView::resetView); From 0f470982de37d492cdab345a7cfaf916fbc512cf Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 May 2022 17:22:21 +0200 Subject: [PATCH 36/58] PerfProfiler: Remove now-useless check for process success The check nowadays always succeeds, the potential failure is handled in the normal done() handling. Change-Id: I858633871a6b66817c795e7057964afebf79eb85 Reviewed-by: Jarek Kobus --- src/plugins/perfprofiler/perfprofilerruncontrol.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp index e0bf209e274..691d5a3aff0 100644 --- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp +++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp @@ -23,9 +23,9 @@ ** ****************************************************************************/ -#include "perfdatareader.h" -#include "perfprofilerconstants.h" #include "perfprofilerruncontrol.h" + +#include "perfdatareader.h" #include "perfprofilertool.h" #include "perfrunconfigurationaspect.h" #include "perfsettings.h" @@ -124,10 +124,6 @@ public: void start() override { m_process = new QtcProcess(this); - if (!m_process) { - reportFailure(tr("Could not start device process.")); - return; - } connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted); connect(m_process, &QtcProcess::done, this, [this] { From 3ad82a66f9743462ee4a1dc52a855742f05471a3 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 30 May 2022 12:38:46 +0200 Subject: [PATCH 37/58] Python: move pyside detection to runconfig Change-Id: I781c4d005ced0c884081616696eae7f738f38c6f Reviewed-by: Christian Stenger --- .../python/pysidebuildconfiguration.cpp | 19 +-- src/plugins/python/pysidebuildconfiguration.h | 2 +- src/plugins/python/pythonrunconfiguration.cpp | 138 ++++++++++-------- 3 files changed, 79 insertions(+), 80 deletions(-) diff --git a/src/plugins/python/pysidebuildconfiguration.cpp b/src/plugins/python/pysidebuildconfiguration.cpp index c344d0d1708..1df0c4ea672 100644 --- a/src/plugins/python/pysidebuildconfiguration.cpp +++ b/src/plugins/python/pysidebuildconfiguration.cpp @@ -89,24 +89,9 @@ PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id) setWorkingDirectoryProvider([this] { return target()->project()->projectDirectory(); }); } -void PySideBuildStep::updateInterpreter(const Utils::FilePath &python) +void PySideBuildStep::updatePySideProjectPath(const Utils::FilePath &pySideProjectPath) { - Utils::FilePath pySideProjectPath; - const PipPackage pySide6Package("PySide6"); - const PipPackageInfo info = pySide6Package.info(python); - for (const FilePath &file : qAsConst(info.files)) { - if (file.fileName() == HostOsInfo::withExecutableSuffix("pyside6-project")) { - pySideProjectPath = info.location.resolvePath(file); - pySideProjectPath = pySideProjectPath.cleanPath(); - break; - } - } - - if (!pySideProjectPath.isExecutableFile()) - pySideProjectPath = Environment::systemEnvironment().searchInPath("pyside6-project"); - - if (pySideProjectPath.isExecutableFile()) - m_pysideProject->setFilePath(pySideProjectPath); + m_pysideProject->setFilePath(pySideProjectPath); } void PySideBuildStep::doRun() diff --git a/src/plugins/python/pysidebuildconfiguration.h b/src/plugins/python/pysidebuildconfiguration.h index 943c995f472..3a199f25388 100644 --- a/src/plugins/python/pysidebuildconfiguration.h +++ b/src/plugins/python/pysidebuildconfiguration.h @@ -50,7 +50,7 @@ class PySideBuildStep : public ProjectExplorer::AbstractProcessStep Q_OBJECT public: PySideBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); - void updateInterpreter(const Utils::FilePath &python); + void updatePySideProjectPath(const Utils::FilePath &pySideProjectPath); private: Utils::StringAspect *m_pysideProject; diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index 12813f6829b..4c184bbdc19 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -140,85 +140,99 @@ private: class PythonRunConfiguration : public RunConfiguration { public: - PythonRunConfiguration(Target *target, Id id) - : RunConfiguration(target, id) - { - auto interpreterAspect = addAspect(); - interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter"); - interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID); + PythonRunConfiguration(Target *target, Id id); + void currentInterpreterChanged(); +}; - connect(interpreterAspect, &InterpreterAspect::changed, - this, &PythonRunConfiguration::currentInterpreterChanged); +PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) +{ + auto interpreterAspect = addAspect(); + interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter"); + interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID); - connect(PythonSettings::instance(), &PythonSettings::interpretersChanged, - interpreterAspect, &InterpreterAspect::updateInterpreters); + connect(interpreterAspect, &InterpreterAspect::changed, + this, &PythonRunConfiguration::currentInterpreterChanged); - QList interpreters = PythonSettings::detectPythonVenvs( - project()->projectDirectory()); - interpreterAspect->updateInterpreters(PythonSettings::interpreters()); - Interpreter defaultInterpreter = interpreters.isEmpty() - ? PythonSettings::defaultInterpreter() - : interpreters.first(); - if (!defaultInterpreter.command.isExecutableFile()) - defaultInterpreter = PythonSettings::interpreters().value(0); - interpreterAspect->setDefaultInterpreter(defaultInterpreter); + connect(PythonSettings::instance(), &PythonSettings::interpretersChanged, + interpreterAspect, &InterpreterAspect::updateInterpreters); - auto bufferedAspect = addAspect(); - bufferedAspect->setSettingsKey("PythonEditor.RunConfiguation.Buffered"); - bufferedAspect->setLabel(tr("Buffered output"), BoolAspect::LabelPlacement::AtCheckBox); - bufferedAspect->setToolTip(tr("Enabling improves output performance, " - "but results in delayed output.")); + QList interpreters = PythonSettings::detectPythonVenvs( + project()->projectDirectory()); + interpreterAspect->updateInterpreters(PythonSettings::interpreters()); + Interpreter defaultInterpreter = interpreters.isEmpty() ? PythonSettings::defaultInterpreter() + : interpreters.first(); + if (!defaultInterpreter.command.isExecutableFile()) + defaultInterpreter = PythonSettings::interpreters().value(0); + interpreterAspect->setDefaultInterpreter(defaultInterpreter); - auto scriptAspect = addAspect(); - scriptAspect->setSettingsKey("PythonEditor.RunConfiguation.Script"); - scriptAspect->setLabelText(tr("Script:")); - scriptAspect->setDisplayStyle(StringAspect::LabelDisplay); + auto bufferedAspect = addAspect(); + bufferedAspect->setSettingsKey("PythonEditor.RunConfiguation.Buffered"); + bufferedAspect->setLabel(tr("Buffered output"), BoolAspect::LabelPlacement::AtCheckBox); + bufferedAspect->setToolTip(tr("Enabling improves output performance, " + "but results in delayed output.")); - addAspect(target); + auto scriptAspect = addAspect(); + scriptAspect->setSettingsKey("PythonEditor.RunConfiguation.Script"); + scriptAspect->setLabelText(tr("Script:")); + scriptAspect->setDisplayStyle(StringAspect::LabelDisplay); - auto argumentsAspect = addAspect(macroExpander()); + addAspect(target); - addAspect(macroExpander(), nullptr); - addAspect(); + auto argumentsAspect = addAspect(macroExpander()); - setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] { - CommandLine cmd{interpreterAspect->currentInterpreter().command}; - if (!bufferedAspect->value()) - cmd.addArg("-u"); - cmd.addArg(scriptAspect->filePath().fileName()); - cmd.addArgs(argumentsAspect->arguments(), CommandLine::Raw); - return cmd; - }); + addAspect(macroExpander(), nullptr); + addAspect(); - setUpdater([this, scriptAspect] { - const BuildTargetInfo bti = buildTargetInfo(); - const QString script = bti.targetFilePath.toUserOutput(); - setDefaultDisplayName(tr("Run %1").arg(script)); - scriptAspect->setValue(script); - aspect()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir()); - }); + setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] { + CommandLine cmd{interpreterAspect->currentInterpreter().command}; + if (!bufferedAspect->value()) + cmd.addArg("-u"); + cmd.addArg(scriptAspect->filePath().fileName()); + cmd.addArgs(argumentsAspect->arguments(), CommandLine::Raw); + return cmd; + }); - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + setUpdater([this, scriptAspect] { + const BuildTargetInfo bti = buildTargetInfo(); + const QString script = bti.targetFilePath.toUserOutput(); + setDefaultDisplayName(tr("Run %1").arg(script)); + scriptAspect->setValue(script); + aspect()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir()); + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); +} + +void PythonRunConfiguration::currentInterpreterChanged() +{ + const FilePath python = aspect()->currentInterpreter().command; + BuildStepList *buildSteps = target()->activeBuildConfiguration()->buildSteps(); + + Utils::FilePath pySideProjectPath; + const PipPackage pySide6Package("PySide6"); + const PipPackageInfo info = pySide6Package.info(python); + + for (const FilePath &file : qAsConst(info.files)) { + if (file.fileName() == HostOsInfo::withExecutableSuffix("pyside6-project")) { + pySideProjectPath = info.location.resolvePath(file); + pySideProjectPath = pySideProjectPath.cleanPath(); + break; + } } - void currentInterpreterChanged() - { - const FilePath python = aspect()->currentInterpreter().command; + if (auto pySideBuildStep = buildSteps->firstOfType()) + pySideBuildStep->updatePySideProjectPath(pySideProjectPath); - BuildStepList *buildSteps = target()->activeBuildConfiguration()->buildSteps(); - if (auto pySideBuildStep = buildSteps->firstOfType()) - pySideBuildStep->updateInterpreter(python); - - for (FilePath &file : project()->files(Project::AllFiles)) { - if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { - if (document->mimeType() == Constants::C_PY_MIMETYPE) { - PyLSConfigureAssistant::openDocumentWithPython(python, document); - PySideInstaller::checkPySideInstallation(python, document); - } + for (FilePath &file : project()->files(Project::AllFiles)) { + if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { + if (document->mimeType() == Constants::C_PY_MIMETYPE) { + PyLSConfigureAssistant::openDocumentWithPython(python, document); + PySideInstaller::checkPySideInstallation(python, document); } } } -}; +} PythonRunConfigurationFactory::PythonRunConfigurationFactory() { From bc3c4d7453a68d6e5e3720669b53dd418a896a9a Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 16:26:10 +0200 Subject: [PATCH 38/58] Dissolve RunControl::setRunnable Change-Id: I52aea3bea0c4ea90a448f5a77bfa01414bc56702 Reviewed-by: Qt CI Bot Reviewed-by: Jarek Kobus --- .../debugger/analyzer/startremotedialog.cpp | 11 ++--- .../debugger/analyzer/startremotedialog.h | 8 +++- src/plugins/projectexplorer/runcontrol.cpp | 25 +++++++++--- src/plugins/projectexplorer/runcontrol.h | 8 +++- .../tests/localqmlprofilerrunner_test.cpp | 40 +++++++++---------- src/plugins/valgrind/callgrindtool.cpp | 7 ++-- src/plugins/valgrind/memchecktool.cpp | 7 ++-- 7 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/plugins/debugger/analyzer/startremotedialog.cpp b/src/plugins/debugger/analyzer/startremotedialog.cpp index 700b688fad8..9f96ab89a2a 100644 --- a/src/plugins/debugger/analyzer/startremotedialog.cpp +++ b/src/plugins/debugger/analyzer/startremotedialog.cpp @@ -129,15 +129,16 @@ void StartRemoteDialog::validate() d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); } -Runnable StartRemoteDialog::runnable() const +CommandLine StartRemoteDialog::commandLine() const { const Kit *kit = d->kitChooser->currentKit(); const FilePath filePath = DeviceKitAspect::deviceFilePath(kit, d->executable->text()); + return {filePath, d->arguments->text(), CommandLine::Raw}; +} - Runnable r; - r.command = {filePath, d->arguments->text(), CommandLine::Raw}; - r.workingDirectory = FilePath::fromString(d->workingDirectory->text()); - return r; +FilePath StartRemoteDialog::workingDirectory() const +{ + return FilePath::fromString(d->workingDirectory->text()); } } // namespace Debugger diff --git a/src/plugins/debugger/analyzer/startremotedialog.h b/src/plugins/debugger/analyzer/startremotedialog.h index b1b91bc62fd..72d7478fb7d 100644 --- a/src/plugins/debugger/analyzer/startremotedialog.h +++ b/src/plugins/debugger/analyzer/startremotedialog.h @@ -29,7 +29,10 @@ #include -namespace ProjectExplorer { class Runnable; } +namespace Utils { +class CommandLine; +class FilePath; +} // Utils namespace Debugger { @@ -43,7 +46,8 @@ public: explicit StartRemoteDialog(QWidget *parent = nullptr); ~StartRemoteDialog() override; - ProjectExplorer::Runnable runnable() const; + Utils::CommandLine commandLine() const; + Utils::FilePath workingDirectory() const; private: void validate(); diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index a5926c31134..c2b25204ca4 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -904,31 +904,46 @@ const Runnable &RunControl::runnable() const return d->runnable; } -void RunControl::setRunnable(const Runnable &runnable) -{ - d->runnable = runnable; -} - const CommandLine &RunControl::commandLine() const { return d->runnable.command; } +void RunControl::setCommandLine(const CommandLine &command) +{ + d->runnable.command = command; +} + const FilePath &RunControl::workingDirectory() const { return d->runnable.workingDirectory; } +void RunControl::setWorkingDirectory(const FilePath &workingDirectory) +{ + d->runnable.workingDirectory = workingDirectory; +} + const Environment &RunControl::environment() const { return d->runnable.environment; } +void RunControl::setEnvironment(const Environment &environment) +{ + d->runnable.environment = environment; +} + const QVariantHash &RunControl::extraData() const { return d->runnable.extraData; } +void RunControl::setExtraData(const QVariantHash &extraData) +{ + d->runnable.extraData = extraData; +} + QString RunControl::displayName() const { return d->displayName; diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 6df3f5d365b..09d9149ee2c 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -243,12 +243,18 @@ public: Utils::Id runMode() const; const Runnable &runnable() const; - void setRunnable(const Runnable &runnable); const Utils::CommandLine &commandLine() const; + void setCommandLine(const Utils::CommandLine &command); + const Utils::FilePath &workingDirectory() const; + void setWorkingDirectory(const Utils::FilePath &workingDirectory); + const Utils::Environment &environment() const; + void setEnvironment(const Utils::Environment &environment); + const QVariantHash &extraData() const; + void setExtraData(const QVariantHash &extraData); static bool showPromptToStopDialog(const QString &title, const QString &text, const QString &stopButtonText = QString(), diff --git a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp index 15f30b1025e..8d9b1b834dd 100644 --- a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp +++ b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp @@ -36,6 +36,9 @@ #include #include +using namespace ProjectExplorer; +using namespace Utils; + namespace QmlProfiler { namespace Internal { @@ -45,9 +48,8 @@ LocalQmlProfilerRunnerTest::LocalQmlProfilerRunnerTest(QObject *parent) : QObjec void LocalQmlProfilerRunnerTest::testRunner() { - QPointer runControl; + QPointer runControl; QPointer profiler; - ProjectExplorer::Runnable debuggee; QUrl serverUrl; bool running = false; @@ -56,37 +58,35 @@ void LocalQmlProfilerRunnerTest::testRunner() int runCount = 0; int stopCount = 0; - debuggee.command.setExecutable("\\-/|\\-/"); - debuggee.environment = Utils::Environment::systemEnvironment(); - // should not be used anywhere but cannot be empty serverUrl.setScheme(Utils::urlSocketScheme()); serverUrl.setPath("invalid"); - runControl = new ProjectExplorer::RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); - runControl->setRunnable(debuggee); + runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); + runControl->setCommandLine({"\\-/|\\-/", {}}); + profiler = new LocalQmlProfilerSupport(runControl, serverUrl); auto connectRunner = [&]() { - connect(runControl, &ProjectExplorer::RunControl::aboutToStart, this, [&]() { + connect(runControl, &RunControl::aboutToStart, this, [&] { QVERIFY(!started); QVERIFY(!running); ++startCount; started = true; }); - connect(runControl, &ProjectExplorer::RunControl::started, this, [&]() { + connect(runControl, &RunControl::started, this, [&] { QVERIFY(started); QVERIFY(!running); ++runCount; running = true; }); - connect(runControl, &ProjectExplorer::RunControl::stopped, this, [&]() { + connect(runControl, &RunControl::stopped, this, [&] { QVERIFY(started); ++stopCount; running = false; started = false; }); - connect(runControl, &ProjectExplorer::RunControl::finished, this, [&]() { + connect(runControl, &RunControl::finished, this, [&]{ running = false; started = false; }); @@ -110,10 +110,10 @@ void LocalQmlProfilerRunnerTest::testRunner() serverUrl = Utils::urlFromLocalSocket(); // comma is used to specify a test function. In this case, an invalid one. - debuggee.command = Utils::CommandLine(Utils::FilePath::fromString(QCoreApplication::applicationFilePath()), - {"-test", "QmlProfiler,"}); - runControl = new ProjectExplorer::RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); - runControl->setRunnable(debuggee); + runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); + + const FilePath app = FilePath::fromString(QCoreApplication::applicationFilePath()); + runControl->setCommandLine({app, {"-test", "QmlProfiler,"}}); profiler = new LocalQmlProfilerSupport(runControl, serverUrl); connectRunner(); runControl->initiateStart(); @@ -128,11 +128,10 @@ void LocalQmlProfilerRunnerTest::testRunner() QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); - debuggee.command.setArguments({}); serverUrl.clear(); serverUrl = Utils::urlFromLocalHostAndFreePort(); - runControl = new ProjectExplorer::RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); - runControl->setRunnable(debuggee); + runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); + runControl->setCommandLine({app, {}}); profiler = new LocalQmlProfilerSupport(runControl, serverUrl); connectRunner(); runControl->initiateStart(); @@ -148,7 +147,6 @@ void LocalQmlProfilerRunnerTest::testRunner() QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); - debuggee.command.setArguments("-test QmlProfiler,"); serverUrl.setScheme(Utils::urlSocketScheme()); { Utils::TemporaryFile file("file with spaces"); @@ -156,8 +154,8 @@ void LocalQmlProfilerRunnerTest::testRunner() serverUrl.setPath(file.fileName()); } - runControl = new ProjectExplorer::RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); - runControl->setRunnable(debuggee); + runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); + runControl->setCommandLine({app, {"-test", "QmlProfiler,"}}); profiler = new LocalQmlProfilerSupport(runControl, serverUrl); connectRunner(); runControl->initiateStart(); diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp index f5cef8600de..a3bf69504f5 100644 --- a/src/plugins/valgrind/callgrindtool.cpp +++ b/src/plugins/valgrind/callgrindtool.cpp @@ -282,12 +282,13 @@ CallgrindToolPrivate::CallgrindToolPrivate() if (dlg.exec() != QDialog::Accepted) return; m_perspective.select(); + CommandLine command = dlg.commandLine(); auto runControl = new RunControl(CALLGRIND_RUN_MODE); runControl->copyDataFromRunConfiguration(runConfig); runControl->createMainWorker(); - const auto runnable = dlg.runnable(); - runControl->setRunnable(runnable); - runControl->setDisplayName(runnable.command.executable().toUserOutput()); + runControl->setCommandLine(command); + runControl->setWorkingDirectory(dlg.workingDirectory()); + runControl->setDisplayName(command.executable().toUserOutput()); ProjectExplorerPlugin::startRunControl(runControl); }); diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 183daa80256..31decb2da31 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -704,12 +704,13 @@ MemcheckToolPrivate::MemcheckToolPrivate() return; TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID); m_perspective.select(); + const CommandLine cmd = dlg.commandLine(); RunControl *rc = new RunControl(MEMCHECK_RUN_MODE); rc->copyDataFromRunConfiguration(runConfig); rc->createMainWorker(); - const auto runnable = dlg.runnable(); - rc->setRunnable(runnable); - rc->setDisplayName(runnable.command.executable().toUserOutput()); + rc->setCommandLine(cmd); + rc->setWorkingDirectory(dlg.workingDirectory()); + rc->setDisplayName(cmd.toUserOutput()); ProjectExplorerPlugin::startRunControl(rc); }); From 1ffe32187acbf1431b56592019b306a703ec9f09 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 May 2022 17:18:15 +0200 Subject: [PATCH 39/58] ProjectExplorer: Remove RunWorker::runnable() A convienience function that leads to an interface that's "too stiff". Change-Id: Ide2e5fd991707d08690e7e384e9048a30e0828f4 Reviewed-by: Jarek Kobus --- src/plugins/boot2qt/qdbdevicedebugsupport.cpp | 8 +++----- src/plugins/debugger/debuggerruncontrol.cpp | 2 +- src/plugins/perfprofiler/perfprofilerruncontrol.cpp | 4 +--- src/plugins/projectexplorer/runcontrol.cpp | 5 ----- src/plugins/projectexplorer/runcontrol.h | 1 - src/plugins/valgrind/callgrindengine.cpp | 4 ++-- src/plugins/valgrind/valgrindengine.cpp | 4 ++-- 7 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp index d4c4d7dbd4b..44e70d01922 100644 --- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp +++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp @@ -83,8 +83,6 @@ public: int lowerPort = 0; int upperPort = 0; - Runnable r = runnable(); - CommandLine cmd; cmd.setExecutable(device()->filePath(Constants::AppcontrollerFilepath)); @@ -118,11 +116,11 @@ public: } cmd.addArg("--port-range"); cmd.addArg(QString("%1-%2").arg(lowerPort).arg(upperPort)); - cmd.addCommandLineAsArgs(r.command); + cmd.addCommandLineAsArgs(runControl()->commandLine()); m_launcher.setCommand(cmd); - m_launcher.setWorkingDirectory(r.workingDirectory); - m_launcher.setEnvironment(r.environment); + m_launcher.setWorkingDirectory(runControl()->workingDirectory()); + m_launcher.setEnvironment(runControl()->environment()); m_launcher.start(); } diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 25a6d1acc57..132ac508543 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -905,7 +905,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm } } - Runnable inferior = runnable(); + Runnable inferior = runControl->runnable(); const FilePath &debuggerExecutable = m_runParameters.debugger.command.executable(); inferior.command.setExecutable(inferior.command.executable().onDevice(debuggerExecutable)); inferior.workingDirectory = inferior.workingDirectory.onDevice(debuggerExecutable); diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp index 691d5a3aff0..e84836e0b8f 100644 --- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp +++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp @@ -140,12 +140,10 @@ public: reportStopped(); }); - Runnable perfRunnable = runnable(); - CommandLine cmd({device()->filePath("perf"), {"record"}}); cmd.addArgs(m_perfRecordArguments); cmd.addArgs({"-o", "-", "--"}); - cmd.addCommandLineAsArgs(perfRunnable.command, CommandLine::Raw); + cmd.addCommandLineAsArgs(runControl()->commandLine(), CommandLine::Raw); m_process->setCommand(cmd); m_process->start(); diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index c2b25204ca4..d358f4396e5 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1828,11 +1828,6 @@ IDevice::ConstPtr RunWorker::device() const return d->runControl->device(); } -const Runnable &RunWorker::runnable() const -{ - return d->runControl->runnable(); -} - void RunWorker::addStartDependency(RunWorker *dependency) { d->startDependencies.append(dependency); diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 09d9149ee2c..bf82f28073b 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -94,7 +94,6 @@ public: // Part of read-only interface of RunControl for convenience. void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true); IDeviceConstPtr device() const; - const Runnable &runnable() const; // States void initiateStart(); diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp index 61ecb13a3e2..21448de4b73 100644 --- a/src/plugins/valgrind/callgrindengine.cpp +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -71,10 +71,10 @@ CallgrindToolRunner::CallgrindToolRunner(RunControl *runControl) triggerParse(); }); - m_controller.setValgrindRunnable(runnable()); + m_controller.setValgrindRunnable(runControl->runnable()); static int fileCount = 100; - m_valgrindOutputFile = runnable().workingDirectory / QString("callgrind.out.f%1").arg(++fileCount); + m_valgrindOutputFile = runControl->workingDirectory() / QString("callgrind.out.f%1").arg(++fileCount); m_controller.setValgrindOutputFile(m_valgrindOutputFile); setupCallgrindRunner(this); diff --git a/src/plugins/valgrind/valgrindengine.cpp b/src/plugins/valgrind/valgrindengine.cpp index a5d77b33fad..a1be3bd4650 100644 --- a/src/plugins/valgrind/valgrindengine.cpp +++ b/src/plugins/valgrind/valgrindengine.cpp @@ -91,7 +91,7 @@ void ValgrindToolRunner::start() m_runner.setValgrindCommand(valgrind); m_runner.setDevice(device()); - m_runner.setDebuggee(runnable()); + m_runner.setDebuggee(runControl()->runnable()); if (auto aspect = runControl()->aspect()) m_runner.setUseTerminal(aspect->useTerminal); @@ -124,7 +124,7 @@ void ValgrindToolRunner::stop() FilePath ValgrindToolRunner::executable() const { - return runnable().command.executable(); + return runControl()->commandLine().executable(); } QStringList ValgrindToolRunner::genericToolArguments() const From 6431b02947003e6ad4cff56169485b5c4adc42e5 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 May 2022 18:01:30 +0200 Subject: [PATCH 40/58] ProjectExplorer: Derive RunControl::displayName from exectuable Unless explicitly provided. Less user code. Also, de-virtualize RunControl::displayName, it's never overridden. Change-Id: Ibd315aac3c478d993e984af20f7df9077dffe634 Reviewed-by: Jarek Kobus --- src/plugins/projectexplorer/runcontrol.cpp | 2 ++ src/plugins/projectexplorer/runcontrol.h | 2 +- src/plugins/valgrind/callgrindtool.cpp | 4 +--- src/plugins/valgrind/memchecktool.cpp | 4 +--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index d358f4396e5..910e8271615 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -946,6 +946,8 @@ void RunControl::setExtraData(const QVariantHash &extraData) QString RunControl::displayName() const { + if (d->displayName.isEmpty()) + return d->runnable.command.executable().toUserOutput(); return d->displayName; } diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index bf82f28073b..ebb0ec54a0b 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -202,7 +202,7 @@ public: bool supportsReRunning() const; - virtual QString displayName() const; + QString displayName() const; void setDisplayName(const QString &displayName); bool isRunning() const; diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp index a3bf69504f5..03606f9a184 100644 --- a/src/plugins/valgrind/callgrindtool.cpp +++ b/src/plugins/valgrind/callgrindtool.cpp @@ -282,13 +282,11 @@ CallgrindToolPrivate::CallgrindToolPrivate() if (dlg.exec() != QDialog::Accepted) return; m_perspective.select(); - CommandLine command = dlg.commandLine(); auto runControl = new RunControl(CALLGRIND_RUN_MODE); runControl->copyDataFromRunConfiguration(runConfig); runControl->createMainWorker(); - runControl->setCommandLine(command); + runControl->setCommandLine(dlg.commandLine()); runControl->setWorkingDirectory(dlg.workingDirectory()); - runControl->setDisplayName(command.executable().toUserOutput()); ProjectExplorerPlugin::startRunControl(runControl); }); diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 31decb2da31..3ba61a556bc 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -704,13 +704,11 @@ MemcheckToolPrivate::MemcheckToolPrivate() return; TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID); m_perspective.select(); - const CommandLine cmd = dlg.commandLine(); RunControl *rc = new RunControl(MEMCHECK_RUN_MODE); rc->copyDataFromRunConfiguration(runConfig); rc->createMainWorker(); - rc->setCommandLine(cmd); + rc->setCommandLine(dlg.commandLine()); rc->setWorkingDirectory(dlg.workingDirectory()); - rc->setDisplayName(cmd.toUserOutput()); ProjectExplorerPlugin::startRunControl(rc); }); From 1976630bbce933901456584b54af56724f22e29d Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 31 May 2022 09:17:50 +0200 Subject: [PATCH 41/58] Valgrind: Inline ValgrindToolRunner::executable Change-Id: Ia714a5a322ae56b07360871f584ec6f9f1babb5b Reviewed-by: Jarek Kobus --- src/plugins/valgrind/callgrindengine.cpp | 6 +++--- src/plugins/valgrind/memchecktool.cpp | 2 +- src/plugins/valgrind/valgrindengine.cpp | 5 ----- src/plugins/valgrind/valgrindengine.h | 2 -- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp index 21448de4b73..333d713e15b 100644 --- a/src/plugins/valgrind/callgrindengine.cpp +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -25,7 +25,6 @@ #include "callgrindengine.h" -#include "callgrindtool.h" #include "valgrindsettings.h" #include @@ -105,7 +104,7 @@ QStringList CallgrindToolRunner::toolArguments() const arguments << "--callgrind-out-file=" + m_valgrindOutputFile.path(); - arguments << Utils::ProcessArgs::splitArgs(m_settings.callgrindArguments.value()); + arguments << ProcessArgs::splitArgs(m_settings.callgrindArguments.value()); return arguments; } @@ -117,7 +116,8 @@ QString CallgrindToolRunner::progressTitle() const void CallgrindToolRunner::start() { - appendMessage(tr("Profiling %1").arg(executable().toUserOutput()), Utils::NormalMessageFormat); + const FilePath executable = runControl()->commandLine().executable(); + appendMessage(tr("Profiling %1").arg(executable.toUserOutput()), NormalMessageFormat); return ValgrindToolRunner::start(); } diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 3ba61a556bc..1fedbcb4a1e 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -1008,7 +1008,7 @@ void MemcheckToolPrivate::setupRunner(MemcheckToolRunner *runTool) m_loadExternalLogFile->setDisabled(true); const FilePath dir = runControl->project()->projectDirectory(); - const QString name = runTool->executable().fileName(); + const QString name = runControl->commandLine().executable().fileName(); m_errorView->setDefaultSuppressionFile(dir.pathAppended(name + ".supp")); diff --git a/src/plugins/valgrind/valgrindengine.cpp b/src/plugins/valgrind/valgrindengine.cpp index a1be3bd4650..a6a21e7dcb5 100644 --- a/src/plugins/valgrind/valgrindengine.cpp +++ b/src/plugins/valgrind/valgrindengine.cpp @@ -122,11 +122,6 @@ void ValgrindToolRunner::stop() m_runner.stop(); } -FilePath ValgrindToolRunner::executable() const -{ - return runControl()->commandLine().executable(); -} - QStringList ValgrindToolRunner::genericToolArguments() const { QString smcCheckValue; diff --git a/src/plugins/valgrind/valgrindengine.h b/src/plugins/valgrind/valgrindengine.h index 4eb3a119a4c..06e767b7629 100644 --- a/src/plugins/valgrind/valgrindengine.h +++ b/src/plugins/valgrind/valgrindengine.h @@ -47,8 +47,6 @@ public: void start() override; void stop() override; - Utils::FilePath executable() const; - protected: virtual QString progressTitle() const = 0; virtual QStringList toolArguments() const = 0; From 8a1f091bf007ff1fbf2ddeb50ab0d54f6010030c Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 30 May 2022 15:19:02 +0200 Subject: [PATCH 42/58] SftpFileTransfer: Fix error reporting Use "-b" option of sftp command for providing batch file instead of setting a standard input file for sftp process. In order to fix showing the authentication dialog we explicitly pass -o BatchMode=no before the "-b" option, which should re-enable the interactive mode before processing batch file. Change-Id: Iaf3c93929c08fd61373003e394a08439d05fe27e Reviewed-by: Christian Kandeler --- .../devicesupport/sshparameters.cpp | 27 +++++++++---------- src/plugins/remotelinux/linuxdevice.cpp | 4 +-- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp index 8ae238deba5..224a4c6595d 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp @@ -64,24 +64,21 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const "-o", "Port=" + QString::number(port())}; if (!userName().isEmpty()) - args.append({"-o", "User=" + userName()}); + args << "-o" << "User=" + userName(); - const bool keyOnly = authenticationType == - SshParameters::AuthenticationTypeSpecificKey; - if (keyOnly) { - args << "-o" << "IdentitiesOnly=yes"; - args << "-i" << privateKeyFile.path(); - } - if (keyOnly || SshSettings::askpassFilePath().isEmpty()) - args << "-o" << "BatchMode=yes"; + const bool keyOnly = authenticationType == SshParameters::AuthenticationTypeSpecificKey; + if (keyOnly) + args << "-o" << "IdentitiesOnly=yes" << "-i" << privateKeyFile.path(); - bool useTimeout = timeout != 0; - if (useTimeout && HostOsInfo::isWindowsHost() - && binary.toString().toLower().contains("/system32/")) { - useTimeout = false; - } + const QString batchModeEnabled = (keyOnly || SshSettings::askpassFilePath().isEmpty()) + ? QLatin1String("yes") : QLatin1String("no"); + args << "-o" << "BatchMode=" + batchModeEnabled; + + const bool isWindows = HostOsInfo::isWindowsHost() + && binary.toString().toLower().contains("/system32/"); + const bool useTimeout = (timeout != 0) && !isWindows; if (useTimeout) - args << "-o" << ("ConnectTimeout=" + QString::number(timeout)); + args << "-o" << "ConnectTimeout=" + QString::number(timeout); return args; } diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index b2982feb109..d90d01e3ab8 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1609,8 +1609,8 @@ private: + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n'); } m_batchFile->close(); - process().setStandardInputFile(m_batchFile->fileName()); - process().setCommand(CommandLine(sftpBinary, fullConnectionOptions() << host())); + process().setCommand(CommandLine(sftpBinary, fullConnectionOptions() + << "-b" << m_batchFile->fileName() << host())); process().start(); } From 21d3f0d51e75e59f1c7386626e3b24b89834ffe2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 24 May 2022 07:12:02 +0200 Subject: [PATCH 43/58] LoadCoreDialog: Use FileTransfer for download Change-Id: I032eceb85af3416e2488f8ba34ef4092f4aacd8d Reviewed-by: hjk Reviewed-by: Christian Kandeler Reviewed-by: Reviewed-by: Qt CI Bot --- src/plugins/debugger/loadcoredialog.cpp | 77 +++++++------------------ 1 file changed, 21 insertions(+), 56 deletions(-) diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp index 71fc01641d0..ac0da69d36f 100644 --- a/src/plugins/debugger/loadcoredialog.cpp +++ b/src/plugins/debugger/loadcoredialog.cpp @@ -25,23 +25,21 @@ #include "loadcoredialog.h" -#include "debuggerdialogs.h" #include "debuggerkitinformation.h" #include "gdb/gdbengine.h" #include +#include #include -#include -#include +#include #include +#include #include -#include #include #include #include #include -#include #include #include #include @@ -69,7 +67,6 @@ class SelectRemoteFileDialog : public QDialog public: explicit SelectRemoteFileDialog(QWidget *parent); - ~SelectRemoteFileDialog(); void attachToDevice(Kit *k); FilePath localFile() const { return m_localFile; } @@ -77,7 +74,6 @@ public: private: void selectFile(); - void clearWatcher(); QSortFilterProxyModel m_model; DeviceFileSystemModel m_fileSystemModel; @@ -86,7 +82,7 @@ private: QDialogButtonBox *m_buttonBox; FilePath m_localFile; FilePath m_remoteFile; - QFutureWatcher *m_watcher = nullptr; + FileTransfer m_fileTransfer; }; SelectRemoteFileDialog::SelectRemoteFileDialog(QWidget *parent) @@ -119,11 +115,21 @@ SelectRemoteFileDialog::SelectRemoteFileDialog(QWidget *parent) connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &SelectRemoteFileDialog::selectFile); -} -SelectRemoteFileDialog::~SelectRemoteFileDialog() -{ - clearWatcher(); + connect(&m_fileTransfer, &FileTransfer::done, this, [this](const ProcessResultData &result) { + const bool success = result.m_error == QProcess::UnknownError + && result.m_exitStatus == QProcess::NormalExit + && result.m_exitCode == 0; + if (success) { + m_textBrowser->append(tr("Download of remote file succeeded.")); + accept(); + } else { + m_textBrowser->append(tr("Download of remote file failed: %1") + .arg(result.m_errorString)); + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + m_fileSystemView->setEnabled(true); + } + }); } void SelectRemoteFileDialog::attachToDevice(Kit *k) @@ -135,22 +141,6 @@ void SelectRemoteFileDialog::attachToDevice(Kit *k) m_fileSystemModel.setDevice(device); } -static void copyFile(QFutureInterface &futureInterface, const FilePath &remoteSource, - const FilePath &localDestination) -{ - // TODO: this should be implemented transparently in FilePath::copyFile() - // The code here should be just: - // - // futureInterface.reportResult(remoteSource.copyFile(localDestination)); - // - // The implementation below won't handle really big files, like core files. - - const QByteArray data = remoteSource.fileContents(); - if (futureInterface.isCanceled()) - return; - futureInterface.reportResult(localDestination.writeFileContents(data)); -} - void SelectRemoteFileDialog::selectFile() { QModelIndex idx = m_model.mapToSource(m_fileSystemView->currentIndex()); @@ -161,7 +151,7 @@ void SelectRemoteFileDialog::selectFile() m_fileSystemView->setEnabled(false); { - Utils::TemporaryFile localFile("remotecore-XXXXXX"); + TemporaryFile localFile("remotecore-XXXXXX"); localFile.open(); m_localFile = FilePath::fromString(localFile.fileName()); } @@ -169,33 +159,8 @@ void SelectRemoteFileDialog::selectFile() idx = idx.sibling(idx.row(), 1); m_remoteFile = FilePath::fromVariant(m_fileSystemModel.data(idx, DeviceFileSystemModel::PathRole)); - clearWatcher(); - m_watcher = new QFutureWatcher(this); - auto future = runAsync(copyFile, m_remoteFile, m_localFile); - connect(m_watcher, &QFutureWatcher::finished, this, [this] { - const bool success = m_watcher->result(); - if (success) { - m_textBrowser->append(tr("Download of remote file succeeded.")); - accept(); - } else { - m_textBrowser->append(tr("Download of remote file failed.")); - m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - m_fileSystemView->setEnabled(true); - } - }); - m_watcher->setFuture(future); -} - -void SelectRemoteFileDialog::clearWatcher() -{ - if (!m_watcher) - return; - - m_watcher->disconnect(); - m_watcher->future().cancel(); - m_watcher->future().waitForFinished(); - delete m_watcher; - m_watcher = nullptr; + m_fileTransfer.setFilesToTransfer({{m_remoteFile, m_localFile}}); + m_fileTransfer.start(); } /////////////////////////////////////////////////////////////////////// From a7fa8c46eee2428a19c2100b98b5dad8d22257c1 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 25 May 2022 20:08:12 +0200 Subject: [PATCH 44/58] LinuxProcessInterface: Fix error reporting on failed to start Change-Id: I2af4a13289458e6270452a254cf90f7bd9cc3684 Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/remotelinux/linuxdevice.cpp | 24 +++++++++++++++++-- .../remotelinux/linuxprocessinterface.h | 1 + src/plugins/remotelinux/rsyncdeploystep.cpp | 13 +++++++++- src/plugins/remotelinux/sshprocessinterface.h | 1 + 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index d90d01e3ab8..93b93ca416f 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -448,6 +448,11 @@ void SshProcessInterface::handleStarted(qint64 processId) emitStarted(processId); } +void SshProcessInterface::handleDone(const ProcessResultData &resultData) +{ + emit done(resultData); +} + void SshProcessInterface::handleReadyReadStandardOutput(const QByteArray &outputData) { emit readyRead(outputData, {}); @@ -580,6 +585,20 @@ void LinuxProcessInterface::handleStarted(qint64 processId) emitStarted(processId); } +void LinuxProcessInterface::handleDone(const ProcessResultData &resultData) +{ + ProcessResultData finalData = resultData; + if (!m_pidParsed) { + finalData.m_error = QProcess::FailedToStart; + if (!m_error.isEmpty()) { + if (!finalData.m_errorString.isEmpty()) + finalData.m_errorString += "\n"; + finalData.m_errorString += QString::fromLocal8Bit(m_error); + } + } + emit done(finalData); +} + void LinuxProcessInterface::handleReadyReadStandardOutput(const QByteArray &outputData) { if (m_pidParsed) { @@ -690,8 +709,9 @@ void SshProcessInterfacePrivate::handleStarted() void SshProcessInterfacePrivate::handleDone() { - m_connectionHandle.reset(); - emit q->done(m_process.resultData()); + if (m_connectionHandle) // TODO: should it disconnect from signals first? + m_connectionHandle.release()->deleteLater(); + q->handleDone(m_process.resultData()); } void SshProcessInterfacePrivate::handleReadyReadStandardOutput() diff --git a/src/plugins/remotelinux/linuxprocessinterface.h b/src/plugins/remotelinux/linuxprocessinterface.h index 85ea19d4c01..c6ee39384b6 100644 --- a/src/plugins/remotelinux/linuxprocessinterface.h +++ b/src/plugins/remotelinux/linuxprocessinterface.h @@ -44,6 +44,7 @@ private: void sendControlSignal(Utils::ControlSignal controlSignal) override; void handleStarted(qint64 processId) final; + void handleDone(const Utils::ProcessResultData &resultData); void handleReadyReadStandardOutput(const QByteArray &outputData) final; void handleReadyReadStandardError(const QByteArray &errorData) final; diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 99eeaf413cf..b4d9929d76b 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -51,12 +51,23 @@ public: { connect(&m_mkdir, &QtcProcess::done, this, [this] { if (m_mkdir.result() != ProcessResult::FinishedWithSuccess) { - emit errorMessage(tr("Failed to create remote directories: %1").arg(m_mkdir.stdErr())); + QString finalMessage = m_mkdir.errorString(); + const QString stdErr = m_mkdir.stdErr(); + if (!stdErr.isEmpty()) { + if (!finalMessage.isEmpty()) + finalMessage += '\n'; + finalMessage += stdErr; + } + emit errorMessage(tr("Deploy via rsync: failed to create remote directories:") + + '\n' + finalMessage); setFinished(); return; } deployFiles(); }); + connect(&m_mkdir, &QtcProcess::readyReadStandardError, this, [this] { + emit stdErrData(QString::fromLocal8Bit(m_mkdir.readAllStandardError())); + }); connect(&m_fileTransfer, &FileTransfer::progress, this, &AbstractRemoteLinuxDeployService::stdOutData); connect(&m_fileTransfer, &FileTransfer::done, this, [this](const ProcessResultData &result) { diff --git a/src/plugins/remotelinux/sshprocessinterface.h b/src/plugins/remotelinux/sshprocessinterface.h index 241b4c0c87e..e60009ded2a 100644 --- a/src/plugins/remotelinux/sshprocessinterface.h +++ b/src/plugins/remotelinux/sshprocessinterface.h @@ -50,6 +50,7 @@ protected: private: virtual void handleStarted(qint64 processId); + virtual void handleDone(const Utils::ProcessResultData &resultData); virtual void handleReadyReadStandardOutput(const QByteArray &outputData); virtual void handleReadyReadStandardError(const QByteArray &errorData); From add9abd672ac2ff7df4fe8e5ea2ad0d7adc46344 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 27 May 2022 10:21:07 +0200 Subject: [PATCH 45/58] GenericDirectUploadService: Fix error handling of stat process Don't rely on error detection based on error string contents. The translators may have provided empty strings (by mistake) and this may influence the behavior. Change-Id: I035ead2ddd93787b268798607d3f856981f0c862 Reviewed-by: Christian Kandeler Reviewed-by: Reviewed-by: Qt CI Bot --- .../genericdirectuploadservice.cpp | 28 +++++++++++-------- .../remotelinux/genericdirectuploadservice.h | 2 +- .../remotelinux/linuxprocessinterface.h | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp index c784e346b9a..f4aed62bfd6 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.cpp +++ b/src/plugins/remotelinux/genericdirectuploadservice.cpp @@ -136,21 +136,27 @@ void GenericDirectUploadService::doDeploy() } QDateTime GenericDirectUploadService::timestampFromStat(const DeployableFile &file, - QtcProcess *statProc, - const QString &errorMsg) + QtcProcess *statProc) { - QString errorDetails; - if (!errorMsg.isEmpty()) - errorDetails = errorMsg; - else if (statProc->exitCode() != 0) - errorDetails = QString::fromUtf8(statProc->readAllStandardError()); - if (!errorDetails.isEmpty()) { + bool succeeded = false; + QString error; + if (statProc->error() == QProcess::FailedToStart) { + error = tr("Failed to start \"stat\": %1").arg(statProc->errorString()); + } else if (statProc->exitStatus() == QProcess::CrashExit) { + error = tr("\"stat\" crashed."); + } else if (statProc->exitCode() != 0) { + error = tr("\"stat\" failed with exit code %1: %2") + .arg(statProc->exitCode()).arg(statProc->stdErr()); + } else { + succeeded = true; + } + if (!succeeded) { emit warningMessage(tr("Failed to retrieve remote timestamp for file \"%1\". " "Incremental deployment will not work. Error message was: %2") - .arg(file.remoteFilePath(), errorDetails)); + .arg(file.remoteFilePath(), error)); return QDateTime(); } - QByteArray output = statProc->readAllStandardOutput().trimmed(); + const QByteArray output = statProc->readAllStandardOutput().trimmed(); const QString warningString(tr("Unexpected stat output for remote file \"%1\": %2") .arg(file.remoteFilePath()).arg(QString::fromUtf8(output))); if (!output.startsWith(file.remoteFilePath().toUtf8())) { @@ -205,7 +211,7 @@ void GenericDirectUploadService::runStat(const DeployableFile &file) QTC_ASSERT(d->state == state, return); const DeployableFile file = d->getFileForProcess(statProc); QTC_ASSERT(file.isValid(), return); - const QDateTime timestamp = timestampFromStat(file, statProc, statProc->errorString()); + const QDateTime timestamp = timestampFromStat(file, statProc); statProc->deleteLater(); switch (state) { case PreChecking: diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h index 42af64e3823..ffbd9fc866d 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.h +++ b/src/plugins/remotelinux/genericdirectuploadservice.h @@ -61,7 +61,7 @@ protected: private: void runStat(const ProjectExplorer::DeployableFile &file); QDateTime timestampFromStat(const ProjectExplorer::DeployableFile &file, - Utils::QtcProcess *statProc, const QString &errorMsg); + Utils::QtcProcess *statProc); void checkForStateChangeOnRemoteProcFinished(); QList collectFilesToUpload( diff --git a/src/plugins/remotelinux/linuxprocessinterface.h b/src/plugins/remotelinux/linuxprocessinterface.h index c6ee39384b6..110e6939f8c 100644 --- a/src/plugins/remotelinux/linuxprocessinterface.h +++ b/src/plugins/remotelinux/linuxprocessinterface.h @@ -44,7 +44,7 @@ private: void sendControlSignal(Utils::ControlSignal controlSignal) override; void handleStarted(qint64 processId) final; - void handleDone(const Utils::ProcessResultData &resultData); + void handleDone(const Utils::ProcessResultData &resultData) final; void handleReadyReadStandardOutput(const QByteArray &outputData) final; void handleReadyReadStandardError(const QByteArray &errorData) final; From b336ebafc32190808ceb47c9bc5b33c95dbd48b6 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 31 May 2022 10:14:50 +0200 Subject: [PATCH 46/58] QmlPuppet: Silence warnings Change-Id: I25ebc3b33d5e636176f87af35ee2316e302f233a Reviewed-by: Christian Kandeler --- .../qml2puppet/instances/qt5informationnodeinstanceserver.cpp | 4 ++++ .../qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index c3fc4d4e076..8059a240a6c 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -837,6 +837,8 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D(bool timerC auto helper = qobject_cast(m_3dHelper); if (helper) helper->storeToolState(helper->globalStateId(), helper->lastSceneIdKey(), QVariant(sceneId), 0); +#else + Q_UNUSED(timerCall) #endif } @@ -1157,6 +1159,8 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView(const Reques {PuppetToCreatorCommand::RenderModelNodePreviewImage, QVariant::fromValue(imgContainer)}); } +#else + Q_UNUSED(cmd) #endif } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp index 95dd4a7effb..eec52e99927 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp @@ -82,6 +82,9 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo m_dummyRootViewCreateFunction = "createViewForNode"; Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags); +#else + Q_UNUSED(objectNodeInstance) + Q_UNUSED(flags) #endif // QUICK3D_MODULE } From dcfa15ff1742bf8a6f5d55892edc75150aa34ae0 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 6 May 2022 15:15:46 +0200 Subject: [PATCH 47/58] GitLab: Allow browsing and cloning projects Change-Id: I1cc877ea6b5a55ae7bdb8e7a529afeb08d09e0c0 Reviewed-by: Reviewed-by: David Schulz --- src/plugins/gitlab/CMakeLists.txt | 2 + src/plugins/gitlab/gitlab.qbs | 5 + src/plugins/gitlab/gitlabclonedialog.cpp | 263 +++++++++++++++++ src/plugins/gitlab/gitlabclonedialog.h | 74 +++++ src/plugins/gitlab/gitlabdialog.cpp | 294 +++++++++++++++++++ src/plugins/gitlab/gitlabdialog.h | 81 +++++ src/plugins/gitlab/gitlabdialog.ui | 268 +++++++++++++++++ src/plugins/gitlab/gitlabplugin.cpp | 45 +++ src/plugins/gitlab/gitlabplugin.h | 3 + src/plugins/gitlab/gitlabprojectsettings.cpp | 1 + src/plugins/gitlab/queryrunner.cpp | 34 +++ src/plugins/gitlab/queryrunner.h | 11 +- src/plugins/gitlab/resultparser.cpp | 111 +++++++ src/plugins/gitlab/resultparser.h | 38 ++- 14 files changed, 1228 insertions(+), 2 deletions(-) create mode 100644 src/plugins/gitlab/gitlabclonedialog.cpp create mode 100644 src/plugins/gitlab/gitlabclonedialog.h create mode 100644 src/plugins/gitlab/gitlabdialog.cpp create mode 100644 src/plugins/gitlab/gitlabdialog.h create mode 100644 src/plugins/gitlab/gitlabdialog.ui diff --git a/src/plugins/gitlab/CMakeLists.txt b/src/plugins/gitlab/CMakeLists.txt index 4e45c77bb71..7bde1d67560 100644 --- a/src/plugins/gitlab/CMakeLists.txt +++ b/src/plugins/gitlab/CMakeLists.txt @@ -3,6 +3,8 @@ add_qtc_plugin(GitLab PLUGIN_DEPENDS Core ProjectExplorer Git VcsBase DEPENDS Utils SOURCES + gitlabclonedialog.cpp gitlabclonedialog.h + gitlabdialog.cpp gitlabdialog.h gitlabdialog.ui gitlaboptionspage.cpp gitlaboptionspage.h gitlabparameters.cpp gitlabparameters.h gitlabplugin.cpp gitlabplugin.h diff --git a/src/plugins/gitlab/gitlab.qbs b/src/plugins/gitlab/gitlab.qbs index 03a06ec5cc9..96076b85d1a 100644 --- a/src/plugins/gitlab/gitlab.qbs +++ b/src/plugins/gitlab/gitlab.qbs @@ -10,6 +10,11 @@ QtcPlugin { Depends { name: "Utils" } files: [ + "gitlabclonedialog.cpp", + "gitlabclonedialog.h", + "gitlabdialog.cpp", + "gitlabdialog.h", + "gitlabdialog.ui", "gitlaboptionspage.cpp", "gitlaboptionspage.h", "gitlabparameters.cpp", diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp new file mode 100644 index 00000000000..e1dabccc28a --- /dev/null +++ b/src/plugins/gitlab/gitlabclonedialog.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "gitlabclonedialog.h" + +#include "gitlabprojectsettings.h" +#include "resultparser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GitLab { + +GitLabCloneDialog::GitLabCloneDialog(const Project &project, QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(tr("Clone Repository")); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(new QLabel(tr("Specify repository URL, checkout path and directory."))); + QHBoxLayout *centerLayout = new QHBoxLayout; + QFormLayout *form = new QFormLayout; + m_repositoryCB = new QComboBox(this); + m_repositoryCB->addItems({project.sshUrl, project.httpUrl}); + form->addRow(tr("Repository"), m_repositoryCB); + m_pathChooser = new Utils::PathChooser(this); + m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); + form->addRow(tr("Path"), m_pathChooser); + m_directoryLE = new Utils::FancyLineEdit(this); + m_directoryLE->setValidationFunction([this](Utils::FancyLineEdit *e, QString *msg) { + const Utils::FilePath fullPath = m_pathChooser->filePath().pathAppended(e->text()); + bool alreadyExists = fullPath.exists(); + if (alreadyExists && msg) + *msg = tr("Path \"%1\" already exists.").arg(fullPath.toUserOutput()); + return !alreadyExists; + }); + form->addRow(tr("Directory"), m_directoryLE); + m_submodulesCB = new QCheckBox(this); + form->addRow(tr("Recursive"), m_submodulesCB); + centerLayout->addLayout(form); + m_cloneOutput = new QPlainTextEdit(this); + m_cloneOutput->setReadOnly(true); + centerLayout->addWidget(m_cloneOutput); + layout->addLayout(centerLayout); + m_infoLabel = new Utils::InfoLabel(this); + layout->addWidget(m_infoLabel); + layout->addStretch(1); + auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this); + m_cloneButton = new QPushButton(tr("Clone"), this); + buttons->addButton(m_cloneButton, QDialogButtonBox::ActionRole); + m_cancelButton = buttons->button(QDialogButtonBox::Cancel); + layout->addWidget(buttons); + setLayout(layout); + + m_pathChooser->setFilePath(Core::DocumentManager::projectsDirectory()); + auto [host, path, port] + = GitLabProjectSettings::remotePartsFromRemote(m_repositoryCB->currentText()); + int slashIndex = path.indexOf('/'); + QTC_ASSERT(slashIndex > 0, return); + m_directoryLE->setText(path.mid(slashIndex + 1)); + + connect(m_pathChooser, &Utils::PathChooser::pathChanged, this, [this]() { + m_directoryLE->validate(); + GitLabCloneDialog::updateUi(); + }); + connect(m_directoryLE, &Utils::FancyLineEdit::textChanged, this, &GitLabCloneDialog::updateUi); + connect(m_cloneButton, &QPushButton::clicked, this, &GitLabCloneDialog::cloneProject); + connect(m_cancelButton, &QPushButton::clicked, + this, &GitLabCloneDialog::cancel); + connect(this, &QDialog::rejected, this, [this]() { + if (m_commandRunning) { + cancel(); + QApplication::restoreOverrideCursor(); + return; + } + }); + + updateUi(); + resize(575, 265); +} + +void GitLabCloneDialog::updateUi() +{ + bool pathValid = m_pathChooser->isValid(); + bool directoryValid = m_directoryLE->isValid(); + m_cloneButton->setEnabled(pathValid && directoryValid); + if (!pathValid) { + m_infoLabel->setText(m_pathChooser->errorMessage()); + m_infoLabel->setType(Utils::InfoLabel::Error); + } else if (!directoryValid) { + m_infoLabel->setText(m_directoryLE->errorMessage()); + m_infoLabel->setType(Utils::InfoLabel::Error); + } + m_infoLabel->setVisible(!pathValid || !directoryValid); +} + +void GitLabCloneDialog::cloneProject() +{ + Core::IVersionControl *vc = Core::VcsManager::versionControl(Utils::Id::fromString("G.Git")); + QTC_ASSERT(vc, return); + const QStringList extraArgs = m_submodulesCB->isChecked() ? QStringList{ "--recursive" } + : QStringList{}; + m_command = vc->createInitialCheckoutCommand(m_repositoryCB->currentText(), + m_pathChooser->absoluteFilePath(), + m_directoryLE->text(), extraArgs); + const Utils::FilePath workingDirectory = m_pathChooser->absoluteFilePath(); + m_command->setProgressiveOutput(true); + connect(m_command, &Utils::ShellCommand::stdOutText, this, [this](const QString &text) { + m_cloneOutput->appendPlainText(text); + }); + connect(m_command, &Utils::ShellCommand::stdErrText, this, [this](const QString &text) { + m_cloneOutput->appendPlainText(text); + }); + connect(m_command, &Utils::ShellCommand::finished, this, &GitLabCloneDialog::cloneFinished); + QApplication::setOverrideCursor(Qt::WaitCursor); + + m_cloneOutput->clear(); + m_cloneButton->setEnabled(false); + m_pathChooser->setReadOnly(true); + m_directoryLE->setReadOnly(true); + m_commandRunning = true; + m_command->execute(); +} + +void GitLabCloneDialog::cancel() +{ + if (m_commandRunning) { + m_cloneOutput->appendPlainText(tr("User canceled process.")); + m_cancelButton->setEnabled(false); + m_command->cancel(); // FIXME does not cancel the git processes... QTCREATORBUG-27567 + } else { + reject(); + } +} + +static Utils::FilePaths scanDirectoryForFiles(const Utils::FilePath &directory) +{ + Utils::FilePaths result; + const Utils::FilePaths entries = directory.dirEntries(QDir::AllEntries | QDir::NoDotAndDotDot); + + for (const Utils::FilePath &entry : entries) { + if (entry.isDir()) + result.append(scanDirectoryForFiles(entry)); + else + result.append(entry); + } + return result; +} + +void GitLabCloneDialog::cloneFinished(bool ok, int exitCode) +{ + const bool success = (ok && exitCode == 0); + m_commandRunning = false; + delete m_command; + m_command = nullptr; + + const QString emptyLine("\n\n"); + m_cloneOutput->appendPlainText(emptyLine); + QApplication::restoreOverrideCursor(); + + if (success) { + m_cloneOutput->appendPlainText(tr("Cloning succeeded.") + emptyLine); + m_cloneButton->setEnabled(false); + + const Utils::FilePath base = m_pathChooser->filePath().pathAppended(m_directoryLE->text()); + Utils::FilePaths filesWeMayOpen + = Utils::filtered(scanDirectoryForFiles(base), [](const Utils::FilePath &f) { + return ProjectExplorer::ProjectManager::canOpenProjectForMimeType( + Utils::mimeTypeForFile(f)); + }); + + // limit the files to the most top-level item(s) + int minimum = std::numeric_limits::max(); + for (const Utils::FilePath &f : filesWeMayOpen) { + int parentCount = f.toString().count('/'); + if (parentCount < minimum) + minimum = parentCount; + } + filesWeMayOpen = Utils::filtered(filesWeMayOpen, [minimum](const Utils::FilePath &f) { + return f.toString().count('/') == minimum; + }); + + hide(); // avoid to many dialogs.. FIXME: maybe change to some wizard approach? + if (filesWeMayOpen.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), + tr("Cloned project does not have a project file that can be " + "opened. Try importing the project as a generic project.")); + accept(); + } else { + const QStringList pFiles = Utils::transform(filesWeMayOpen, + [base](const Utils::FilePath &f) { + return f.relativePath(base).toUserOutput(); + }); + bool ok = false; + const QString fileToOpen + = QInputDialog::getItem(this, tr("Open Project"), + tr("Choose the project file to be opened."), + pFiles, 0, false, &ok); + accept(); + if (ok && !fileToOpen.isEmpty()) + ProjectExplorer::ProjectExplorerPlugin::openProject(base.pathAppended(fileToOpen)); + } + } else { + m_cloneOutput->appendPlainText(tr("Cloning failed.") + emptyLine); + const Utils::FilePath fullPath = m_pathChooser->filePath() + .pathAppended(m_directoryLE->text()); + fullPath.removeRecursively(); + m_cloneButton->setEnabled(true); + m_cancelButton->setEnabled(true); + m_pathChooser->setReadOnly(false); + m_directoryLE->setReadOnly(false); + m_directoryLE->validate(); + } +} + +} // namespace GitLab diff --git a/src/plugins/gitlab/gitlabclonedialog.h b/src/plugins/gitlab/gitlabclonedialog.h new file mode 100644 index 00000000000..5d3b19543aa --- /dev/null +++ b/src/plugins/gitlab/gitlabclonedialog.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +QT_BEGIN_NAMESPACE +class QCheckBox; +class QComboBox; +class QPlainTextEdit; +class QPushButton; +QT_END_NAMESPACE + +namespace Core { class ShellCommand; } + +namespace Utils { +class FancyLineEdit; +class InfoLabel; +class PathChooser; +} + +namespace GitLab { + +class Project; + +class GitLabCloneDialog : public QDialog +{ + Q_DECLARE_TR_FUNCTIONS(GitLab::GitLabCloneDialog) +public: + explicit GitLabCloneDialog(const Project &project, QWidget *parent = nullptr); + +private: + void updateUi(); + void cloneProject(); + void cancel(); + void cloneFinished(bool ok, int exitCode); + + QComboBox * m_repositoryCB = nullptr; + QCheckBox *m_submodulesCB = nullptr; + QPushButton *m_cloneButton = nullptr; + QPushButton *m_cancelButton = nullptr; + QPlainTextEdit *m_cloneOutput = nullptr; + Utils::PathChooser *m_pathChooser = nullptr; + Utils::FancyLineEdit *m_directoryLE = nullptr; + Utils::InfoLabel *m_infoLabel = nullptr; + Core::ShellCommand *m_command = nullptr; + bool m_commandRunning = false; +}; + +} // namespace GitLab diff --git a/src/plugins/gitlab/gitlabdialog.cpp b/src/plugins/gitlab/gitlabdialog.cpp new file mode 100644 index 00000000000..6af65fef2dc --- /dev/null +++ b/src/plugins/gitlab/gitlabdialog.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "gitlabdialog.h" + +#include "gitlabclonedialog.h" +#include "gitlabparameters.h" +#include "gitlabplugin.h" +#include "gitlabprojectsettings.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace GitLab { + +GitLabDialog::GitLabDialog(QWidget *parent) + : QDialog(parent) + , m_lastTreeViewQuery(Query::NoQuery) +{ + m_ui.setupUi(this); + m_clonePB = new QPushButton(Utils::Icons::DOWNLOAD.icon(), tr("Clone..."), this); + m_ui.buttonBox->addButton(m_clonePB, QDialogButtonBox::ActionRole); + m_clonePB->setEnabled(false); + + updateRemotes(); + + connect(m_ui.remoteCB, QOverload::of(&QComboBox::currentIndexChanged), + this, &GitLabDialog::requestMainViewUpdate); + connect(m_ui.searchLE, &QLineEdit::returnPressed, this, &GitLabDialog::querySearch); + connect(m_ui.searchPB, &QPushButton::clicked, this, &GitLabDialog::querySearch); + connect(m_clonePB, &QPushButton::clicked, this, &GitLabDialog::cloneSelected); + connect(m_ui.firstTB, &QToolButton::clicked, this, &GitLabDialog::queryFirstPage); + connect(m_ui.previousTB, &QToolButton::clicked, this, &GitLabDialog::queryPreviousPage); + connect(m_ui.nextTB, &QToolButton::clicked, this, &GitLabDialog::queryNextPage); + connect(m_ui.lastTB, &QToolButton::clicked, this, &GitLabDialog::queryLastPage); + requestMainViewUpdate(); +} + +void GitLabDialog::resetTreeView(QTreeView *treeView, QAbstractItemModel *model) +{ + auto oldModel = treeView->model(); + treeView->setModel(model); + delete oldModel; + if (model) { + connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, [this](const QItemSelection &selected, const QItemSelection &) { + m_clonePB->setEnabled(!selected.isEmpty()); + }); + m_clonePB->setEnabled(!treeView->selectionModel()->selectedIndexes().isEmpty()); + } +} + +void GitLabDialog::updateRemotes() +{ + m_ui.remoteCB->clear(); + const GitLabParameters *global = GitLabPlugin::globalParameters(); + for (const GitLabServer &server : qAsConst(global->gitLabServers)) + m_ui.remoteCB->addItem(server.displayString(), QVariant::fromValue(server)); + + m_ui.remoteCB->setCurrentIndex(m_ui.remoteCB->findData( + QVariant::fromValue(global->currentDefaultServer()))); +} + +void GitLabDialog::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) + return; + QDialog::keyPressEvent(event); +} + +void GitLabDialog::requestMainViewUpdate() +{ + m_lastPageInformation = PageInformation(); + m_lastTreeViewQuery = Query(Query::NoQuery); + + m_ui.mainLabel->setText({}); + m_ui.detailsLabel->setText({}); + m_ui.treeViewTitle->setText({}); + m_ui.searchLE->setText({}); + resetTreeView(m_ui.treeView, nullptr); + updatePageButtons(); + + bool linked = false; + m_currentServerId = Utils::Id(); + if (auto project = ProjectExplorer::SessionManager::startupProject()) { + GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project); + if (projSettings->isLinked()) { + m_currentServerId = projSettings->currentServer(); + linked = true; + } + } + if (!m_currentServerId.isValid()) + m_currentServerId = m_ui.remoteCB->currentData().value().id; + if (m_currentServerId.isValid()) { + const GitLabParameters *global = GitLabPlugin::globalParameters(); + const GitLabServer server = global->serverForId(m_currentServerId); + m_ui.remoteCB->setCurrentIndex(m_ui.remoteCB->findData(QVariant::fromValue(server))); + } + m_ui.remoteCB->setEnabled(!linked); + + const Query query(Query::User); + QueryRunner *runner = new QueryRunner(query, m_currentServerId, this); + connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) { + handleUser(ResultParser::parseUser(result)); + }); + connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); }); + runner->start(); +} + +void GitLabDialog::updatePageButtons() +{ + if (m_lastPageInformation.currentPage == -1) { + m_ui.currentPage->setVisible(false); + m_ui.firstTB->setVisible(false); + m_ui.lastTB->setVisible(false); + m_ui.previousTB->setVisible(false); + m_ui.nextTB->setVisible(false); + } else { + m_ui.currentPage->setText(QString::number(m_lastPageInformation.currentPage)); + m_ui.currentPage->setVisible(true); + m_ui.firstTB->setVisible(true); + m_ui.lastTB->setVisible(true); + } + if (m_lastPageInformation.currentPage > 1) { + m_ui.firstTB->setEnabled(true); + m_ui.previousTB->setText(QString::number(m_lastPageInformation.currentPage - 1)); + m_ui.previousTB->setVisible(true); + } else { + m_ui.firstTB->setEnabled(false); + m_ui.previousTB->setVisible(false); + } + if (m_lastPageInformation.currentPage < m_lastPageInformation.totalPages) { + m_ui.lastTB->setEnabled(true); + m_ui.nextTB->setText(QString::number(m_lastPageInformation.currentPage + 1)); + m_ui.nextTB->setVisible(true); + } else { + m_ui.lastTB->setEnabled(false); + m_ui.nextTB->setVisible(false); + } +} + +void GitLabDialog::queryFirstPage() +{ + QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return); + QTC_ASSERT(m_lastPageInformation.currentPage != -1, return); + m_lastTreeViewQuery.setPageParameter(1); + fetchProjects(); +} + +void GitLabDialog::queryPreviousPage() +{ + QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return); + QTC_ASSERT(m_lastPageInformation.currentPage != -1, return); + m_lastTreeViewQuery.setPageParameter(m_lastPageInformation.currentPage - 1); + fetchProjects(); +} + +void GitLabDialog::queryNextPage() +{ + QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return); + QTC_ASSERT(m_lastPageInformation.currentPage != -1, return); + m_lastTreeViewQuery.setPageParameter(m_lastPageInformation.currentPage + 1); + fetchProjects(); +} + +void GitLabDialog::queryLastPage() +{ + QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return); + QTC_ASSERT(m_lastPageInformation.currentPage != -1, return); + m_lastTreeViewQuery.setPageParameter(m_lastPageInformation.totalPages); + fetchProjects(); +} + +void GitLabDialog::querySearch() +{ + QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return); + m_lastTreeViewQuery.setPageParameter(-1); + m_lastTreeViewQuery.setAdditionalParameters({"search=" + m_ui.searchLE->text()}); + fetchProjects(); +} + +void GitLabDialog::handleUser(const User &user) +{ + m_lastPageInformation = {}; + m_currentUserId = user.id; + + if (!user.error.message.isEmpty()) { + // TODO + if (user.error.code == 1) { + m_ui.mainLabel->setText(tr("Not logged in.")); + m_ui.detailsLabel->setText(tr("Insufficient access token.")); + m_ui.detailsLabel->setToolTip(user.error.message + QLatin1Char('\n') + + tr("Permission scope read_api or api needed.")); + updatePageButtons(); + m_ui.treeViewTitle->setText(tr("Projects (%1)").arg(0)); + return; + } + } + + if (user.id != -1) { + if (user.bot) { + m_ui.mainLabel->setText(tr("Using project access token.")); + m_ui.detailsLabel->setText({}); + } else { + m_ui.mainLabel->setText(tr("Logged in as %1").arg(user.name)); + m_ui.detailsLabel->setText(tr("Id: %1 (%2)").arg(user.id).arg(user.email)); + } + m_ui.detailsLabel->setToolTip({}); + } else { + m_ui.mainLabel->setText(tr("Not logged in.")); + m_ui.detailsLabel->setText({}); + m_ui.detailsLabel->setToolTip({}); + } + m_lastTreeViewQuery = Query(Query::Projects); + fetchProjects(); +} + +void GitLabDialog::handleProjects(const Projects &projects) +{ + Utils::ListModel *listModel = new Utils::ListModel(this); + for (const Project &project : projects.projects) + listModel->appendItem(new Project(project)); + + // TODO use a real model / delegate..? + listModel->setDataAccessor([](Project *data, int /*column*/, int role) -> QVariant { + if (role == Qt::DisplayRole) + return QString(data->displayName + " (" + data->visibility + ')'); + if (role == Qt::UserRole) + return QVariant::fromValue(*data); + return QVariant(); + }); + resetTreeView(m_ui.treeView, listModel); + int count = projects.error.message.isEmpty() ? projects.pageInfo.total : 0; + m_ui.treeViewTitle->setText(tr("Projects (%1)").arg(count)); + + m_lastPageInformation = projects.pageInfo; + updatePageButtons(); +} + +void GitLabDialog::fetchProjects() +{ + QueryRunner *runner = new QueryRunner(m_lastTreeViewQuery, m_currentServerId, this); + connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) { + handleProjects(ResultParser::parseProjects(result)); + }); + connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); }); + runner->start(); +} + +void GitLabDialog::cloneSelected() +{ + const QModelIndexList indexes = m_ui.treeView->selectionModel()->selectedIndexes(); + QTC_ASSERT(indexes.size() == 1, return); + const Project project = indexes.first().data(Qt::UserRole).value(); + QTC_ASSERT(!project.sshUrl.isEmpty() && !project.httpUrl.isEmpty(), return); + GitLabCloneDialog dialog(project, this); + if (dialog.exec() == QDialog::Accepted) + reject(); +} + +} // namespace GitLab diff --git a/src/plugins/gitlab/gitlabdialog.h b/src/plugins/gitlab/gitlabdialog.h new file mode 100644 index 00000000000..907d786f474 --- /dev/null +++ b/src/plugins/gitlab/gitlabdialog.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "ui_gitlabdialog.h" + +#include "queryrunner.h" +#include "resultparser.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QPushButton; +QT_END_NAMESPACE + +namespace GitLab { + +class GitLabParameters; +class GitLabDialog : public QDialog +{ + Q_OBJECT +public: + explicit GitLabDialog(QWidget *parent = nullptr); + + void updateRemotes(); + +protected: + void keyPressEvent(QKeyEvent *event) override; + +private: + void resetTreeView(QTreeView *treeView, QAbstractItemModel *model); + void requestMainViewUpdate(); + void updatePageButtons(); + + void queryFirstPage(); + void queryPreviousPage(); + void queryNextPage(); + void queryLastPage(); + void querySearch(); + void continuePageUpdate(); + + void handleUser(const User &user); + void handleProjects(const Projects &projects); + void fetchProjects(); + + void cloneSelected(); + + Ui::GitLabDialog m_ui; + QPushButton *m_clonePB = nullptr; + Utils::Id m_currentServerId; + Query m_lastTreeViewQuery; + PageInformation m_lastPageInformation; + int m_currentUserId = -1; +}; + +} // namespace GitLab diff --git a/src/plugins/gitlab/gitlabdialog.ui b/src/plugins/gitlab/gitlabdialog.ui new file mode 100644 index 00000000000..2b4377b1263 --- /dev/null +++ b/src/plugins/gitlab/gitlabdialog.ui @@ -0,0 +1,268 @@ + + + GitLab::GitLabDialog + + + + 0 + 0 + 665 + 530 + + + + GitLab + + + + + + + + + + + + Login + + + + + + + Details + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remote: + + + + + + + + 200 + 0 + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 40 + + + + + + + + + + + + Projects + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Search + + + + + + + Search + + + + + + + + + + + false + + + true + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 200 + 20 + + + + + + + + + + |< + + + + + + + ... + + + + + + + 0 + + + + + + + ... + + + + + + + >| + + + + + + + + + Qt::Horizontal + + + + 200 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + rejected() + GitLab::GitLabDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/plugins/gitlab/gitlabplugin.cpp b/src/plugins/gitlab/gitlabplugin.cpp index 1f45fe97d1d..2a4dd7c69de 100644 --- a/src/plugins/gitlab/gitlabplugin.cpp +++ b/src/plugins/gitlab/gitlabplugin.cpp @@ -25,16 +25,27 @@ #include "gitlabplugin.h" +#include "gitlabdialog.h" #include "gitlaboptionspage.h" #include "gitlabparameters.h" #include "gitlabprojectsettings.h" +#include +#include #include +#include #include #include #include +#include +#include +#include + namespace GitLab { +namespace Constants { +const char GITLAB_OPEN_VIEW[] = "GitLab.OpenView"; +} // namespace Constants class GitLabPluginPrivate { @@ -42,6 +53,7 @@ public: GitLabParameters parameters; GitLabOptionsPage optionsPage{¶meters}; QHash projectSettings; + QPointer dialog; }; static GitLabPluginPrivate *dd = nullptr; @@ -71,9 +83,42 @@ bool GitLabPlugin::initialize(const QStringList & /*arguments*/, QString * /*err return new GitLabProjectSettingsWidget(project); }); ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); + QAction *openViewAction = new QAction(tr("GitLab..."), this); + auto gitlabCommand = Core::ActionManager::registerAction(openViewAction, + Constants::GITLAB_OPEN_VIEW); + connect(openViewAction, &QAction::triggered, this, &GitLabPlugin::openView); + Core::ActionContainer *ac = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS); + ac->addAction(gitlabCommand); + connect(&dd->optionsPage, &GitLabOptionsPage::settingsChanged, this, [this] { + if (dd->dialog) + dd->dialog->updateRemotes(); + }); return true; } +void GitLabPlugin::openView() +{ + if (dd->dialog.isNull()) { + while (!dd->parameters.isValid()) { + QMessageBox::warning(Core::ICore::dialogParent(), tr("Error"), + tr("Invalid GitLab configuration. For a fully functional " + "configuration, you need to set up host name or address and " + "an access token. Providing the path to curl is mandatory.")); + if (!Core::ICore::showOptionsDialog("GitLab")) + return; + } + GitLabDialog *gitlabD = new GitLabDialog(Core::ICore::dialogParent()); + gitlabD->setModal(true); + Core::ICore::registerWindow(gitlabD, Core::Context("Git.GitLab")); + dd->dialog = gitlabD; + } + const Qt::WindowStates state = dd->dialog->windowState(); + if (state & Qt::WindowMinimized) + dd->dialog->setWindowState(state & ~Qt::WindowMinimized); + dd->dialog->show(); + dd->dialog->raise(); +} + QList GitLabPlugin::allGitLabServers() { QTC_ASSERT(dd, return {}); diff --git a/src/plugins/gitlab/gitlabplugin.h b/src/plugins/gitlab/gitlabplugin.h index 7d578120f9f..14368ef29fc 100644 --- a/src/plugins/gitlab/gitlabplugin.h +++ b/src/plugins/gitlab/gitlabplugin.h @@ -52,6 +52,9 @@ public: static GitLabParameters *globalParameters(); static GitLabProjectSettings *projectSettings(ProjectExplorer::Project *project); static GitLabOptionsPage *optionsPage(); + +private: + void openView(); }; } // namespace GitLab diff --git a/src/plugins/gitlab/gitlabprojectsettings.cpp b/src/plugins/gitlab/gitlabprojectsettings.cpp index 7088131c409..22921e229de 100644 --- a/src/plugins/gitlab/gitlabprojectsettings.cpp +++ b/src/plugins/gitlab/gitlabprojectsettings.cpp @@ -294,6 +294,7 @@ void GitLabProjectSettingsWidget::updateEnabledStates() const bool isGitRepository = m_hostCB->count() > 0; const bool hasGitLabServers = m_linkedGitLabServer->count(); const bool linked = m_projectSettings->isLinked(); + m_linkedGitLabServer->setEnabled(isGitRepository && !linked); m_hostCB->setEnabled(isGitRepository && !linked); m_linkWithGitLab->setEnabled(isGitRepository && !linked && hasGitLabServers); diff --git a/src/plugins/gitlab/queryrunner.cpp b/src/plugins/gitlab/queryrunner.cpp index b680374dbd1..e0369d78c52 100644 --- a/src/plugins/gitlab/queryrunner.cpp +++ b/src/plugins/gitlab/queryrunner.cpp @@ -41,6 +41,8 @@ namespace GitLab { const char API_PREFIX[] = "/api/v4"; const char QUERY_PROJECT[] = "/projects/%1"; +const char QUERY_PROJECTS[] = "/projects?simple=true"; +const char QUERY_USER[] = "/user"; Query::Query(Type type, const QStringList ¶meter) : m_type(type) @@ -48,6 +50,21 @@ Query::Query(Type type, const QStringList ¶meter) { } +void Query::setPageParameter(int page) +{ + m_pageParameter = page; +} + +void Query::setAdditionalParameters(const QStringList &additional) +{ + m_additionalParameters = additional; +} + +bool Query::hasPaginatedResults() const +{ + return m_type == Query::Projects; +} + QString Query::toString() const { QString query = API_PREFIX; @@ -59,6 +76,20 @@ QString Query::toString() const query += QLatin1String(QUERY_PROJECT).arg(QLatin1String( QUrl::toPercentEncoding(m_parameter.at(0)))); break; + case Query::Projects: + query += QLatin1String(QUERY_PROJECTS); + break; + case Query::User: + query += QUERY_USER; + break; + } + if (m_pageParameter > 0) { + query.append(m_type == Query::Projects ? '&' : '?'); + query.append("page=").append(QString::number(m_pageParameter)); + } + if (!m_additionalParameters.isEmpty()) { + query.append((m_type == Query::Projects || m_pageParameter > 0) ? '&' : '?'); + query.append(m_additionalParameters.join('&')); } return query; } @@ -69,6 +100,9 @@ QueryRunner::QueryRunner(const Query &query, const Utils::Id &id, QObject *paren const GitLabParameters *p = GitLabPlugin::globalParameters(); const auto server = p->serverForId(id); QStringList args = server.curlArguments(); + m_paginated = query.hasPaginatedResults(); + if (m_paginated) + args << "-i"; if (!server.token.isEmpty()) args << "--header" << "PRIVATE-TOKEN: " + server.token; QString url = "https://" + server.host; diff --git a/src/plugins/gitlab/queryrunner.h b/src/plugins/gitlab/queryrunner.h index c0fa801b0fd..a431921ab72 100644 --- a/src/plugins/gitlab/queryrunner.h +++ b/src/plugins/gitlab/queryrunner.h @@ -38,15 +38,23 @@ class Query public: enum Type { NoQuery, - Project + User, + Project, + Projects }; explicit Query(Type type, const QStringList ¶meters = {}); + void setPageParameter(int page); + void setAdditionalParameters(const QStringList &additional); + bool hasPaginatedResults() const; + Type type() const { return m_type; } QString toString() const; private: Type m_type = NoQuery; QStringList m_parameter; + QStringList m_additionalParameters; + int m_pageParameter = -1; }; class QueryRunner : public QObject @@ -70,6 +78,7 @@ private: Utils::QtcProcess m_process; bool m_running = false; + bool m_paginated = false; }; } // namespace GitLab diff --git a/src/plugins/gitlab/resultparser.cpp b/src/plugins/gitlab/resultparser.cpp index bd857c2e865..7fb5db8d2ff 100644 --- a/src/plugins/gitlab/resultparser.cpp +++ b/src/plugins/gitlab/resultparser.cpp @@ -34,6 +34,38 @@ namespace GitLab { namespace ResultParser { +static PageInformation paginationInformation(const QByteArray &header) +{ + PageInformation result; + const QByteArrayList lines = header.split('\n'); + for (const QByteArray &line : lines) { + const QByteArray lower = line.toLower(); // depending on OS this may be capitalized + if (lower.startsWith("x-page: ")) + result.currentPage = line.mid(8).toInt(); + else if (lower.startsWith("x-per-page: ")) + result.perPage = line.mid(12).toInt(); + else if (lower.startsWith("x-total: ")) + result.total = line.mid(9).toInt(); + else if (lower.startsWith("x-total-pages: ")) + result.totalPages = line.mid(15).toInt(); + } + return result; +} + +static std::pair splitHeaderAndBody(const QByteArray &input) +{ + QByteArray header; + QByteArray json; + int emptyLine = input.indexOf("\r\n\r\n"); // we always get \r\n as line separator? + if (emptyLine != -1) { + header = input.left(emptyLine); + json = input.mid(emptyLine + 4); + } else { + json = input; + } + return std::make_pair(header, json); +} + static std::pair preHandleSingle(const QByteArray &json) { Error result; @@ -59,6 +91,52 @@ static std::pair preHandleSingle(const QByteArray &json) return std::make_pair(result, object); } +static std::pair preHandleHeaderAndBody(const QByteArray &header, + const QByteArray &json) +{ + Error result; + if (header.isEmpty()) { + result.message = "Missing Expected Header"; + return std::make_pair(result, QJsonDocument()); + } + + QJsonParseError error; + const QJsonDocument doc = QJsonDocument::fromJson(json, &error); + if (error.error != QJsonParseError::NoError) { + result.message = error.errorString(); + return std::make_pair(result, doc); + } + + if (doc.isObject()) { + const QJsonObject obj = doc.object(); + if (obj.contains("message")) { + result = parseErrorMessage(obj.value("message").toString()); + return std::make_pair(result, doc); + } else if (obj.contains("error")) { + if (obj.value("error").toString() == "insufficient_scope") + result.code = 1; + result.message = obj.value("error_description").toString(); + return std::make_pair(result, doc); + } + } + + if (!doc.isArray()) + result.message = "Not an Array"; + + return std::make_pair(result, doc); +} + +static User userFromJson(const QJsonObject &jsonObj) +{ + User user; + user.name = jsonObj.value("username").toString(); + user.realname = jsonObj.value("name").toString(); + user.id = jsonObj.value("id").toInt(-1); + user.email = jsonObj.value("email").toString(); + user.bot = jsonObj.value("bot").toBool(); + return user; +} + static Project projectFromJson(const QJsonObject &jsonObj) { Project project; @@ -67,6 +145,8 @@ static Project projectFromJson(const QJsonObject &jsonObj) project.pathName = jsonObj.value("path_with_namespace").toString(); project.id = jsonObj.value("id").toInt(-1); project.visibility = jsonObj.value("visibility").toString("public"); + project.httpUrl = jsonObj.value("http_url_to_repo").toString(); + project.sshUrl = jsonObj.value("ssh_url_to_repo").toString(); if (jsonObj.contains("forks_count")) project.forkCount = jsonObj.value("forks_count").toInt(); if (jsonObj.contains("star_count")) @@ -82,6 +162,17 @@ static Project projectFromJson(const QJsonObject &jsonObj) return project; } +User parseUser(const QByteArray &input) +{ + auto [error, userObj] = preHandleSingle(input); + if (!error.message.isEmpty()) { + User result; + result.error = error; + return result; + } + return userFromJson(userObj); +} + Project parseProject(const QByteArray &input) { auto [error, projectObj] = preHandleSingle(input); @@ -93,6 +184,26 @@ Project parseProject(const QByteArray &input) return projectFromJson(projectObj); } +Projects parseProjects(const QByteArray &input) +{ + auto [header, json] = splitHeaderAndBody(input); + auto [error, jsonDoc] = preHandleHeaderAndBody(header, json); + Projects result; + if (!error.message.isEmpty()) { + result.error = error; + return result; + } + result.pageInfo = paginationInformation(header); + const QJsonArray projectsArray = jsonDoc.array(); + for (const QJsonValue &value : projectsArray) { + if (!value.isObject()) + continue; + const QJsonObject projectObj = value.toObject(); + result.projects.append(projectFromJson(projectObj)); + } + return result; +} + Error parseErrorMessage(const QString &message) { Error error; diff --git a/src/plugins/gitlab/resultparser.h b/src/plugins/gitlab/resultparser.h index 7a99bc14a3e..7c71b5c6d18 100644 --- a/src/plugins/gitlab/resultparser.h +++ b/src/plugins/gitlab/resultparser.h @@ -25,6 +25,8 @@ #pragma once +#include +#include #include namespace GitLab { @@ -35,6 +37,26 @@ struct Error QString message; }; +class PageInformation +{ +public: + int currentPage = -1; + int totalPages = -1; + int perPage = -1; + int total = -1; +}; + +class User +{ +public: + QString name; + QString realname; + QString email; + Error error; + int id = -1; + bool bot = false; +}; + class Project { public: @@ -42,18 +64,32 @@ public: QString displayName; QString pathName; QString visibility; + QString httpUrl; + QString sshUrl; Error error; int id = -1; int starCount = -1; int forkCount = -1; int issuesCount = -1; - int accessLevel = -1; // 40 maintainer, 30 developer, 20 reporter, 10 guest + int accessLevel = -1; // 50 owner, 40 maintainer, 30 developer, 20 reporter, 10 guest +}; + +class Projects +{ +public: + QList projects; + Error error; + PageInformation pageInfo; }; namespace ResultParser { +User parseUser(const QByteArray &input); Project parseProject(const QByteArray &input); +Projects parseProjects(const QByteArray &input); Error parseErrorMessage(const QString &message); } // namespace ResultParser } // namespace GitLab + +Q_DECLARE_METATYPE(GitLab::Project) From b17bbcff38afd9b511bc3395af1592a3edc7ed55 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 17:38:12 +0200 Subject: [PATCH 48/58] Debugger: Use USVC extra data more directly Not nicer, but more similar to the rest of extra data, so one approach less. Change-Id: I2c9a3a041ab3f017f3350c0399d57feeb422048c Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger --- .../baremetal/debugservers/gdb/gdbserverprovider.cpp | 3 +-- .../debugservers/uvsc/uvscserverprovider.cpp | 9 ++++----- src/plugins/debugger/debuggerconstants.h | 8 -------- src/plugins/debugger/debuggerengine.h | 8 ++++++++ src/plugins/debugger/debuggerruncontrol.h | 2 ++ src/plugins/debugger/peripheralregisterhandler.cpp | 7 ++----- src/plugins/debugger/uvsc/uvscengine.cpp | 11 ++++------- 7 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp index 2486ef3ecef..d103e86c0a8 100644 --- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp @@ -179,8 +179,6 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, Runnable inferior; inferior.command.setExecutable(bin); - inferior.extraData.insert(Debugger::Constants::kPeripheralDescriptionFile, - m_peripheralDescriptionFile.toVariant()); if (const auto argAspect = runControl->aspect()) inferior.command.setArguments(argAspect->arguments); runTool->setInferior(inferior); @@ -191,6 +189,7 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, runTool->setRemoteChannel(channelString()); runTool->setUseContinueInsteadOfRun(true); runTool->setUseExtendedRemote(useExtendedRemote()); + runTool->runParameters().peripheralDescriptionFile = m_peripheralDescriptionFile; return true; } diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp index 19c4b07ad55..ed0c419e87e 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp @@ -216,11 +216,10 @@ bool UvscServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMess Runnable inferior; inferior.command.setExecutable(bin); - inferior.extraData.insert(Debugger::Constants::kPeripheralDescriptionFile, - peripheralDescriptionFile.toVariant()); - inferior.extraData.insert(Debugger::Constants::kUVisionProjectFilePath, projFilePath.toString()); - inferior.extraData.insert(Debugger::Constants::kUVisionOptionsFilePath, optFilePath.toString()); - inferior.extraData.insert(Debugger::Constants::kUVisionSimulator, isSimulator()); + runTool->runParameters().peripheralDescriptionFile = peripheralDescriptionFile; + runTool->runParameters().uVisionProjectFilePath = projFilePath; + runTool->runParameters().uVisionOptionsFilePath = optFilePath; + runTool->runParameters().uVisionSimulator = isSimulator(); runTool->setInferior(inferior); runTool->setSymbolFile(bin); runTool->setStartMode(AttachToRemoteServer); diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index 74ccf23e430..09ffe19365a 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -36,14 +36,6 @@ const char MODE_DEBUG[] = "Mode.Debug"; // Debug mode context const char C_DEBUGMODE[] = "Debugger.DebugMode"; -// Common debugger constants. -const char kPeripheralDescriptionFile[] = "PeripheralDescriptionFile"; - -// UVSC-specific debugger constants. -const char kUVisionProjectFilePath[] = "UVisionProjectFilePath"; -const char kUVisionOptionsFilePath[] = "UVisionOptionsFilePath"; -const char kUVisionSimulator[] = "UVisionSimulator"; - } // namespace Constants // Keep in sync with dumper.py diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 34830aafaca..4205384cd99 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -206,6 +206,14 @@ public: Utils::FilePath dumperPath; int fallbackQtVersion = 0x50200; + + // Common debugger constants. + Utils::FilePath peripheralDescriptionFile; + + // UVSC-specific debugger constants. + Utils::FilePath uVisionProjectFilePath; + Utils::FilePath uVisionOptionsFilePath; + bool uVisionSimulator = false; }; class UpdateParameters diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index 4781a059366..a58ffaff9f6 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -131,6 +131,8 @@ public: Internal::TerminalRunner *terminalRunner() const; DebuggerEngineType cppEngineType() const; + Internal::DebuggerRunParameters &runParameters() { return m_runParameters; } + private: bool fixupParameters(); void handleEngineStarted(Internal::DebuggerEngine *engine); diff --git a/src/plugins/debugger/peripheralregisterhandler.cpp b/src/plugins/debugger/peripheralregisterhandler.cpp index 5c63c156cae..b1748d3b986 100644 --- a/src/plugins/debugger/peripheralregisterhandler.cpp +++ b/src/plugins/debugger/peripheralregisterhandler.cpp @@ -732,12 +732,9 @@ void PeripheralRegisterHandler::updateRegisterGroups() clear(); const DebuggerRunParameters &rp = m_engine->runParameters(); - const FilePath peripheralDescriptionFile = FilePath::fromVariant( - rp.inferior.extraData.value(Debugger::Constants::kPeripheralDescriptionFile)); - - if (!peripheralDescriptionFile.exists()) + if (!rp.peripheralDescriptionFile.exists()) return; - m_peripheralRegisterGroups = availablePeripheralRegisterGroups(peripheralDescriptionFile); + m_peripheralRegisterGroups = availablePeripheralRegisterGroups(rp.peripheralDescriptionFile); } void PeripheralRegisterHandler::updateRegister(quint64 address, quint64 value) diff --git a/src/plugins/debugger/uvsc/uvscengine.cpp b/src/plugins/debugger/uvsc/uvscengine.cpp index 915a6c970bd..6a6253c9dc9 100644 --- a/src/plugins/debugger/uvsc/uvscengine.cpp +++ b/src/plugins/debugger/uvsc/uvscengine.cpp @@ -536,10 +536,8 @@ void UvscEngine::updateAll() bool UvscEngine::configureProject(const DebuggerRunParameters &rp) { // Fetch patchs for the generated uVision project files. - const FilePath optionsPath = FilePath::fromString(rp.inferior.extraData.value( - Constants::kUVisionOptionsFilePath).toString()); - const FilePath projectPath = FilePath::fromString(rp.inferior.extraData.value( - Constants::kUVisionProjectFilePath).toString()); + const FilePath optionsPath = rp.uVisionOptionsFilePath; + const FilePath projectPath = rp.uVisionProjectFilePath; showMessage("UVSC: LOADING PROJECT..."); if (!optionsPath.exists()) { @@ -557,7 +555,7 @@ bool UvscEngine::configureProject(const DebuggerRunParameters &rp) } showMessage("UVSC: SETTING PROJECT DEBUG TARGET..."); - m_simulator = rp.inferior.extraData.value(Constants::kUVisionSimulator).toBool(); + m_simulator = rp.uVisionSimulator; if (!m_client->setProjectDebugTarget(m_simulator)) { handleSetupFailure(tr("Internal error: Unable to set the uVision debug target: %1.") .arg(m_client->errorString())); @@ -606,8 +604,7 @@ void UvscEngine::handleProjectClosed() m_loadingRequired = false; const DebuggerRunParameters &rp = runParameters(); - const FilePath projectPath = FilePath::fromString(rp.inferior.extraData.value( - Constants::kUVisionProjectFilePath).toString()); + const FilePath projectPath = rp.uVisionProjectFilePath; // This magic function removes specific files from the uVision // project directory. Without of this we can't enumerate the local From cd1af2864bf79c4cc8c7b79c4dae0d1ca51402c0 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 25 May 2022 16:34:57 +0200 Subject: [PATCH 49/58] ProjectExplorer: Slim down PEPlugin::runningRunControlProcesses() Accessed only from the (semi-dead) GammaRay plugin which does not really use it. Change-Id: Id4149a23869a8d9ceb1c31a3a380bbad32e3981b Reviewed-by: Jarek Kobus Reviewed-by: Qt CI Bot --- src/plugins/projectexplorer/projectexplorer.cpp | 6 +++--- src/plugins/projectexplorer/projectexplorer.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 99b8f691f04..b857468106a 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -3246,13 +3246,13 @@ void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc, dd->doUpdateRunActions(); } -QList> ProjectExplorerPlugin::runningRunControlProcesses() +QList> ProjectExplorerPlugin::runningRunControlProcesses() { - QList> processes; + QList> processes; const QList runControls = allRunControls(); for (RunControl *rc : runControls) { if (rc->isRunning()) - processes << qMakePair(rc->runnable(), rc->applicationProcessHandle()); + processes << qMakePair(rc->commandLine(), rc->applicationProcessHandle()); } return processes; } diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index 0b2ad59d04b..563a380b1bb 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -164,7 +164,7 @@ public: static void runStartupProject(Utils::Id runMode, bool forceSkipDeploy = false); static void runRunConfiguration(RunConfiguration *rc, Utils::Id runMode, const bool forceSkipDeploy = false); - static QList> runningRunControlProcesses(); + static QList> runningRunControlProcesses(); static QList allRunControls(); static void addExistingFiles(FolderNode *folderNode, const Utils::FilePaths &filePaths); From dd27901759e3f03edfb7926fbd63fc821d509760 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 12 May 2022 13:54:00 +0200 Subject: [PATCH 50/58] GitLab: Allow fetching events Projects that are linked to a GitLab instance will now fetch notifications for this project and print them to the vcs output pane. Change-Id: Ifb960e64b30a260327efb28a3dfd26f6457503a0 Reviewed-by: Reviewed-by: David Schulz --- src/plugins/gitlab/gitlabplugin.cpp | 181 ++++++++++++++++++- src/plugins/gitlab/gitlabplugin.h | 3 + src/plugins/gitlab/gitlabprojectsettings.cpp | 7 + src/plugins/gitlab/gitlabprojectsettings.h | 5 + src/plugins/gitlab/queryrunner.cpp | 8 +- src/plugins/gitlab/queryrunner.h | 3 +- src/plugins/gitlab/resultparser.cpp | 66 +++++++ src/plugins/gitlab/resultparser.h | 24 +++ 8 files changed, 294 insertions(+), 3 deletions(-) diff --git a/src/plugins/gitlab/gitlabplugin.cpp b/src/plugins/gitlab/gitlabplugin.cpp index 2a4dd7c69de..5df45c8e688 100644 --- a/src/plugins/gitlab/gitlabplugin.cpp +++ b/src/plugins/gitlab/gitlabplugin.cpp @@ -29,6 +29,8 @@ #include "gitlaboptionspage.h" #include "gitlabparameters.h" #include "gitlabprojectsettings.h" +#include "queryrunner.h" +#include "resultparser.h" #include #include @@ -36,24 +38,39 @@ #include #include #include +#include #include +#include #include #include #include +#include namespace GitLab { namespace Constants { const char GITLAB_OPEN_VIEW[] = "GitLab.OpenView"; } // namespace Constants -class GitLabPluginPrivate +class GitLabPluginPrivate : public QObject { public: GitLabParameters parameters; GitLabOptionsPage optionsPage{¶meters}; QHash projectSettings; QPointer dialog; + + QTimer notificationTimer; + QString projectName; + Utils::Id serverId; + bool runningQuery = false; + + void setupNotificationTimer(); + void fetchEvents(); + void fetchUser(); + void createAndSendEventsRequest(const QDateTime timeStamp, int page = -1); + void handleUser(const User &user); + void handleEvents(const Events &events, const QDateTime &timeStamp); }; static GitLabPluginPrivate *dd = nullptr; @@ -93,6 +110,9 @@ bool GitLabPlugin::initialize(const QStringList & /*arguments*/, QString * /*err if (dd->dialog) dd->dialog->updateRemotes(); }); + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, &GitLabPlugin::onStartupProjectChanged); return true; } @@ -119,6 +139,141 @@ void GitLabPlugin::openView() dd->dialog->raise(); } +void GitLabPlugin::onStartupProjectChanged() +{ + QTC_ASSERT(dd, return); + disconnect(&dd->notificationTimer); + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + if (!project) { + dd->notificationTimer.stop(); + return; + } + + const GitLabProjectSettings *projSettings = projectSettings(project); + if (!projSettings->isLinked()) { + dd->notificationTimer.stop(); + return; + } + + dd->fetchEvents(); + dd->setupNotificationTimer(); +} + +void GitLabPluginPrivate::setupNotificationTimer() +{ + // make interval configurable? + notificationTimer.setInterval(15 * 60 * 1000); + QObject::connect(¬ificationTimer, &QTimer::timeout, this, &GitLabPluginPrivate::fetchEvents); + notificationTimer.start(); +} + +void GitLabPluginPrivate::fetchEvents() +{ + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + QTC_ASSERT(project, return); + + if (runningQuery) + return; + + const GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project); + projectName = projSettings->currentProject(); + serverId = projSettings->currentServer(); + + const QDateTime lastRequest = projSettings->lastRequest(); + if (!lastRequest.isValid()) { // we haven't queried events for this project yet + fetchUser(); + return; + } + createAndSendEventsRequest(lastRequest); +} + +void GitLabPluginPrivate::fetchUser() +{ + if (runningQuery) + return; + + const Query query(Query::User); + QueryRunner *runner = new QueryRunner(query, serverId, this); + QObject::connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) { + handleUser(ResultParser::parseUser(result)); + }); + QObject::connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); }); + runningQuery = true; + runner->start(); +} + +void GitLabPluginPrivate::createAndSendEventsRequest(const QDateTime timeStamp, int page) +{ + if (runningQuery) + return; + + Query query(Query::Events, {projectName}); + QStringList additional = {"sort=asc"}; + + QDateTime after = timeStamp.addDays(-1); + additional.append(QLatin1String("after=%1").arg(after.toString("yyyy-MM-dd"))); + query.setAdditionalParameters(additional); + + if (page > 1) + query.setPageParameter(page); + + QueryRunner *runner = new QueryRunner(query, serverId, this); + QObject::connect(runner, &QueryRunner::resultRetrieved, this, + [this, timeStamp](const QByteArray &result) { + handleEvents(ResultParser::parseEvents(result), timeStamp); + }); + QObject::connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); }); + runningQuery = true; + runner->start(); +} + +void GitLabPluginPrivate::handleUser(const User &user) +{ + runningQuery = false; + + QTC_ASSERT(user.error.message.isEmpty(), return); + const QDateTime timeStamp = QDateTime::fromString(user.lastLogin, Qt::ISODateWithMs); + createAndSendEventsRequest(timeStamp); +} + +void GitLabPluginPrivate::handleEvents(const Events &events, const QDateTime &timeStamp) +{ + runningQuery = false; + + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + QTC_ASSERT(project, return); + + GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project); + QTC_ASSERT(projSettings->currentProject() == projectName, return); + + if (!projSettings->isLinked()) // link state has changed meanwhile - ignore the request + return; + + if (!events.error.message.isEmpty()) { + VcsBase::VcsOutputWindow::appendError("GitLab: Error while fetching events. " + + events.error.message + '\n'); + return; + } + + QDateTime lastTimeStamp; + for (const Event &event : events.events) { + const QDateTime eventTimeStamp = QDateTime::fromString(event.timeStamp, Qt::ISODateWithMs); + if (!timeStamp.isValid() || timeStamp < eventTimeStamp) { + VcsBase::VcsOutputWindow::appendMessage("GitLab: " + event.toMessage()); + if (!lastTimeStamp.isValid() || lastTimeStamp < eventTimeStamp) + lastTimeStamp = eventTimeStamp; + } + } + if (lastTimeStamp.isValid()) { + if (auto outputWindow = VcsBase::VcsOutputWindow::instance()) + outputWindow->flash(); + projSettings->setLastRequest(lastTimeStamp); + } + + if (events.pageInfo.currentPage < events.pageInfo.totalPages) + createAndSendEventsRequest(timeStamp, events.pageInfo.currentPage + 1); +} + QList GitLabPlugin::allGitLabServers() { QTC_ASSERT(dd, return {}); @@ -152,4 +307,28 @@ GitLabOptionsPage *GitLabPlugin::optionsPage() return &dd->optionsPage; } +void GitLabPlugin::linkedStateChanged(bool enabled) +{ + QTC_ASSERT(dd, return); + + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + if (project) { + const GitLabProjectSettings *pSettings = projectSettings(project); + dd->serverId = pSettings->currentServer(); + dd->projectName = pSettings->currentProject(); + } else { + dd->serverId = Utils::Id(); + dd->projectName = QString(); + } + + if (enabled) { + dd->fetchEvents(); + dd->setupNotificationTimer(); + } else { + QObject::disconnect(&dd->notificationTimer, &QTimer::timeout, + dd, &GitLabPluginPrivate::fetchEvents); + dd->notificationTimer.stop(); + } +} + } // namespace GitLab diff --git a/src/plugins/gitlab/gitlabplugin.h b/src/plugins/gitlab/gitlabplugin.h index 14368ef29fc..f1abf1bd1ee 100644 --- a/src/plugins/gitlab/gitlabplugin.h +++ b/src/plugins/gitlab/gitlabplugin.h @@ -33,6 +33,7 @@ namespace ProjectExplorer { class Project; } namespace GitLab { +class Events; class GitLabProjectSettings; class GitLabOptionsPage; @@ -53,8 +54,10 @@ public: static GitLabProjectSettings *projectSettings(ProjectExplorer::Project *project); static GitLabOptionsPage *optionsPage(); + static void linkedStateChanged(bool enabled); private: void openView(); + void onStartupProjectChanged(); }; } // namespace GitLab diff --git a/src/plugins/gitlab/gitlabprojectsettings.cpp b/src/plugins/gitlab/gitlabprojectsettings.cpp index 22921e229de..e24b4699829 100644 --- a/src/plugins/gitlab/gitlabprojectsettings.cpp +++ b/src/plugins/gitlab/gitlabprojectsettings.cpp @@ -49,6 +49,7 @@ namespace GitLab { const char PSK_LINKED_ID[] = "GitLab.LinkedId"; const char PSK_SERVER[] = "GitLab.Server"; const char PSK_PROJECT[] = "GitLab.Project"; +const char PSK_LAST_REQ[] = "GitLab.LastRequest"; static QString accessLevelString(int accessLevel) { @@ -106,6 +107,7 @@ void GitLabProjectSettings::load() m_id = Utils::Id::fromSetting(m_project->namedSettings(PSK_LINKED_ID)); m_host = m_project->namedSettings(PSK_SERVER).toString(); m_currentProject = m_project->namedSettings(PSK_PROJECT).toString(); + m_lastRequest = m_project->namedSettings(PSK_LAST_REQ).toDateTime(); // may still be wrong, but we avoid an additional request by just doing sanity check here if (!m_id.isValid() || m_host.isEmpty()) @@ -124,6 +126,7 @@ void GitLabProjectSettings::save() m_project->setNamedSettings(PSK_SERVER, QString()); } m_project->setNamedSettings(PSK_PROJECT, m_currentProject); + m_project->setNamedSettings(PSK_LAST_REQ, m_lastRequest); } GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Project *project, @@ -184,6 +187,7 @@ void GitLabProjectSettingsWidget::unlink() m_projectSettings->setLinked(false); m_projectSettings->setCurrentProject({}); updateEnabledStates(); + GitLabPlugin::linkedStateChanged(false); } void GitLabProjectSettingsWidget::checkConnection(CheckMode mode) @@ -245,6 +249,7 @@ void GitLabProjectSettingsWidget::onConnectionChecked(const Project &project, m_projectSettings->setCurrentServerHost(remote); m_projectSettings->setLinked(true); m_projectSettings->setCurrentProject(projectName); + GitLabPlugin::linkedStateChanged(true); } updateEnabledStates(); } @@ -282,8 +287,10 @@ void GitLabProjectSettingsWidget::updateUi() m_hostCB->setCurrentIndex(m_hostCB->findData(QVariant::fromValue(serverHost))); m_linkedGitLabServer->setCurrentIndex( m_linkedGitLabServer->findData(QVariant::fromValue(server))); + GitLabPlugin::linkedStateChanged(true); } else { m_projectSettings->setLinked(false); + GitLabPlugin::linkedStateChanged(false); } } updateEnabledStates(); diff --git a/src/plugins/gitlab/gitlabprojectsettings.h b/src/plugins/gitlab/gitlabprojectsettings.h index de8bdb851d8..5a9081adc73 100644 --- a/src/plugins/gitlab/gitlabprojectsettings.h +++ b/src/plugins/gitlab/gitlabprojectsettings.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -59,9 +60,12 @@ public: QString currentProject() const { return m_currentProject; } bool isLinked() const { return m_linked; } void setLinked(bool linked); + QDateTime lastRequest() const { return m_lastRequest; } + void setLastRequest(const QDateTime &lastRequest) { m_lastRequest = lastRequest; } ProjectExplorer::Project *project() const { return m_project; } static std::tuple remotePartsFromRemote(const QString &remote); + private: void load(); void save(); @@ -69,6 +73,7 @@ private: ProjectExplorer::Project *m_project = nullptr; QString m_host; Utils::Id m_id; + QDateTime m_lastRequest; QString m_currentProject; bool m_linked = false; }; diff --git a/src/plugins/gitlab/queryrunner.cpp b/src/plugins/gitlab/queryrunner.cpp index e0369d78c52..86ad6b303ab 100644 --- a/src/plugins/gitlab/queryrunner.cpp +++ b/src/plugins/gitlab/queryrunner.cpp @@ -43,6 +43,7 @@ const char API_PREFIX[] = "/api/v4"; const char QUERY_PROJECT[] = "/projects/%1"; const char QUERY_PROJECTS[] = "/projects?simple=true"; const char QUERY_USER[] = "/user"; +const char QUERY_EVENTS[] = "/projects/%1/events"; Query::Query(Type type, const QStringList ¶meter) : m_type(type) @@ -62,7 +63,7 @@ void Query::setAdditionalParameters(const QStringList &additional) bool Query::hasPaginatedResults() const { - return m_type == Query::Projects; + return m_type == Query::Projects || m_type == Query::Events; } QString Query::toString() const @@ -82,6 +83,11 @@ QString Query::toString() const case Query::User: query += QUERY_USER; break; + case Query::Events: + QTC_ASSERT(!m_parameter.isEmpty(), return {}); + query += QLatin1String(QUERY_EVENTS).arg(QLatin1String( + QUrl::toPercentEncoding(m_parameter.at(0)))); + break; } if (m_pageParameter > 0) { query.append(m_type == Query::Projects ? '&' : '?'); diff --git a/src/plugins/gitlab/queryrunner.h b/src/plugins/gitlab/queryrunner.h index a431921ab72..ead892f55db 100644 --- a/src/plugins/gitlab/queryrunner.h +++ b/src/plugins/gitlab/queryrunner.h @@ -40,7 +40,8 @@ public: NoQuery, User, Project, - Projects + Projects, + Events }; explicit Query(Type type, const QStringList ¶meters = {}); diff --git a/src/plugins/gitlab/resultparser.cpp b/src/plugins/gitlab/resultparser.cpp index 7fb5db8d2ff..e9ce0d70835 100644 --- a/src/plugins/gitlab/resultparser.cpp +++ b/src/plugins/gitlab/resultparser.cpp @@ -32,6 +32,25 @@ #include namespace GitLab { + + +QString Event::toMessage() const +{ + QString message; + if (author.realname.isEmpty()) + message.append(author.name); + else + message.append(author.realname + " (" + author.name + ')'); + message.append(' '); + if (!pushData.isEmpty()) + message.append(pushData); + else if (!targetTitle.isEmpty()) + message.append(action + ' ' + targetType + " '" + targetTitle +'\''); + else + message.append(action + ' ' + targetType); + return message; +} + namespace ResultParser { static PageInformation paginationInformation(const QByteArray &header) @@ -133,6 +152,7 @@ static User userFromJson(const QJsonObject &jsonObj) user.realname = jsonObj.value("name").toString(); user.id = jsonObj.value("id").toInt(-1); user.email = jsonObj.value("email").toString(); + user.lastLogin = jsonObj.value("last_sign_in_at").toString(); user.bot = jsonObj.value("bot").toBool(); return user; } @@ -162,6 +182,31 @@ static Project projectFromJson(const QJsonObject &jsonObj) return project; } +static Event eventFromJson(const QJsonObject &jsonObj) +{ + Event event; + event.action = jsonObj.value("action_name").toString(); + const QJsonValue value = jsonObj.value("target_type"); + event.targetType = value.isNull() ? "project" : jsonObj.value("target_type").toString(); + if (event.targetType == "DiffNote") { + const QJsonObject noteObject = jsonObj.value("note").toObject(); + event.targetType = noteObject.value("noteable_type").toString(); + } + event.targetTitle = jsonObj.value("target_title").toString(); + event.author = userFromJson(jsonObj.value("author").toObject()); + event.timeStamp = jsonObj.value("created_at").toString(); + if (jsonObj.contains("push_data")) { + const QJsonObject pushDataObj = jsonObj.value("push_data").toObject(); + if (!pushDataObj.isEmpty()) { + const QString action = pushDataObj.value("action").toString(); + const QString ref = pushDataObj.value("ref").toString(); + const QString refType = pushDataObj.value("ref_type").toString(); + event.pushData = action + ' ' + refType + " '" + ref + '\''; + } + } + return event; +} + User parseUser(const QByteArray &input) { auto [error, userObj] = preHandleSingle(input); @@ -204,6 +249,27 @@ Projects parseProjects(const QByteArray &input) return result; } +Events parseEvents(const QByteArray &input) +{ + auto [header, json] = splitHeaderAndBody(input); + auto [error, jsonDoc] = preHandleHeaderAndBody(header, json); + Events result; + if (!error.message.isEmpty()) { + result.error = error; + return result; + } + result.pageInfo = paginationInformation(header); + const QJsonArray eventsArray = jsonDoc.array(); + for (const QJsonValue &value : eventsArray) { + if (!value.isObject()) + continue; + const QJsonObject eventObj = value.toObject(); + result.events.append(eventFromJson(eventObj)); + } + return result; + +} + Error parseErrorMessage(const QString &message) { Error error; diff --git a/src/plugins/gitlab/resultparser.h b/src/plugins/gitlab/resultparser.h index 7c71b5c6d18..aeaeeadcc5a 100644 --- a/src/plugins/gitlab/resultparser.h +++ b/src/plugins/gitlab/resultparser.h @@ -52,6 +52,7 @@ public: QString name; QString realname; QString email; + QString lastLogin; Error error; int id = -1; bool bot = false; @@ -82,11 +83,34 @@ public: PageInformation pageInfo; }; +class Event +{ +public: + QString action; + QString targetType; + QString targetTitle; + QString timeStamp; + QString pushData; + User author; + Error error; + + QString toMessage() const; +}; + +class Events +{ +public: + QList events; + Error error; + PageInformation pageInfo; +}; + namespace ResultParser { User parseUser(const QByteArray &input); Project parseProject(const QByteArray &input); Projects parseProjects(const QByteArray &input); +Events parseEvents(const QByteArray &input); Error parseErrorMessage(const QString &message); } // namespace ResultParser From 51b6aa76492bfeb1ea6e69011f8371c84faa3616 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 30 May 2022 13:39:08 +0200 Subject: [PATCH 51/58] ClangCodeModel: Move highlighting code into its own set of files No functional changes. Change-Id: If6e5da7e79bf39e564f0f38520ae088f76543642 Reviewed-by: David Schulz Reviewed-by: Reviewed-by: Qt CI Bot --- src/plugins/clangcodemodel/CMakeLists.txt | 4 +- src/plugins/clangcodemodel/clangcodemodel.qbs | 4 + src/plugins/clangcodemodel/clangdclient.cpp | 988 +----------------- .../clangdsemantichighlighting.cpp | 916 ++++++++++++++++ .../clangdsemantichighlighting.h | 57 + src/plugins/clangcodemodel/tasktimers.cpp | 108 ++ src/plugins/clangcodemodel/tasktimers.h | 90 ++ 7 files changed, 1181 insertions(+), 986 deletions(-) create mode 100644 src/plugins/clangcodemodel/clangdsemantichighlighting.cpp create mode 100644 src/plugins/clangcodemodel/clangdsemantichighlighting.h create mode 100644 src/plugins/clangcodemodel/tasktimers.cpp create mode 100644 src/plugins/clangcodemodel/tasktimers.h diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt index 7bcfb4e7a9c..ef813ff5ef4 100644 --- a/src/plugins/clangcodemodel/CMakeLists.txt +++ b/src/plugins/clangcodemodel/CMakeLists.txt @@ -15,11 +15,12 @@ add_qtc_plugin(ClangCodeModel clangcodemodelplugin.cpp clangcodemodelplugin.h clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h clangconstants.h - clangdclient.cpp clangdclient.h clangdast.cpp clangdast.h + clangdclient.cpp clangdclient.h clangdiagnostictooltipwidget.cpp clangdiagnostictooltipwidget.h clangdquickfixfactory.cpp clangdquickfixfactory.h clangdqpropertyhighlighter.cpp clangdqpropertyhighlighter.h + clangdsemantichighlighting.cpp clangdsemantichighlighting.h clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h clangfixitoperation.cpp clangfixitoperation.h clangdlocatorfilters.cpp clangdlocatorfilters.h @@ -28,6 +29,7 @@ add_qtc_plugin(ClangCodeModel clangtextmark.cpp clangtextmark.h clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h clangutils.cpp clangutils.h + tasktimers.cpp tasktimers.h EXPLICIT_MOC clangcodemodelplugin.h ) diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index f1d2c89497a..b16c685c99c 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -42,6 +42,8 @@ QtcPlugin { "clangdqpropertyhighlighter.h", "clangdquickfixfactory.cpp", "clangdquickfixfactory.h", + "clangdsemantichighlighting.cpp", + "clangdsemantichighlighting.h", "clangeditordocumentprocessor.cpp", "clangeditordocumentprocessor.h", "clangfixitoperation.cpp", @@ -56,6 +58,8 @@ QtcPlugin { "clanguiheaderondiskmanager.h", "clangutils.cpp", "clangutils.h", + "tasktimers.cpp", + "tasktimers.h", ] Group { diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 64f8b014261..836ba30cfe3 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -29,11 +29,11 @@ #include "clangconstants.h" #include "clangdast.h" #include "clangdlocatorfilters.h" -#include "clangdqpropertyhighlighter.h" -#include "clangmodelmanagersupport.h" #include "clangpreprocessorassistproposalitem.h" #include "clangtextmark.h" #include "clangutils.h" +#include "clangdsemantichighlighting.h" +#include "tasktimers.h" #include #include @@ -89,7 +89,6 @@ #include #include -#include #include #include #include @@ -100,7 +99,6 @@ #include #include #include -#include #include #include @@ -120,8 +118,6 @@ namespace Internal { static Q_LOGGING_CATEGORY(clangdLog, "qtc.clangcodemodel.clangd", QtWarningMsg); static Q_LOGGING_CATEGORY(clangdLogServer, "qtc.clangcodemodel.clangd.server", QtWarningMsg); static Q_LOGGING_CATEGORY(clangdLogAst, "qtc.clangcodemodel.clangd.ast", QtWarningMsg); -static Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg); -static Q_LOGGING_CATEGORY(clangdLogTiming, "qtc.clangcodemodel.clangd.timing", QtWarningMsg); static Q_LOGGING_CATEGORY(clangdLogCompletion, "qtc.clangcodemodel.clangd.completion", QtWarningMsg); static QString indexingToken() { return "backgroundIndexProgress"; } @@ -733,121 +729,6 @@ private: std::unordered_map> m_data; }; -class TaskTimer -{ -public: - TaskTimer(const QString &task) : m_task(task) {} - - void stopTask() - { - // This can happen due to the RAII mechanism employed with SubtaskTimer. - // The subtask timers will expire immediately after, so this does not distort - // the timing data. - if (m_subtasks > 0) { - QTC_CHECK(m_timer.isValid()); - m_elapsedMs += m_timer.elapsed(); - m_timer.invalidate(); - m_subtasks = 0; - } - m_started = false; - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_elapsedMs - << " ms in UI thread"; - m_elapsedMs = 0; - } - void startSubtask() - { - // We have some callbacks that are either synchronous or asynchronous, depending on - // dynamic conditions. In the sync case, we will have nested subtasks, in which case - // the inner ones must not collect timing data, as their code blocks are already covered. - if (++m_subtasks > 1) - return; - if (!m_started) { - QTC_ASSERT(m_elapsedMs == 0, m_elapsedMs = 0); - m_started = true; - m_finalized = false; - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting"; - - // Used by ThreadedSubtaskTimer to mark the end of the whole highlighting operation - m_startTimer.restart(); - } - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask started at " - << QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz"); - QTC_CHECK(!m_timer.isValid()); - m_timer.start(); - } - void stopSubtask(bool isFinalizing) - { - if (m_subtasks == 0) // See stopTask(). - return; - if (isFinalizing) - m_finalized = true; - if (--m_subtasks > 0) // See startSubtask(). - return; - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask stopped at " - << QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz"); - QTC_CHECK(m_timer.isValid()); - m_elapsedMs += m_timer.elapsed(); - m_timer.invalidate(); - if (m_finalized) - stopTask(); - } - - QElapsedTimer startTimer() const { return m_startTimer; } - -private: - const QString m_task; - QElapsedTimer m_timer; - QElapsedTimer m_startTimer; - qint64 m_elapsedMs = 0; - int m_subtasks = 0; - bool m_started = false; - bool m_finalized = false; -}; - -class SubtaskTimer -{ -public: - SubtaskTimer(TaskTimer &timer) : m_timer(timer) { m_timer.startSubtask(); } - ~SubtaskTimer() { m_timer.stopSubtask(m_isFinalizing); } - -protected: - void makeFinalizing() { m_isFinalizing = true; } - -private: - TaskTimer &m_timer; - bool m_isFinalizing = false; -}; - -class FinalizingSubtaskTimer : public SubtaskTimer -{ -public: - FinalizingSubtaskTimer(TaskTimer &timer) : SubtaskTimer(timer) { makeFinalizing(); } -}; - -class ThreadedSubtaskTimer -{ -public: - ThreadedSubtaskTimer(const QString &task, const TaskTimer &taskTimer) : m_task(task), m_taskTimer(taskTimer) - { - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting thread"; - m_timer.start(); - } - - ~ThreadedSubtaskTimer() - { - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_timer.elapsed() - << " ms in dedicated thread"; - - qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": Start to end: " - << m_taskTimer.startTimer().elapsed() << " ms"; - } - -private: - const QString m_task; - QElapsedTimer m_timer; - const TaskTimer &m_taskTimer; -}; - class MemoryTreeModel; class MemoryUsageWidget : public QWidget { @@ -2593,349 +2474,6 @@ void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const q->hoverHandler()->setHelpItem(token, helpItem); } -class ExtraHighlightingResultsCollector -{ -public: - ExtraHighlightingResultsCollector(QFutureInterface &future, - HighlightingResults &results, - const Utils::FilePath &filePath, const ClangdAstNode &ast, - const QTextDocument *doc, const QString &docContent); - - void collect(); -private: - static bool lessThan(const HighlightingResult &r1, const HighlightingResult &r2); - static int onlyIndexOf(const QStringView &text, const QStringView &subString, int from = 0); - int posForNodeStart(const ClangdAstNode &node) const; - int posForNodeEnd(const ClangdAstNode &node) const; - void insertResult(const HighlightingResult &result); - void insertResult(const ClangdAstNode &node, TextStyle style); - void insertAngleBracketInfo(int searchStart1, int searchEnd1, int searchStart2, int searchEnd2); - void setResultPosFromRange(HighlightingResult &result, const Range &range); - void collectFromNode(const ClangdAstNode &node); - void visitNode(const ClangdAstNode&node); - - QFutureInterface &m_future; - HighlightingResults &m_results; - const Utils::FilePath m_filePath; - const ClangdAstNode &m_ast; - const QTextDocument * const m_doc; - const QString &m_docContent; - ClangdAstNode::FileStatus m_currentFileStatus = ClangdAstNode::FileStatus::Unknown; -}; - -// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled, -// and not even in a consistent manner. We don't want this, so we have to clean up here. -// But note that we require this behavior, as otherwise we would not be able to grey out -// e.g. empty lines after an #ifdef, due to the lack of symbols. -static QList cleanupDisabledCode(HighlightingResults &results, const QTextDocument *doc, - const QString &docContent) -{ - QList ifdefedOutRanges; - int rangeStartPos = -1; - for (auto it = results.begin(); it != results.end();) { - const bool wasIfdefedOut = rangeStartPos != -1; - const bool isIfDefedOut = it->textStyles.mainStyle == C_DISABLED_CODE; - if (!isIfDefedOut) { - if (wasIfdefedOut) { - const QTextBlock block = doc->findBlockByNumber(it->line - 1); - ifdefedOutRanges << BlockRange(rangeStartPos, block.position()); - rangeStartPos = -1; - } - ++it; - continue; - } - - if (!wasIfdefedOut) - rangeStartPos = doc->findBlockByNumber(it->line - 1).position(); - - // Does the current line contain a potential "ifdefed-out switcher"? - // If not, no state change is possible and we continue with the next line. - const auto isPreprocessorControlStatement = [&] { - const int pos = Utils::Text::positionInText(doc, it->line, it->column); - const QStringView content = subViewLen(docContent, pos, it->length).trimmed(); - if (content.isEmpty() || content.first() != '#') - return false; - int offset = 1; - while (offset < content.size() && content.at(offset).isSpace()) - ++offset; - if (offset == content.size()) - return false; - const QStringView ppDirective = content.mid(offset); - return ppDirective.startsWith(QLatin1String("if")) - || ppDirective.startsWith(QLatin1String("elif")) - || ppDirective.startsWith(QLatin1String("else")) - || ppDirective.startsWith(QLatin1String("endif")); - }; - if (!isPreprocessorControlStatement()) { - ++it; - continue; - } - - if (!wasIfdefedOut) { - // The #if or #else that starts disabled code should not be disabled. - const QTextBlock nextBlock = doc->findBlockByNumber(it->line); - rangeStartPos = nextBlock.isValid() ? nextBlock.position() : -1; - it = results.erase(it); - continue; - } - - if (wasIfdefedOut && (it + 1 == results.end() - || (it + 1)->textStyles.mainStyle != C_DISABLED_CODE - || (it + 1)->line != it->line + 1)) { - // The #else or #endif that ends disabled code should not be disabled. - const QTextBlock block = doc->findBlockByNumber(it->line - 1); - ifdefedOutRanges << BlockRange(rangeStartPos, block.position()); - rangeStartPos = -1; - it = results.erase(it); - continue; - } - ++it; - } - - if (rangeStartPos != -1) - ifdefedOutRanges << BlockRange(rangeStartPos, doc->characterCount()); - - qCDebug(clangdLogHighlight) << "found" << ifdefedOutRanges.size() << "ifdefed-out ranges"; - if (clangdLogHighlight().isDebugEnabled()) { - for (const BlockRange &r : qAsConst(ifdefedOutRanges)) - qCDebug(clangdLogHighlight) << r.first() << r.last(); - } - - return ifdefedOutRanges; -} - -static void semanticHighlighter(QFutureInterface &future, - const Utils::FilePath &filePath, - const QList &tokens, - const QString &docContents, const ClangdAstNode &ast, - const QPointer &textDocument, - int docRevision, const QVersionNumber &clangdVersion, - const TaskTimer &taskTimer) -{ - ThreadedSubtaskTimer t("highlighting", taskTimer); - if (future.isCanceled()) { - future.reportFinished(); - return; - } - - const QTextDocument doc(docContents); - const auto tokenRange = [&doc](const ExpandedSemanticToken &token) { - const Position startPos(token.line - 1, token.column - 1); - const Position endPos = startPos.withOffset(token.length, &doc); - return Range(startPos, endPos); - }; - const auto isOutputParameter = [&ast, &tokenRange](const ExpandedSemanticToken &token) { - if (token.modifiers.contains(QLatin1String("usedAsMutableReference"))) - return true; - if (token.type != "variable" && token.type != "property" && token.type != "parameter") - return false; - const Range range = tokenRange(token); - const ClangdAstPath path = getAstPath(ast, range); - if (path.size() < 2) - return false; - if (token.type == "property" - && (path.rbegin()->kind() == "MemberInitializer" - || path.rbegin()->kind() == "CXXConstruct")) { - return false; - } - if (path.rbegin()->hasConstType()) - return false; - for (auto it = path.rbegin() + 1; it != path.rend(); ++it) { - if (it->kind() == "CXXConstruct" || it->kind() == "MemberInitializer") - return true; - - if (it->kind() == "Call") { - // The first child is e.g. a called lambda or an object on which - // the call happens, and should not be highlighted as an output argument. - // If the call is not fully resolved (as in templates), we don't - // know whether the argument is passed as const or not. - if (it->arcanaContains("dependent type")) - return false; - const QList children = it->children().value_or(QList()); - return children.isEmpty() - || (children.first().range() != (it - 1)->range() - && children.first().kind() != "UnresolvedLookup"); - } - - // The token should get marked for e.g. lambdas, but not for assignment operators, - // where the user sees that it's being written. - if (it->kind() == "CXXOperatorCall") { - const QList children = it->children().value_or(QList()); - - // Child 1 is the call itself, Child 2 is the named entity on which the call happens - // (a lambda or a class instance), after that follow the actual call arguments. - if (children.size() < 2) - return false; - - // The call itself is never modifiable. - if (children.first().range() == range) - return false; - - // The callable is never displayed as an output parameter. - // TODO: A good argument can be made to display objects on which a non-const - // operator or function is called as output parameters. - if (children.at(1).range().contains(range)) - return false; - - QList firstChildTree{children.first()}; - while (!firstChildTree.isEmpty()) { - const ClangdAstNode n = firstChildTree.takeFirst(); - const QString detail = n.detail().value_or(QString()); - if (detail.startsWith("operator")) { - return !detail.contains('=') - && !detail.contains("++") && !detail.contains("--") - && !detail.contains("<<") && !detail.contains(">>") - && !detail.contains("*"); - } - firstChildTree << n.children().value_or(QList()); - } - return true; - } - - if (it->kind() == "Lambda") - return false; - if (it->kind() == "BinaryOperator") - return false; - if (it->hasConstType()) - return false; - - if (it->kind() == "CXXMemberCall") { - if (it == path.rbegin()) - return false; - const QList children = it->children().value_or(QList()); - QTC_ASSERT(!children.isEmpty(), return false); - - // The called object is never displayed as an output parameter. - // TODO: A good argument can be made to display objects on which a non-const - // operator or function is called as output parameters. - return (it - 1)->range() != children.first().range(); - } - - if (it->kind() == "Member" && it->arcanaContains("(") - && !it->arcanaContains("bound member function type")) { - return false; - } - } - return false; - }; - - const std::function toResult - = [&ast, &isOutputParameter, &clangdVersion, &tokenRange] - (const ExpandedSemanticToken &token) { - TextStyles styles; - if (token.type == "variable") { - if (token.modifiers.contains(QLatin1String("functionScope"))) { - styles.mainStyle = C_LOCAL; - } else if (token.modifiers.contains(QLatin1String("classScope"))) { - styles.mainStyle = C_FIELD; - } else if (token.modifiers.contains(QLatin1String("fileScope")) - || token.modifiers.contains(QLatin1String("globalScope"))) { - styles.mainStyle = C_GLOBAL; - } - } else if (token.type == "function" || token.type == "method") { - styles.mainStyle = token.modifiers.contains(QLatin1String("virtual")) - ? C_VIRTUAL_METHOD : C_FUNCTION; - if (ast.isValid()) { - const ClangdAstPath path = getAstPath(ast, tokenRange(token)); - if (path.length() > 1) { - const ClangdAstNode declNode = path.at(path.length() - 2); - if (declNode.kind() == "Function" || declNode.kind() == "CXXMethod") { - if (clangdVersion < QVersionNumber(14) - && declNode.arcanaContains("' virtual")) { - styles.mainStyle = C_VIRTUAL_METHOD; - } - if (declNode.hasChildWithRole("statement")) - styles.mixinStyles.push_back(C_FUNCTION_DEFINITION); - } - } - } - } else if (token.type == "class") { - styles.mainStyle = C_TYPE; - - // clang hardly ever differentiates between constructors and the associated class, - // whereas we highlight constructors as functions. - if (ast.isValid()) { - const ClangdAstPath path = getAstPath(ast, tokenRange(token)); - if (!path.isEmpty()) { - if (path.last().kind() == "CXXConstructor") { - if (!path.last().arcanaContains("implicit")) - styles.mainStyle = C_FUNCTION; - } else if (path.last().kind() == "Record" && path.length() > 1) { - const ClangdAstNode node = path.at(path.length() - 2); - if (node.kind() == "CXXDestructor" && !node.arcanaContains("implicit")) { - styles.mainStyle = C_FUNCTION; - - // https://github.com/clangd/clangd/issues/872 - if (node.role() == "declaration") - styles.mixinStyles.push_back(C_DECLARATION); - } - } - } - } - } else if (token.type == "comment") { // "comment" means code disabled via the preprocessor - styles.mainStyle = C_DISABLED_CODE; - } else if (token.type == "namespace") { - styles.mainStyle = C_NAMESPACE; - } else if (token.type == "property") { - styles.mainStyle = C_FIELD; - } else if (token.type == "enum") { - styles.mainStyle = C_TYPE; - } else if (token.type == "enumMember") { - styles.mainStyle = C_ENUMERATION; - } else if (token.type == "parameter") { - styles.mainStyle = C_PARAMETER; - } else if (token.type == "macro") { - styles.mainStyle = C_PREPROCESSOR; - } else if (token.type == "type") { - styles.mainStyle = C_TYPE; - } else if (token.type == "typeParameter") { - // clangd reports both type and non-type template parameters as type parameters, - // but the latter can be distinguished by the readonly modifier. - styles.mainStyle = token.modifiers.contains(QLatin1String("readonly")) - ? C_PARAMETER : C_TYPE; - } - if (token.modifiers.contains(QLatin1String("declaration"))) - styles.mixinStyles.push_back(C_DECLARATION); - if (token.modifiers.contains(QLatin1String("static"))) { - if (styles.mainStyle != C_FIELD && styles.mainStyle != C_TEXT) - styles.mixinStyles.push_back(styles.mainStyle); - styles.mainStyle = C_STATIC_MEMBER; - } - if (isOutputParameter(token)) - styles.mixinStyles.push_back(C_OUTPUT_ARGUMENT); - qCDebug(clangdLogHighlight) << "adding highlighting result" - << token.line << token.column << token.length << int(styles.mainStyle); - return HighlightingResult(token.line, token.column, token.length, styles); - }; - - auto results = QtConcurrent::blockingMapped(tokens, toResult); - const QList ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents); - ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect(); - if (!future.isCanceled()) { - qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results"; - QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] { - if (textDocument && textDocument->document()->revision() == docRevision) - textDocument->setIfdefedOutBlocks(ifdefedOutBlocks); - }, Qt::QueuedConnection); - QList virtualRanges; - for (const HighlightingResult &r : results) { - if (r.textStyles.mainStyle != C_VIRTUAL_METHOD) - continue; - const Position startPos(r.line - 1, r.column - 1); - virtualRanges << Range(startPos, startPos.withOffset(r.length, &doc)); - } - QMetaObject::invokeMethod(ClangModelManagerSupport::instance(), - [filePath, virtualRanges, docRevision] { - if (ClangdClient * const client - = ClangModelManagerSupport::instance()->clientForFile(filePath)) { - client->setVirtualRanges(filePath, virtualRanges, docRevision); - } - }, Qt::QueuedConnection); - future.reportResults(QVector(results.cbegin(), results.cend())); - } - future.reportFinished(); -} - // Unfortunately, clangd ignores almost everything except symbols when sending // semantic token info, so we need to consult the AST for additional information. // In particular, we inspect the following constructs: @@ -2996,7 +2534,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc, doc = QPointer(doc), rev = doc->document()->revision(), clangdVersion = q->versionNumber(), this] { - return Utils::runAsync(semanticHighlighter, filePath, tokens, text, ast, doc, rev, + return Utils::runAsync(doSemanticHighlighting, filePath, tokens, text, ast, doc, rev, clangdVersion, highlightingTimer); }; @@ -3472,526 +3010,6 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc, return requestAst(q, filePath, range, wrapperHandler); } -ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector( - QFutureInterface &future, HighlightingResults &results, - const Utils::FilePath &filePath, const ClangdAstNode &ast, const QTextDocument *doc, - const QString &docContent) - : m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc), - m_docContent(docContent) -{ -} - -void ExtraHighlightingResultsCollector::collect() -{ - for (int i = 0; i < m_results.length(); ++i) { - const HighlightingResult res = m_results.at(i); - if (res.textStyles.mainStyle != C_PREPROCESSOR || res.length != 10) - continue; - const int pos = Utils::Text::positionInText(m_doc, res.line, res.column); - if (subViewLen(m_docContent, pos, 10) != QLatin1String("Q_PROPERTY")) - continue; - int endPos; - if (i < m_results.length() - 1) { - const HighlightingResult nextRes = m_results.at(i + 1); - endPos = Utils::Text::positionInText(m_doc, nextRes.line, nextRes.column); - } else { - endPos = m_docContent.length(); - } - const QString qPropertyString = m_docContent.mid(pos, endPos - pos); - QPropertyHighlighter propHighlighter(m_doc, qPropertyString, pos); - for (const HighlightingResult &newRes : propHighlighter.highlight()) - m_results.insert(++i, newRes); - } - - if (!m_ast.isValid()) - return; - visitNode(m_ast); -} - -bool ExtraHighlightingResultsCollector::lessThan(const HighlightingResult &r1, - const HighlightingResult &r2) -{ - return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column) - || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length); -} - -int ExtraHighlightingResultsCollector::onlyIndexOf(const QStringView &text, - const QStringView &subString, int from) -{ - const int firstIndex = text.indexOf(subString, from); - if (firstIndex == -1) - return -1; - const int nextIndex = text.indexOf(subString, firstIndex + 1); - - // The second condion deals with the off-by-one error in TemplateSpecialization nodes; - // see collectFromNode(). - return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1; -} - -// Unfortunately, the exact position of a specific token is usually not -// recorded in the AST, so if we need that, we have to search for it textually. -// In corner cases, this might get sabotaged by e.g. comments, in which case we give up. -int ExtraHighlightingResultsCollector::posForNodeStart(const ClangdAstNode &node) const -{ - return Utils::Text::positionInText(m_doc, node.range().start().line() + 1, - node.range().start().character() + 1); -} - -int ExtraHighlightingResultsCollector::posForNodeEnd(const ClangdAstNode &node) const -{ - return Utils::Text::positionInText(m_doc, node.range().end().line() + 1, - node.range().end().character() + 1); -} - -void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &result) -{ - if (!result.isValid()) // Some nodes don't have a range. - return; - const auto it = std::lower_bound(m_results.begin(), m_results.end(), result, lessThan); - if (it == m_results.end() || *it != result) { - - // Prevent inserting expansions for function-like macros. For instance: - // #define TEST() "blubb" - // const char *s = TEST(); - // The macro name is always shorter than the expansion and starts at the same - // location, so it should occur right before the insertion position. - if (it > m_results.begin() && (it - 1)->line == result.line - && (it - 1)->column == result.column - && (it - 1)->textStyles.mainStyle == C_PREPROCESSOR) { - return; - } - - qCDebug(clangdLogHighlight) << "adding additional highlighting result" - << result.line << result.column << result.length; - m_results.insert(it, result); - return; - } - - // This is for conversion operators, whose type part is only reported as a type by clangd. - if ((it->textStyles.mainStyle == C_TYPE - || it->textStyles.mainStyle == C_PRIMITIVE_TYPE) - && !result.textStyles.mixinStyles.empty() - && result.textStyles.mixinStyles.at(0) == C_OPERATOR) { - it->textStyles.mixinStyles = result.textStyles.mixinStyles; - } -} - -void ExtraHighlightingResultsCollector::insertResult(const ClangdAstNode &node, TextStyle style) -{ - HighlightingResult result; - result.useTextSyles = true; - result.textStyles.mainStyle = style; - setResultPosFromRange(result, node.range()); - insertResult(result); - return; -} - -// For matching the "<" and ">" brackets of template declarations, specializations -// and instantiations. -void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1, int searchEnd1, - int searchStart2, int searchEnd2) -{ - const int openingAngleBracketPos = onlyIndexOf( - subViewEnd(m_docContent, searchStart1, searchEnd1), - QStringView(QStringLiteral("<"))); - if (openingAngleBracketPos == -1) - return; - const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos; - if (absOpeningAngleBracketPos > searchStart2) - searchStart2 = absOpeningAngleBracketPos + 1; - if (searchStart2 >= searchEnd2) - return; - const int closingAngleBracketPos = onlyIndexOf( - subViewEnd(m_docContent, searchStart2, searchEnd2), - QStringView(QStringLiteral(">"))); - if (closingAngleBracketPos == -1) - return; - - const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos; - if (absOpeningAngleBracketPos > absClosingAngleBracketPos) - return; - - HighlightingResult result; - result.useTextSyles = true; - result.textStyles.mainStyle = C_PUNCTUATION; - Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column); - result.length = 1; - result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen; - insertResult(result); - Utils::Text::convertPosition(m_doc, absClosingAngleBracketPos, &result.line, &result.column); - result.kind = CppEditor::SemanticHighlighter::AngleBracketClose; - insertResult(result); -} - -void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult &result, - const Range &range) -{ - if (!range.isValid()) - return; - const Position startPos = range.start(); - const Position endPos = range.end(); - result.line = startPos.line() + 1; - result.column = startPos.character() + 1; - result.length = endPos.toPositionInDocument(m_doc) - startPos.toPositionInDocument(m_doc); -} - -void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &node) -{ - if (node.kind() == "UserDefinedLiteral") - return; - if (node.kind().endsWith("Literal")) { - const bool isKeyword = node.kind() == "CXXBoolLiteral" - || node.kind() == "CXXNullPtrLiteral"; - const bool isStringLike = !isKeyword && (node.kind().startsWith("String") - || node.kind().startsWith("Character")); - const TextStyle style = isKeyword ? C_KEYWORD : isStringLike ? C_STRING : C_NUMBER; - insertResult(node, style); - return; - } - if (node.role() == "type" && node.kind() == "Builtin") { - insertResult(node, C_PRIMITIVE_TYPE); - return; - } - if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) { - insertResult(node, C_KEYWORD); - return; - } - - const bool isExpression = node.role() == "expression"; - if (isExpression && node.kind() == "Predefined") { - insertResult(node, C_PREPROCESSOR); - return; - } - - const bool isDeclaration = node.role() == "declaration"; - const int nodeStartPos = posForNodeStart(node); - const int nodeEndPos = posForNodeEnd(node); - const QList children = node.children().value_or(QList()); - - // Match question mark and colon in ternary operators. - if (isExpression && node.kind() == "ConditionalOperator") { - if (children.size() != 3) - return; - - // The question mark is between sub-expressions 1 and 2, the colon is between - // sub-expressions 2 and 3. - const int searchStartPosQuestionMark = posForNodeEnd(children.first()); - const int searchEndPosQuestionMark = posForNodeStart(children.at(1)); - QStringView content = subViewEnd(m_docContent, searchStartPosQuestionMark, - searchEndPosQuestionMark); - const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?"))); - if (questionMarkPos == -1) - return; - const int searchStartPosColon = posForNodeEnd(children.at(1)); - const int searchEndPosColon = posForNodeStart(children.at(2)); - content = subViewEnd(m_docContent, searchStartPosColon, searchEndPosColon); - const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":"))); - if (colonPos == -1) - return; - - const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos; - const int absColonPos = searchStartPosColon + colonPos; - if (absQuestionMarkPos > absColonPos) - return; - - HighlightingResult result; - result.useTextSyles = true; - result.textStyles.mainStyle = C_PUNCTUATION; - result.textStyles.mixinStyles.push_back(C_OPERATOR); - Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column); - result.length = 1; - result.kind = CppEditor::SemanticHighlighter::TernaryIf; - insertResult(result); - Utils::Text::convertPosition(m_doc, absColonPos, &result.line, &result.column); - result.kind = CppEditor::SemanticHighlighter::TernaryElse; - insertResult(result); - return; - } - - if (isDeclaration && (node.kind() == "FunctionTemplate" - || node.kind() == "ClassTemplate")) { - // The child nodes are the template parameters and and the function or class. - // The opening angle bracket is before the first child node, the closing angle - // bracket is before the function child node and after the last param node. - const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate" - ? "Function" : "CXXRecord"); - const auto functionOrClassIt = std::find_if(children.begin(), children.end(), - [&classOrFunctionKind](const ClangdAstNode &n) { - return n.role() == "declaration" && n.kind() == classOrFunctionKind; - }); - if (functionOrClassIt == children.end() || functionOrClassIt == children.begin()) - return; - const int firstTemplateParamStartPos = posForNodeStart(children.first()); - const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1)); - const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt); - insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos, - lastTemplateParamEndPos, functionOrClassStartPos); - return; - } - - const auto isTemplateParamDecl = [](const ClangdAstNode &node) { - return node.isTemplateParameterDeclaration(); - }; - if (isDeclaration && node.kind() == "TypeAliasTemplate") { - // Children are one node of type TypeAlias and the template parameters. - // The opening angle bracket is before the first parameter and the closing - // angle bracket is after the last parameter. - // The TypeAlias node seems to appear first in the AST, even though lexically - // is comes after the parameters. We don't rely on the order here. - // Note that there is a second pair of angle brackets. That one is part of - // a TemplateSpecialization, which is handled further below. - const auto firstTemplateParam = std::find_if(children.begin(), children.end(), - isTemplateParamDecl); - if (firstTemplateParam == children.end()) - return; - const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(), - isTemplateParamDecl); - QTC_ASSERT(lastTemplateParam != children.rend(), return); - const auto typeAlias = std::find_if(children.begin(), children.end(), - [](const ClangdAstNode &n) { return n.kind() == "TypeAlias"; }); - if (typeAlias == children.end()) - return; - - const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam); - const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam); - const int searchEndPos = posForNodeStart(*typeAlias); - insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos, - lastTemplateParamEndPos, searchEndPos); - return; - } - - if (isDeclaration && node.kind() == "ClassTemplateSpecialization") { - // There is one child of kind TemplateSpecialization. The first pair - // of angle brackets comes before that. - if (children.size() == 1) { - const int childNodePos = posForNodeStart(children.first()); - insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos); - } - return; - } - - if (isDeclaration && node.kind() == "TemplateTemplateParm") { - // The child nodes are template arguments and template parameters. - // Arguments seem to appear before parameters in the AST, even though they - // come after them in the source code. We don't rely on the order here. - const auto firstTemplateParam = std::find_if(children.begin(), children.end(), - isTemplateParamDecl); - if (firstTemplateParam == children.end()) - return; - const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(), - isTemplateParamDecl); - QTC_ASSERT(lastTemplateParam != children.rend(), return); - const auto templateArg = std::find_if(children.begin(), children.end(), - [](const ClangdAstNode &n) { return n.role() == "template argument"; }); - - const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam); - const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam); - const int searchEndPos = templateArg == children.end() - ? nodeEndPos : posForNodeStart(*templateArg); - insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos, - lastTemplateParamEndPos, searchEndPos); - return; - } - - // {static,dynamic,reinterpret}_cast<>(). - if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) { - // First child is type, second child is expression. - // The opening angle bracket is before the first child, the closing angle bracket - // is between the two children. - if (children.size() == 2) { - insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()), - posForNodeEnd(children.first()), - posForNodeStart(children.last())); - } - return; - } - - if (node.kind() == "TemplateSpecialization") { - // First comes the template type, then the template arguments. - // The opening angle bracket is before the first template argument, - // the closing angle bracket is after the last template argument. - // The first child node has no range, so we start searching at the parent node. - if (children.size() >= 2) { - int searchStart2 = posForNodeEnd(children.last()); - int searchEnd2 = nodeEndPos; - - // There is a weird off-by-one error on the clang side: If there is a - // nested template instantiation *and* there is no space between - // the closing angle brackets, then the inner TemplateSpecialization node's range - // will extend one character too far, covering the outer's closing angle bracket. - // This is what we are correcting for here. - // This issue is tracked at https://github.com/clangd/clangd/issues/871. - if (searchStart2 == searchEnd2) - --searchStart2; - insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)), - searchStart2, searchEnd2); - } - return; - } - - if (!isExpression && !isDeclaration) - return; - - // Operators, overloaded ones in particular. - static const QString operatorPrefix = "operator"; - QString detail = node.detail().value_or(QString()); - const bool isCallToNew = node.kind() == "CXXNew"; - const bool isCallToDelete = node.kind() == "CXXDelete"; - const auto isProperOperator = [&] { - if (isCallToNew || isCallToDelete) - return true; - if (!detail.startsWith(operatorPrefix)) - return false; - if (detail == operatorPrefix) - return false; - const QChar nextChar = detail.at(operatorPrefix.length()); - return !nextChar.isLetterOrNumber() && nextChar != '_'; - }; - if (!isProperOperator()) - return; - - if (!isCallToNew && !isCallToDelete) - detail.remove(0, operatorPrefix.length()); - - HighlightingResult result; - result.useTextSyles = true; - const bool isConversionOp = node.kind() == "CXXConversion"; - const bool isOverloaded = !isConversionOp - && (isDeclaration || ((!isCallToNew && !isCallToDelete) - || node.arcanaContains("CXXMethod"))); - result.textStyles.mainStyle = isConversionOp - ? C_PRIMITIVE_TYPE - : isCallToNew || isCallToDelete || detail.at(0).isSpace() - ? C_KEYWORD : C_PUNCTUATION; - result.textStyles.mixinStyles.push_back(C_OPERATOR); - if (isOverloaded) - result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR); - if (isDeclaration) - result.textStyles.mixinStyles.push_back(C_DECLARATION); - - const QStringView nodeText = subViewEnd(m_docContent, nodeStartPos, nodeEndPos); - - if (isCallToNew || isCallToDelete) { - result.line = node.range().start().line() + 1; - result.column = node.range().start().character() + 1; - result.length = isCallToNew ? 3 : 6; - insertResult(result); - if (node.arcanaContains("array")) { - const int openingBracketOffset = nodeText.indexOf('['); - if (openingBracketOffset == -1) - return; - const int closingBracketOffset = nodeText.lastIndexOf(']'); - if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset) - return; - - result.textStyles.mainStyle = C_PUNCTUATION; - result.length = 1; - Utils::Text::convertPosition(m_doc, - nodeStartPos + openingBracketOffset, - &result.line, &result.column); - insertResult(result); - Utils::Text::convertPosition(m_doc, - nodeStartPos + closingBracketOffset, - &result.line, &result.column); - insertResult(result); - } - return; - } - - if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) { - result.line = node.range().start().line() + 1; - result.column = node.range().start().character() + 1; - result.length = 1; - insertResult(result); - result.line = node.range().end().line() + 1; - result.column = node.range().end().character(); - insertResult(result); - return; - } - - const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length(); - - // The simple case: Call to operator+, +=, * etc. - if (nodeEndPos - nodeStartPos == opStringLen) { - setResultPosFromRange(result, node.range()); - insertResult(result); - return; - } - - const int prefixOffset = nodeText.indexOf(operatorPrefix); - if (prefixOffset == -1) - return; - - const bool isArray = detail == "[]"; - const bool isCall = detail == "()"; - const bool isArrayNew = detail == " new[]"; - const bool isArrayDelete = detail == " delete[]"; - const QStringView searchTerm = isArray || isCall - ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete - ? QStringView(detail).chopped(2) : detail; - const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset - + operatorPrefix.length()); - if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1) - return; - - const int opStringOffsetInDoc = nodeStartPos + opStringOffset - + detail.length() - opStringLen; - Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column); - result.length = opStringLen; - if (isArray || isCall) - result.length = 1; - else if (isArrayNew || isArrayDelete) - result.length -= 2; - if (!isArray && !isCall) - insertResult(result); - if (!isArray && !isCall && !isArrayNew && !isArrayDelete) - return; - - result.textStyles.mainStyle = C_PUNCTUATION; - result.length = 1; - const int openingParenOffset = nodeText.indexOf( - isCall ? '(' : '[', prefixOffset + operatorPrefix.length()); - if (openingParenOffset == -1) - return; - const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1); - if (closingParenOffset == -1 || closingParenOffset < openingParenOffset) - return; - Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset, - &result.line, &result.column); - insertResult(result); - Utils::Text::convertPosition(m_doc, nodeStartPos + closingParenOffset, - &result.line, &result.column); - insertResult(result); -} - -void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node) -{ - if (m_future.isCanceled()) - return; - const ClangdAstNode::FileStatus prevFileStatus = m_currentFileStatus; - m_currentFileStatus = node.fileStatus(m_filePath); - if (m_currentFileStatus == ClangdAstNode::FileStatus::Unknown - && prevFileStatus != ClangdAstNode::FileStatus::Ours) { - m_currentFileStatus = prevFileStatus; - } - switch (m_currentFileStatus) { - case ClangdAstNode::FileStatus::Ours: - case ClangdAstNode::FileStatus::Unknown: - collectFromNode(node); - [[fallthrough]]; - case ClangdAstNode::FileStatus::Foreign: - case ClangCodeModel::Internal::ClangdAstNode::FileStatus::Mixed: { - const auto children = node.children(); - if (!children) - return; - for (const ClangdAstNode &childNode : *children) - visitNode(childNode); - break; - } - } - m_currentFileStatus = prevFileStatus; -} - bool ClangdClient::FollowSymbolData::defLinkIsAmbiguous() const { // Even if the call is to a virtual function, it might not be ambiguous: diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp new file mode 100644 index 00000000000..e3c6f08bdb9 --- /dev/null +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp @@ -0,0 +1,916 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangdsemantichighlighting.h" + +#include "clangdast.h" +#include "clangdclient.h" +#include "clangdqpropertyhighlighter.h" +#include "clangmodelmanagersupport.h" +#include "tasktimers.h" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace LanguageClient; +using namespace LanguageServerProtocol; +using namespace TextEditor; + +namespace ClangCodeModel::Internal { +Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg); + +// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled, +// and not even in a consistent manner. We don't want this, so we have to clean up here. +// But note that we require this behavior, as otherwise we would not be able to grey out +// e.g. empty lines after an #ifdef, due to the lack of symbols. +static QList cleanupDisabledCode(HighlightingResults &results, const QTextDocument *doc, + const QString &docContent) +{ + QList ifdefedOutRanges; + int rangeStartPos = -1; + for (auto it = results.begin(); it != results.end();) { + const bool wasIfdefedOut = rangeStartPos != -1; + const bool isIfDefedOut = it->textStyles.mainStyle == C_DISABLED_CODE; + if (!isIfDefedOut) { + if (wasIfdefedOut) { + const QTextBlock block = doc->findBlockByNumber(it->line - 1); + ifdefedOutRanges << BlockRange(rangeStartPos, block.position()); + rangeStartPos = -1; + } + ++it; + continue; + } + + if (!wasIfdefedOut) + rangeStartPos = doc->findBlockByNumber(it->line - 1).position(); + + // Does the current line contain a potential "ifdefed-out switcher"? + // If not, no state change is possible and we continue with the next line. + const auto isPreprocessorControlStatement = [&] { + const int pos = Utils::Text::positionInText(doc, it->line, it->column); + const QStringView content = subViewLen(docContent, pos, it->length).trimmed(); + if (content.isEmpty() || content.first() != '#') + return false; + int offset = 1; + while (offset < content.size() && content.at(offset).isSpace()) + ++offset; + if (offset == content.size()) + return false; + const QStringView ppDirective = content.mid(offset); + return ppDirective.startsWith(QLatin1String("if")) + || ppDirective.startsWith(QLatin1String("elif")) + || ppDirective.startsWith(QLatin1String("else")) + || ppDirective.startsWith(QLatin1String("endif")); + }; + if (!isPreprocessorControlStatement()) { + ++it; + continue; + } + + if (!wasIfdefedOut) { + // The #if or #else that starts disabled code should not be disabled. + const QTextBlock nextBlock = doc->findBlockByNumber(it->line); + rangeStartPos = nextBlock.isValid() ? nextBlock.position() : -1; + it = results.erase(it); + continue; + } + + if (wasIfdefedOut && (it + 1 == results.end() + || (it + 1)->textStyles.mainStyle != C_DISABLED_CODE + || (it + 1)->line != it->line + 1)) { + // The #else or #endif that ends disabled code should not be disabled. + const QTextBlock block = doc->findBlockByNumber(it->line - 1); + ifdefedOutRanges << BlockRange(rangeStartPos, block.position()); + rangeStartPos = -1; + it = results.erase(it); + continue; + } + ++it; + } + + if (rangeStartPos != -1) + ifdefedOutRanges << BlockRange(rangeStartPos, doc->characterCount()); + + qCDebug(clangdLogHighlight) << "found" << ifdefedOutRanges.size() << "ifdefed-out ranges"; + if (clangdLogHighlight().isDebugEnabled()) { + for (const BlockRange &r : qAsConst(ifdefedOutRanges)) + qCDebug(clangdLogHighlight) << r.first() << r.last(); + } + + return ifdefedOutRanges; +} + +class ExtraHighlightingResultsCollector +{ +public: + ExtraHighlightingResultsCollector(QFutureInterface &future, + HighlightingResults &results, + const Utils::FilePath &filePath, const ClangdAstNode &ast, + const QTextDocument *doc, const QString &docContent); + + void collect(); +private: + static bool lessThan(const HighlightingResult &r1, const HighlightingResult &r2); + static int onlyIndexOf(const QStringView &text, const QStringView &subString, int from = 0); + int posForNodeStart(const ClangdAstNode &node) const; + int posForNodeEnd(const ClangdAstNode &node) const; + void insertResult(const HighlightingResult &result); + void insertResult(const ClangdAstNode &node, TextStyle style); + void insertAngleBracketInfo(int searchStart1, int searchEnd1, int searchStart2, int searchEnd2); + void setResultPosFromRange(HighlightingResult &result, const Range &range); + void collectFromNode(const ClangdAstNode &node); + void visitNode(const ClangdAstNode&node); + + QFutureInterface &m_future; + HighlightingResults &m_results; + const Utils::FilePath m_filePath; + const ClangdAstNode &m_ast; + const QTextDocument * const m_doc; + const QString &m_docContent; + ClangdAstNode::FileStatus m_currentFileStatus = ClangdAstNode::FileStatus::Unknown; +}; + +void doSemanticHighlighting( + QFutureInterface &future, + const Utils::FilePath &filePath, + const QList &tokens, + const QString &docContents, + const ClangdAstNode &ast, + const QPointer &textDocument, + int docRevision, + const QVersionNumber &clangdVersion, + const TaskTimer &taskTimer) +{ + ThreadedSubtaskTimer t("highlighting", taskTimer); + if (future.isCanceled()) { + future.reportFinished(); + return; + } + + const QTextDocument doc(docContents); + const auto tokenRange = [&doc](const ExpandedSemanticToken &token) { + const Position startPos(token.line - 1, token.column - 1); + const Position endPos = startPos.withOffset(token.length, &doc); + return Range(startPos, endPos); + }; + const auto isOutputParameter = [&ast, &tokenRange](const ExpandedSemanticToken &token) { + if (token.modifiers.contains(QLatin1String("usedAsMutableReference"))) + return true; + if (token.type != "variable" && token.type != "property" && token.type != "parameter") + return false; + const Range range = tokenRange(token); + const ClangdAstPath path = getAstPath(ast, range); + if (path.size() < 2) + return false; + if (token.type == "property" + && (path.rbegin()->kind() == "MemberInitializer" + || path.rbegin()->kind() == "CXXConstruct")) { + return false; + } + if (path.rbegin()->hasConstType()) + return false; + for (auto it = path.rbegin() + 1; it != path.rend(); ++it) { + if (it->kind() == "CXXConstruct" || it->kind() == "MemberInitializer") + return true; + + if (it->kind() == "Call") { + // The first child is e.g. a called lambda or an object on which + // the call happens, and should not be highlighted as an output argument. + // If the call is not fully resolved (as in templates), we don't + // know whether the argument is passed as const or not. + if (it->arcanaContains("dependent type")) + return false; + const QList children = it->children().value_or(QList()); + return children.isEmpty() + || (children.first().range() != (it - 1)->range() + && children.first().kind() != "UnresolvedLookup"); + } + + // The token should get marked for e.g. lambdas, but not for assignment operators, + // where the user sees that it's being written. + if (it->kind() == "CXXOperatorCall") { + const QList children = it->children().value_or(QList()); + + // Child 1 is the call itself, Child 2 is the named entity on which the call happens + // (a lambda or a class instance), after that follow the actual call arguments. + if (children.size() < 2) + return false; + + // The call itself is never modifiable. + if (children.first().range() == range) + return false; + + // The callable is never displayed as an output parameter. + // TODO: A good argument can be made to display objects on which a non-const + // operator or function is called as output parameters. + if (children.at(1).range().contains(range)) + return false; + + QList firstChildTree{children.first()}; + while (!firstChildTree.isEmpty()) { + const ClangdAstNode n = firstChildTree.takeFirst(); + const QString detail = n.detail().value_or(QString()); + if (detail.startsWith("operator")) { + return !detail.contains('=') + && !detail.contains("++") && !detail.contains("--") + && !detail.contains("<<") && !detail.contains(">>") + && !detail.contains("*"); + } + firstChildTree << n.children().value_or(QList()); + } + return true; + } + + if (it->kind() == "Lambda") + return false; + if (it->kind() == "BinaryOperator") + return false; + if (it->hasConstType()) + return false; + + if (it->kind() == "CXXMemberCall") { + if (it == path.rbegin()) + return false; + const QList children = it->children().value_or(QList()); + QTC_ASSERT(!children.isEmpty(), return false); + + // The called object is never displayed as an output parameter. + // TODO: A good argument can be made to display objects on which a non-const + // operator or function is called as output parameters. + return (it - 1)->range() != children.first().range(); + } + + if (it->kind() == "Member" && it->arcanaContains("(") + && !it->arcanaContains("bound member function type")) { + return false; + } + } + return false; + }; + + const std::function toResult + = [&ast, &isOutputParameter, &clangdVersion, &tokenRange] + (const ExpandedSemanticToken &token) { + TextStyles styles; + if (token.type == "variable") { + if (token.modifiers.contains(QLatin1String("functionScope"))) { + styles.mainStyle = C_LOCAL; + } else if (token.modifiers.contains(QLatin1String("classScope"))) { + styles.mainStyle = C_FIELD; + } else if (token.modifiers.contains(QLatin1String("fileScope")) + || token.modifiers.contains(QLatin1String("globalScope"))) { + styles.mainStyle = C_GLOBAL; + } + } else if (token.type == "function" || token.type == "method") { + styles.mainStyle = token.modifiers.contains(QLatin1String("virtual")) + ? C_VIRTUAL_METHOD : C_FUNCTION; + if (ast.isValid()) { + const ClangdAstPath path = getAstPath(ast, tokenRange(token)); + if (path.length() > 1) { + const ClangdAstNode declNode = path.at(path.length() - 2); + if (declNode.kind() == "Function" || declNode.kind() == "CXXMethod") { + if (clangdVersion < QVersionNumber(14) + && declNode.arcanaContains("' virtual")) { + styles.mainStyle = C_VIRTUAL_METHOD; + } + if (declNode.hasChildWithRole("statement")) + styles.mixinStyles.push_back(C_FUNCTION_DEFINITION); + } + } + } + } else if (token.type == "class") { + styles.mainStyle = C_TYPE; + + // clang hardly ever differentiates between constructors and the associated class, + // whereas we highlight constructors as functions. + if (ast.isValid()) { + const ClangdAstPath path = getAstPath(ast, tokenRange(token)); + if (!path.isEmpty()) { + if (path.last().kind() == "CXXConstructor") { + if (!path.last().arcanaContains("implicit")) + styles.mainStyle = C_FUNCTION; + } else if (path.last().kind() == "Record" && path.length() > 1) { + const ClangdAstNode node = path.at(path.length() - 2); + if (node.kind() == "CXXDestructor" && !node.arcanaContains("implicit")) { + styles.mainStyle = C_FUNCTION; + + // https://github.com/clangd/clangd/issues/872 + if (node.role() == "declaration") + styles.mixinStyles.push_back(C_DECLARATION); + } + } + } + } + } else if (token.type == "comment") { // "comment" means code disabled via the preprocessor + styles.mainStyle = C_DISABLED_CODE; + } else if (token.type == "namespace") { + styles.mainStyle = C_NAMESPACE; + } else if (token.type == "property") { + styles.mainStyle = C_FIELD; + } else if (token.type == "enum") { + styles.mainStyle = C_TYPE; + } else if (token.type == "enumMember") { + styles.mainStyle = C_ENUMERATION; + } else if (token.type == "parameter") { + styles.mainStyle = C_PARAMETER; + } else if (token.type == "macro") { + styles.mainStyle = C_PREPROCESSOR; + } else if (token.type == "type") { + styles.mainStyle = C_TYPE; + } else if (token.type == "typeParameter") { + // clangd reports both type and non-type template parameters as type parameters, + // but the latter can be distinguished by the readonly modifier. + styles.mainStyle = token.modifiers.contains(QLatin1String("readonly")) + ? C_PARAMETER : C_TYPE; + } + if (token.modifiers.contains(QLatin1String("declaration"))) + styles.mixinStyles.push_back(C_DECLARATION); + if (token.modifiers.contains(QLatin1String("static"))) { + if (styles.mainStyle != C_FIELD && styles.mainStyle != C_TEXT) + styles.mixinStyles.push_back(styles.mainStyle); + styles.mainStyle = C_STATIC_MEMBER; + } + if (isOutputParameter(token)) + styles.mixinStyles.push_back(C_OUTPUT_ARGUMENT); + qCDebug(clangdLogHighlight) << "adding highlighting result" + << token.line << token.column << token.length << int(styles.mainStyle); + return HighlightingResult(token.line, token.column, token.length, styles); + }; + + auto results = QtConcurrent::blockingMapped(tokens, toResult); + const QList ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents); + ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect(); + if (!future.isCanceled()) { + qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results"; + QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] { + if (textDocument && textDocument->document()->revision() == docRevision) + textDocument->setIfdefedOutBlocks(ifdefedOutBlocks); + }, Qt::QueuedConnection); + QList virtualRanges; + for (const HighlightingResult &r : results) { + if (r.textStyles.mainStyle != C_VIRTUAL_METHOD) + continue; + const Position startPos(r.line - 1, r.column - 1); + virtualRanges << Range(startPos, startPos.withOffset(r.length, &doc)); + } + QMetaObject::invokeMethod(ClangModelManagerSupport::instance(), + [filePath, virtualRanges, docRevision] { + if (ClangdClient * const client + = ClangModelManagerSupport::instance()->clientForFile(filePath)) { + client->setVirtualRanges(filePath, virtualRanges, docRevision); + } + }, Qt::QueuedConnection); + future.reportResults(QVector(results.cbegin(), results.cend())); + } + future.reportFinished(); +} + +ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector( + QFutureInterface &future, HighlightingResults &results, + const Utils::FilePath &filePath, const ClangdAstNode &ast, const QTextDocument *doc, + const QString &docContent) + : m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc), + m_docContent(docContent) +{ +} + +void ExtraHighlightingResultsCollector::collect() +{ + for (int i = 0; i < m_results.length(); ++i) { + const HighlightingResult res = m_results.at(i); + if (res.textStyles.mainStyle != C_PREPROCESSOR || res.length != 10) + continue; + const int pos = Utils::Text::positionInText(m_doc, res.line, res.column); + if (subViewLen(m_docContent, pos, 10) != QLatin1String("Q_PROPERTY")) + continue; + int endPos; + if (i < m_results.length() - 1) { + const HighlightingResult nextRes = m_results.at(i + 1); + endPos = Utils::Text::positionInText(m_doc, nextRes.line, nextRes.column); + } else { + endPos = m_docContent.length(); + } + const QString qPropertyString = m_docContent.mid(pos, endPos - pos); + QPropertyHighlighter propHighlighter(m_doc, qPropertyString, pos); + for (const HighlightingResult &newRes : propHighlighter.highlight()) + m_results.insert(++i, newRes); + } + + if (!m_ast.isValid()) + return; + visitNode(m_ast); +} + +bool ExtraHighlightingResultsCollector::lessThan(const HighlightingResult &r1, + const HighlightingResult &r2) +{ + return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column) + || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length); +} + +int ExtraHighlightingResultsCollector::onlyIndexOf(const QStringView &text, + const QStringView &subString, int from) +{ + const int firstIndex = text.indexOf(subString, from); + if (firstIndex == -1) + return -1; + const int nextIndex = text.indexOf(subString, firstIndex + 1); + + // The second condion deals with the off-by-one error in TemplateSpecialization nodes; + // see collectFromNode(). + return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1; +} + +// Unfortunately, the exact position of a specific token is usually not +// recorded in the AST, so if we need that, we have to search for it textually. +// In corner cases, this might get sabotaged by e.g. comments, in which case we give up. +int ExtraHighlightingResultsCollector::posForNodeStart(const ClangdAstNode &node) const +{ + return Utils::Text::positionInText(m_doc, node.range().start().line() + 1, + node.range().start().character() + 1); +} + +int ExtraHighlightingResultsCollector::posForNodeEnd(const ClangdAstNode &node) const +{ + return Utils::Text::positionInText(m_doc, node.range().end().line() + 1, + node.range().end().character() + 1); +} + +void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &result) +{ + if (!result.isValid()) // Some nodes don't have a range. + return; + const auto it = std::lower_bound(m_results.begin(), m_results.end(), result, lessThan); + if (it == m_results.end() || *it != result) { + + // Prevent inserting expansions for function-like macros. For instance: + // #define TEST() "blubb" + // const char *s = TEST(); + // The macro name is always shorter than the expansion and starts at the same + // location, so it should occur right before the insertion position. + if (it > m_results.begin() && (it - 1)->line == result.line + && (it - 1)->column == result.column + && (it - 1)->textStyles.mainStyle == C_PREPROCESSOR) { + return; + } + + qCDebug(clangdLogHighlight) << "adding additional highlighting result" + << result.line << result.column << result.length; + m_results.insert(it, result); + return; + } + + // This is for conversion operators, whose type part is only reported as a type by clangd. + if ((it->textStyles.mainStyle == C_TYPE + || it->textStyles.mainStyle == C_PRIMITIVE_TYPE) + && !result.textStyles.mixinStyles.empty() + && result.textStyles.mixinStyles.at(0) == C_OPERATOR) { + it->textStyles.mixinStyles = result.textStyles.mixinStyles; + } +} + +void ExtraHighlightingResultsCollector::insertResult(const ClangdAstNode &node, TextStyle style) +{ + HighlightingResult result; + result.useTextSyles = true; + result.textStyles.mainStyle = style; + setResultPosFromRange(result, node.range()); + insertResult(result); + return; +} + +// For matching the "<" and ">" brackets of template declarations, specializations +// and instantiations. +void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1, int searchEnd1, + int searchStart2, int searchEnd2) +{ + const int openingAngleBracketPos = onlyIndexOf( + subViewEnd(m_docContent, searchStart1, searchEnd1), + QStringView(QStringLiteral("<"))); + if (openingAngleBracketPos == -1) + return; + const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos; + if (absOpeningAngleBracketPos > searchStart2) + searchStart2 = absOpeningAngleBracketPos + 1; + if (searchStart2 >= searchEnd2) + return; + const int closingAngleBracketPos = onlyIndexOf( + subViewEnd(m_docContent, searchStart2, searchEnd2), + QStringView(QStringLiteral(">"))); + if (closingAngleBracketPos == -1) + return; + + const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos; + if (absOpeningAngleBracketPos > absClosingAngleBracketPos) + return; + + HighlightingResult result; + result.useTextSyles = true; + result.textStyles.mainStyle = C_PUNCTUATION; + Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column); + result.length = 1; + result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen; + insertResult(result); + Utils::Text::convertPosition(m_doc, absClosingAngleBracketPos, &result.line, &result.column); + result.kind = CppEditor::SemanticHighlighter::AngleBracketClose; + insertResult(result); +} + +void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult &result, + const Range &range) +{ + if (!range.isValid()) + return; + const Position startPos = range.start(); + const Position endPos = range.end(); + result.line = startPos.line() + 1; + result.column = startPos.character() + 1; + result.length = endPos.toPositionInDocument(m_doc) - startPos.toPositionInDocument(m_doc); +} + +void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &node) +{ + if (node.kind() == "UserDefinedLiteral") + return; + if (node.kind().endsWith("Literal")) { + const bool isKeyword = node.kind() == "CXXBoolLiteral" + || node.kind() == "CXXNullPtrLiteral"; + const bool isStringLike = !isKeyword && (node.kind().startsWith("String") + || node.kind().startsWith("Character")); + const TextStyle style = isKeyword ? C_KEYWORD : isStringLike ? C_STRING : C_NUMBER; + insertResult(node, style); + return; + } + if (node.role() == "type" && node.kind() == "Builtin") { + insertResult(node, C_PRIMITIVE_TYPE); + return; + } + if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) { + insertResult(node, C_KEYWORD); + return; + } + + const bool isExpression = node.role() == "expression"; + if (isExpression && node.kind() == "Predefined") { + insertResult(node, C_PREPROCESSOR); + return; + } + + const bool isDeclaration = node.role() == "declaration"; + const int nodeStartPos = posForNodeStart(node); + const int nodeEndPos = posForNodeEnd(node); + const QList children = node.children().value_or(QList()); + + // Match question mark and colon in ternary operators. + if (isExpression && node.kind() == "ConditionalOperator") { + if (children.size() != 3) + return; + + // The question mark is between sub-expressions 1 and 2, the colon is between + // sub-expressions 2 and 3. + const int searchStartPosQuestionMark = posForNodeEnd(children.first()); + const int searchEndPosQuestionMark = posForNodeStart(children.at(1)); + QStringView content = subViewEnd(m_docContent, searchStartPosQuestionMark, + searchEndPosQuestionMark); + const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?"))); + if (questionMarkPos == -1) + return; + const int searchStartPosColon = posForNodeEnd(children.at(1)); + const int searchEndPosColon = posForNodeStart(children.at(2)); + content = subViewEnd(m_docContent, searchStartPosColon, searchEndPosColon); + const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":"))); + if (colonPos == -1) + return; + + const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos; + const int absColonPos = searchStartPosColon + colonPos; + if (absQuestionMarkPos > absColonPos) + return; + + HighlightingResult result; + result.useTextSyles = true; + result.textStyles.mainStyle = C_PUNCTUATION; + result.textStyles.mixinStyles.push_back(C_OPERATOR); + Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column); + result.length = 1; + result.kind = CppEditor::SemanticHighlighter::TernaryIf; + insertResult(result); + Utils::Text::convertPosition(m_doc, absColonPos, &result.line, &result.column); + result.kind = CppEditor::SemanticHighlighter::TernaryElse; + insertResult(result); + return; + } + + if (isDeclaration && (node.kind() == "FunctionTemplate" + || node.kind() == "ClassTemplate")) { + // The child nodes are the template parameters and and the function or class. + // The opening angle bracket is before the first child node, the closing angle + // bracket is before the function child node and after the last param node. + const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate" + ? "Function" : "CXXRecord"); + const auto functionOrClassIt = std::find_if(children.begin(), children.end(), + [&classOrFunctionKind](const ClangdAstNode &n) { + return n.role() == "declaration" && n.kind() == classOrFunctionKind; + }); + if (functionOrClassIt == children.end() || functionOrClassIt == children.begin()) + return; + const int firstTemplateParamStartPos = posForNodeStart(children.first()); + const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1)); + const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt); + insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos, + lastTemplateParamEndPos, functionOrClassStartPos); + return; + } + + const auto isTemplateParamDecl = [](const ClangdAstNode &node) { + return node.isTemplateParameterDeclaration(); + }; + if (isDeclaration && node.kind() == "TypeAliasTemplate") { + // Children are one node of type TypeAlias and the template parameters. + // The opening angle bracket is before the first parameter and the closing + // angle bracket is after the last parameter. + // The TypeAlias node seems to appear first in the AST, even though lexically + // is comes after the parameters. We don't rely on the order here. + // Note that there is a second pair of angle brackets. That one is part of + // a TemplateSpecialization, which is handled further below. + const auto firstTemplateParam = std::find_if(children.begin(), children.end(), + isTemplateParamDecl); + if (firstTemplateParam == children.end()) + return; + const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(), + isTemplateParamDecl); + QTC_ASSERT(lastTemplateParam != children.rend(), return); + const auto typeAlias = std::find_if(children.begin(), children.end(), + [](const ClangdAstNode &n) { return n.kind() == "TypeAlias"; }); + if (typeAlias == children.end()) + return; + + const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam); + const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam); + const int searchEndPos = posForNodeStart(*typeAlias); + insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos, + lastTemplateParamEndPos, searchEndPos); + return; + } + + if (isDeclaration && node.kind() == "ClassTemplateSpecialization") { + // There is one child of kind TemplateSpecialization. The first pair + // of angle brackets comes before that. + if (children.size() == 1) { + const int childNodePos = posForNodeStart(children.first()); + insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos); + } + return; + } + + if (isDeclaration && node.kind() == "TemplateTemplateParm") { + // The child nodes are template arguments and template parameters. + // Arguments seem to appear before parameters in the AST, even though they + // come after them in the source code. We don't rely on the order here. + const auto firstTemplateParam = std::find_if(children.begin(), children.end(), + isTemplateParamDecl); + if (firstTemplateParam == children.end()) + return; + const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(), + isTemplateParamDecl); + QTC_ASSERT(lastTemplateParam != children.rend(), return); + const auto templateArg = std::find_if(children.begin(), children.end(), + [](const ClangdAstNode &n) { return n.role() == "template argument"; }); + + const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam); + const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam); + const int searchEndPos = templateArg == children.end() + ? nodeEndPos : posForNodeStart(*templateArg); + insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos, + lastTemplateParamEndPos, searchEndPos); + return; + } + + // {static,dynamic,reinterpret}_cast<>(). + if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) { + // First child is type, second child is expression. + // The opening angle bracket is before the first child, the closing angle bracket + // is between the two children. + if (children.size() == 2) { + insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()), + posForNodeEnd(children.first()), + posForNodeStart(children.last())); + } + return; + } + + if (node.kind() == "TemplateSpecialization") { + // First comes the template type, then the template arguments. + // The opening angle bracket is before the first template argument, + // the closing angle bracket is after the last template argument. + // The first child node has no range, so we start searching at the parent node. + if (children.size() >= 2) { + int searchStart2 = posForNodeEnd(children.last()); + int searchEnd2 = nodeEndPos; + + // There is a weird off-by-one error on the clang side: If there is a + // nested template instantiation *and* there is no space between + // the closing angle brackets, then the inner TemplateSpecialization node's range + // will extend one character too far, covering the outer's closing angle bracket. + // This is what we are correcting for here. + // This issue is tracked at https://github.com/clangd/clangd/issues/871. + if (searchStart2 == searchEnd2) + --searchStart2; + insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)), + searchStart2, searchEnd2); + } + return; + } + + if (!isExpression && !isDeclaration) + return; + + // Operators, overloaded ones in particular. + static const QString operatorPrefix = "operator"; + QString detail = node.detail().value_or(QString()); + const bool isCallToNew = node.kind() == "CXXNew"; + const bool isCallToDelete = node.kind() == "CXXDelete"; + const auto isProperOperator = [&] { + if (isCallToNew || isCallToDelete) + return true; + if (!detail.startsWith(operatorPrefix)) + return false; + if (detail == operatorPrefix) + return false; + const QChar nextChar = detail.at(operatorPrefix.length()); + return !nextChar.isLetterOrNumber() && nextChar != '_'; + }; + if (!isProperOperator()) + return; + + if (!isCallToNew && !isCallToDelete) + detail.remove(0, operatorPrefix.length()); + + HighlightingResult result; + result.useTextSyles = true; + const bool isConversionOp = node.kind() == "CXXConversion"; + const bool isOverloaded = !isConversionOp + && (isDeclaration || ((!isCallToNew && !isCallToDelete) + || node.arcanaContains("CXXMethod"))); + result.textStyles.mainStyle = isConversionOp + ? C_PRIMITIVE_TYPE + : isCallToNew || isCallToDelete || detail.at(0).isSpace() + ? C_KEYWORD : C_PUNCTUATION; + result.textStyles.mixinStyles.push_back(C_OPERATOR); + if (isOverloaded) + result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR); + if (isDeclaration) + result.textStyles.mixinStyles.push_back(C_DECLARATION); + + const QStringView nodeText = subViewEnd(m_docContent, nodeStartPos, nodeEndPos); + + if (isCallToNew || isCallToDelete) { + result.line = node.range().start().line() + 1; + result.column = node.range().start().character() + 1; + result.length = isCallToNew ? 3 : 6; + insertResult(result); + if (node.arcanaContains("array")) { + const int openingBracketOffset = nodeText.indexOf('['); + if (openingBracketOffset == -1) + return; + const int closingBracketOffset = nodeText.lastIndexOf(']'); + if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset) + return; + + result.textStyles.mainStyle = C_PUNCTUATION; + result.length = 1; + Utils::Text::convertPosition(m_doc, + nodeStartPos + openingBracketOffset, + &result.line, &result.column); + insertResult(result); + Utils::Text::convertPosition(m_doc, + nodeStartPos + closingBracketOffset, + &result.line, &result.column); + insertResult(result); + } + return; + } + + if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) { + result.line = node.range().start().line() + 1; + result.column = node.range().start().character() + 1; + result.length = 1; + insertResult(result); + result.line = node.range().end().line() + 1; + result.column = node.range().end().character(); + insertResult(result); + return; + } + + const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length(); + + // The simple case: Call to operator+, +=, * etc. + if (nodeEndPos - nodeStartPos == opStringLen) { + setResultPosFromRange(result, node.range()); + insertResult(result); + return; + } + + const int prefixOffset = nodeText.indexOf(operatorPrefix); + if (prefixOffset == -1) + return; + + const bool isArray = detail == "[]"; + const bool isCall = detail == "()"; + const bool isArrayNew = detail == " new[]"; + const bool isArrayDelete = detail == " delete[]"; + const QStringView searchTerm = isArray || isCall + ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete + ? QStringView(detail).chopped(2) : detail; + const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset + + operatorPrefix.length()); + if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1) + return; + + const int opStringOffsetInDoc = nodeStartPos + opStringOffset + + detail.length() - opStringLen; + Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column); + result.length = opStringLen; + if (isArray || isCall) + result.length = 1; + else if (isArrayNew || isArrayDelete) + result.length -= 2; + if (!isArray && !isCall) + insertResult(result); + if (!isArray && !isCall && !isArrayNew && !isArrayDelete) + return; + + result.textStyles.mainStyle = C_PUNCTUATION; + result.length = 1; + const int openingParenOffset = nodeText.indexOf( + isCall ? '(' : '[', prefixOffset + operatorPrefix.length()); + if (openingParenOffset == -1) + return; + const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1); + if (closingParenOffset == -1 || closingParenOffset < openingParenOffset) + return; + Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset, + &result.line, &result.column); + insertResult(result); + Utils::Text::convertPosition(m_doc, nodeStartPos + closingParenOffset, + &result.line, &result.column); + insertResult(result); +} + +void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node) +{ + if (m_future.isCanceled()) + return; + const ClangdAstNode::FileStatus prevFileStatus = m_currentFileStatus; + m_currentFileStatus = node.fileStatus(m_filePath); + if (m_currentFileStatus == ClangdAstNode::FileStatus::Unknown + && prevFileStatus != ClangdAstNode::FileStatus::Ours) { + m_currentFileStatus = prevFileStatus; + } + switch (m_currentFileStatus) { + case ClangdAstNode::FileStatus::Ours: + case ClangdAstNode::FileStatus::Unknown: + collectFromNode(node); + [[fallthrough]]; + case ClangdAstNode::FileStatus::Foreign: + case ClangCodeModel::Internal::ClangdAstNode::FileStatus::Mixed: { + const auto children = node.children(); + if (!children) + return; + for (const ClangdAstNode &childNode : *children) + visitNode(childNode); + break; + } + } + m_currentFileStatus = prevFileStatus; +} + +} // namespace ClangCodeModel::Internal diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.h b/src/plugins/clangcodemodel/clangdsemantichighlighting.h new file mode 100644 index 00000000000..29a4446e9e1 --- /dev/null +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace LanguageClient { class ExpandedSemanticToken; } +namespace TextEditor { +class HighlightingResult; +class TextDocument; +} +namespace Utils { class FilePath; } + +namespace ClangCodeModel::Internal { +class ClangdAstNode; +class TaskTimer; +Q_DECLARE_LOGGING_CATEGORY(clangdLogHighlight); + +void doSemanticHighlighting( + QFutureInterface &future, + const Utils::FilePath &filePath, + const QList &tokens, + const QString &docContents, + const ClangdAstNode &ast, + const QPointer &textDocument, + int docRevision, + const QVersionNumber &clangdVersion, + const TaskTimer &taskTimer + ); + +} // namespace ClangCodeModel::Internal diff --git a/src/plugins/clangcodemodel/tasktimers.cpp b/src/plugins/clangcodemodel/tasktimers.cpp new file mode 100644 index 00000000000..e9e82d7d149 --- /dev/null +++ b/src/plugins/clangcodemodel/tasktimers.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "tasktimers.h" + +#include + +#include + +namespace ClangCodeModel::Internal { + +Q_LOGGING_CATEGORY(clangdLogTiming, "qtc.clangcodemodel.clangd.timing", QtWarningMsg); + +void ClangCodeModel::Internal::TaskTimer::stopTask() +{ + // This can happen due to the RAII mechanism employed with SubtaskTimer. + // The subtask timers will expire immediately after, so this does not distort + // the timing data. + if (m_subtasks > 0) { + QTC_CHECK(m_timer.isValid()); + m_elapsedMs += m_timer.elapsed(); + m_timer.invalidate(); + m_subtasks = 0; + } + m_started = false; + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_elapsedMs + << " ms in UI thread"; + m_elapsedMs = 0; +} + +void TaskTimer::startSubtask() +{ + // We have some callbacks that are either synchronous or asynchronous, depending on + // dynamic conditions. In the sync case, we will have nested subtasks, in which case + // the inner ones must not collect timing data, as their code blocks are already covered. + if (++m_subtasks > 1) + return; + if (!m_started) { + QTC_ASSERT(m_elapsedMs == 0, m_elapsedMs = 0); + m_started = true; + m_finalized = false; + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting"; + + // Used by ThreadedSubtaskTimer to mark the end of the whole highlighting operation + m_startTimer.restart(); + } + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask started at " + << QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz"); + QTC_CHECK(!m_timer.isValid()); + m_timer.start(); +} + +void TaskTimer::stopSubtask(bool isFinalizing) +{ + if (m_subtasks == 0) // See stopTask(). + return; + if (isFinalizing) + m_finalized = true; + if (--m_subtasks > 0) // See startSubtask(). + return; + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask stopped at " + << QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz"); + QTC_CHECK(m_timer.isValid()); + m_elapsedMs += m_timer.elapsed(); + m_timer.invalidate(); + if (m_finalized) + stopTask(); +} + +ThreadedSubtaskTimer::ThreadedSubtaskTimer(const QString &task, const TaskTimer &taskTimer) + : m_task(task), m_taskTimer(taskTimer) +{ + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting thread"; + m_timer.start(); +} + +ThreadedSubtaskTimer::~ThreadedSubtaskTimer() +{ + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_timer.elapsed() + << " ms in dedicated thread"; + + qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": Start to end: " + << m_taskTimer.startTimer().elapsed() << " ms"; +} + +} // namespace ClangCodeModel::Internal diff --git a/src/plugins/clangcodemodel/tasktimers.h b/src/plugins/clangcodemodel/tasktimers.h new file mode 100644 index 00000000000..8995700d79b --- /dev/null +++ b/src/plugins/clangcodemodel/tasktimers.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace ClangCodeModel::Internal { + +Q_DECLARE_LOGGING_CATEGORY(clangdLogTiming); + +// TODO: Generalize, document interface and move to Utils? +class TaskTimer +{ +public: + TaskTimer(const QString &task) : m_task(task) {} + + void stopTask(); + void startSubtask(); + void stopSubtask(bool isFinalizing); + + QElapsedTimer startTimer() const { return m_startTimer; } + +private: + const QString m_task; + QElapsedTimer m_timer; + QElapsedTimer m_startTimer; + qint64 m_elapsedMs = 0; + int m_subtasks = 0; + bool m_started = false; + bool m_finalized = false; +}; + +class SubtaskTimer +{ +public: + SubtaskTimer(TaskTimer &timer) : m_timer(timer) { m_timer.startSubtask(); } + ~SubtaskTimer() { m_timer.stopSubtask(m_isFinalizing); } + +protected: + void makeFinalizing() { m_isFinalizing = true; } + +private: + TaskTimer &m_timer; + bool m_isFinalizing = false; +}; + +class FinalizingSubtaskTimer : public SubtaskTimer +{ +public: + FinalizingSubtaskTimer(TaskTimer &timer) : SubtaskTimer(timer) { makeFinalizing(); } +}; + +class ThreadedSubtaskTimer +{ +public: + ThreadedSubtaskTimer(const QString &task, const TaskTimer &taskTimer); + ~ThreadedSubtaskTimer(); + +private: + const QString m_task; + QElapsedTimer m_timer; + const TaskTimer &m_taskTimer; +}; + +} // namespace ClangCodeModel::Internal From b1e73ca45dd1dd9ce28227b8c986fdfcee892ee2 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 30 May 2022 15:35:13 +0200 Subject: [PATCH 52/58] ClangCodeModel: Try harder to catch bogus AST ranges String literals that are adjacent after preprocessing are reported as a single AST node, leading to potentially wrong highlighting. We can catch this condition at least for the case where there are semantic tokens in between the string components. Fixes: QTCREATORBUG-27601 Change-Id: If023d06db74bd6cfefa670649f0e733ceaaede2d Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: David Schulz --- .../clangcodemodel/clangdsemantichighlighting.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp index e3c6f08bdb9..b37605af128 100644 --- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp @@ -482,6 +482,15 @@ void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &r return; } + // Bogus ranges; e.g. QTCREATORBUG-27601 + if (it != m_results.end()) { + const int nextStartPos = Utils::Text::positionInText(m_doc, it->line, it->column); + const int resultEndPos = Utils::Text::positionInText(m_doc, result.line, result.column) + + result.length; + if (resultEndPos > nextStartPos) + return; + } + qCDebug(clangdLogHighlight) << "adding additional highlighting result" << result.line << result.column << result.length; m_results.insert(it, result); From abd289190e820c1d64cdcb078ac7a5c47be7e422 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 31 May 2022 12:59:27 +0200 Subject: [PATCH 53/58] Valgrind: Inline CallGrindController into CallgrindToolRunner No functional changes. Change-Id: I3b73da69e20e4d0324c582544c67a84eac8dbdab Reviewed-by: Jarek Kobus --- src/plugins/valgrind/CMakeLists.txt | 1 - .../callgrind/callgrindcontroller.cpp | 193 ------------------ .../valgrind/callgrind/callgrindcontroller.h | 87 -------- src/plugins/valgrind/callgrindengine.cpp | 188 ++++++++++++++--- src/plugins/valgrind/callgrindengine.h | 47 ++++- src/plugins/valgrind/valgrind.qbs | 1 - tests/auto/valgrind/callgrind/CMakeLists.txt | 1 - 7 files changed, 204 insertions(+), 314 deletions(-) delete mode 100644 src/plugins/valgrind/callgrind/callgrindcontroller.cpp delete mode 100644 src/plugins/valgrind/callgrind/callgrindcontroller.h diff --git a/src/plugins/valgrind/CMakeLists.txt b/src/plugins/valgrind/CMakeLists.txt index 9896c93a3a8..192304e5795 100644 --- a/src/plugins/valgrind/CMakeLists.txt +++ b/src/plugins/valgrind/CMakeLists.txt @@ -4,7 +4,6 @@ add_qtc_plugin(Valgrind SOURCES callgrind/callgrindabstractmodel.h callgrind/callgrindcallmodel.cpp callgrind/callgrindcallmodel.h - callgrind/callgrindcontroller.cpp callgrind/callgrindcontroller.h callgrind/callgrindcostitem.cpp callgrind/callgrindcostitem.h callgrind/callgrindcycledetection.cpp callgrind/callgrindcycledetection.h callgrind/callgrinddatamodel.cpp callgrind/callgrinddatamodel.h diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp deleted file mode 100644 index 3d9420f969a..00000000000 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "callgrindcontroller.h" - -#include -#include - -#include - -#define CALLGRIND_CONTROL_DEBUG 0 - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Valgrind { -namespace Callgrind { - -const char CALLGRIND_CONTROL_BINARY[] = "callgrind_control"; - -CallgrindController::CallgrindController() = default; - -CallgrindController::~CallgrindController() -{ - cleanupTempFile(); -} - -static QString toOptionString(CallgrindController::Option option) -{ - /* callgrind_control help from v3.9.0 - - Options: - -h --help Show this help text - --version Show version - -s --stat Show statistics - -b --back Show stack/back trace - -e [,...] Show event counters for ,... (default: all) - --dump[=] Request a dump optionally using as description - -z --zero Zero all event counters - -k --kill Kill - --instr= Switch instrumentation state on/off - */ - - switch (option) { - case CallgrindController::Dump: - return QLatin1String("--dump"); - case CallgrindController::ResetEventCounters: - return QLatin1String("--zero"); - case CallgrindController::Pause: - return QLatin1String("--instr=off"); - case CallgrindController::UnPause: - return QLatin1String("--instr=on"); - default: - return QString(); // never reached - } -} - -void CallgrindController::run(Option option) -{ - if (m_controllerProcess) { - emit statusMessage(tr("Previous command has not yet finished.")); - return; - } - - // save back current running operation - m_lastOption = option; - - m_controllerProcess.reset(new QtcProcess); - - switch (option) { - case CallgrindController::Dump: - emit statusMessage(tr("Dumping profile data...")); - break; - case CallgrindController::ResetEventCounters: - emit statusMessage(tr("Resetting event counters...")); - break; - case CallgrindController::Pause: - emit statusMessage(tr("Pausing instrumentation...")); - break; - case CallgrindController::UnPause: - emit statusMessage(tr("Unpausing instrumentation...")); - break; - default: - break; - } - -#if CALLGRIND_CONTROL_DEBUG - m_controllerProcess->setProcessChannelMode(QProcess::ForwardedChannels); -#endif - connect(m_controllerProcess.get(), &QtcProcess::finished, - this, &CallgrindController::controllerProcessDone); - - const FilePath control = - FilePath(CALLGRIND_CONTROL_BINARY).onDevice(m_valgrindRunnable.command.executable()); - m_controllerProcess->setCommand({control, {toOptionString(option), QString::number(m_pid)}}); - m_controllerProcess->setWorkingDirectory(m_valgrindRunnable.workingDirectory); - m_controllerProcess->setEnvironment(m_valgrindRunnable.environment); - m_controllerProcess->start(); -} - -void CallgrindController::setValgrindPid(qint64 pid) -{ - m_pid = pid; -} - -void CallgrindController::controllerProcessDone() -{ - const QString error = m_controllerProcess->errorString(); - const ProcessResult result = m_controllerProcess->result(); - - m_controllerProcess.release()->deleteLater(); - - if (result != ProcessResult::FinishedWithSuccess) { - emit statusMessage(tr("An error occurred while trying to run %1: %2").arg(CALLGRIND_CONTROL_BINARY).arg(error)); - qWarning() << "Controller exited abnormally:" << error; - return; - } - - // this call went fine, we might run another task after this - switch (m_lastOption) { - case ResetEventCounters: - // lets dump the new reset profiling info - run(Dump); - return; - case Pause: - break; - case Dump: - emit statusMessage(tr("Callgrind dumped profiling info")); - break; - case UnPause: - emit statusMessage(tr("Callgrind unpaused.")); - break; - default: - break; - } - - emit finished(m_lastOption); - m_lastOption = Unknown; -} - -void CallgrindController::getLocalDataFile() -{ - cleanupTempFile(); - { - TemporaryFile dataFile("callgrind.out"); - dataFile.open(); - m_hostOutputFile = FilePath::fromString(dataFile.fileName()); - } - - const auto afterCopy = [this](bool res) { - QTC_CHECK(res); - emit localParseDataAvailable(m_hostOutputFile); - }; - m_valgrindOutputFile.asyncCopyFile(afterCopy, m_hostOutputFile); -} - -void CallgrindController::cleanupTempFile() -{ - if (!m_hostOutputFile.isEmpty() && m_hostOutputFile.exists()) - m_hostOutputFile.removeFile(); - - m_hostOutputFile.clear(); -} - -void CallgrindController::setValgrindRunnable(const Runnable &runnable) -{ - m_valgrindRunnable = runnable; -} - -} // namespace Callgrind -} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.h b/src/plugins/valgrind/callgrind/callgrindcontroller.h deleted file mode 100644 index d5c273dc951..00000000000 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include - -#include - -namespace Utils { class QtcProcess; } - -namespace Valgrind { -namespace Callgrind { - -class CallgrindController : public QObject -{ - Q_OBJECT -public: - enum Option { - Unknown, - Dump, - ResetEventCounters, - Pause, - UnPause - }; - Q_ENUM(Option) - - CallgrindController(); - ~CallgrindController() override; - - void run(Option option); - - /** - * Make data file available locally, triggers @c localParseDataAvailable. - * - * If the valgrind process was run remotely, this transparently - * downloads the data file first and returns a local path. - */ - void getLocalDataFile(); - void setValgrindPid(qint64 pid); - void setValgrindRunnable(const ProjectExplorer::Runnable &runnable); - void setValgrindOutputFile(const Utils::FilePath &output) { m_valgrindOutputFile = output; } - -signals: - void finished(Valgrind::Callgrind::CallgrindController::Option option); - void localParseDataAvailable(const Utils::FilePath &file); - void statusMessage(const QString &msg); - -private: - void cleanupTempFile(); - void controllerProcessDone(); - - std::unique_ptr m_controllerProcess; - ProjectExplorer::Runnable m_valgrindRunnable; - qint64 m_pid = 0; - - Option m_lastOption = Unknown; - - // remote callgrind support - Utils::FilePath m_valgrindOutputFile; // On the device that runs valgrind - Utils::FilePath m_hostOutputFile; // On the device that runs creator -}; - -} // namespace Callgrind -} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp index 333d713e15b..c7660468d54 100644 --- a/src/plugins/valgrind/callgrindengine.cpp +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -27,7 +27,6 @@ #include "valgrindsettings.h" -#include #include #include @@ -36,6 +35,11 @@ #include #include #include +#include + +#include + +#define CALLGRIND_CONTROL_DEBUG 0 using namespace ProjectExplorer; using namespace Valgrind::Callgrind; @@ -44,6 +48,8 @@ using namespace Utils; namespace Valgrind { namespace Internal { +const char CALLGRIND_CONTROL_BINARY[] = "callgrind_control"; + void setupCallgrindRunner(CallgrindToolRunner *); CallgrindToolRunner::CallgrindToolRunner(RunControl *runControl) @@ -56,29 +62,27 @@ CallgrindToolRunner::CallgrindToolRunner(RunControl *runControl) connect(&m_parser, &Callgrind::Parser::parserDataReady, this, &CallgrindToolRunner::slotFinished); - connect(&m_controller, &CallgrindController::finished, - this, &CallgrindToolRunner::controllerFinished); - connect(&m_controller, &CallgrindController::localParseDataAvailable, - this, &CallgrindToolRunner::handleLocalParseData); - connect(&m_controller, &CallgrindController::statusMessage, - this, &CallgrindToolRunner::showStatusMessage); - connect(&m_runner, &ValgrindRunner::valgrindStarted, - &m_controller, &CallgrindController::setValgrindPid); + this, &CallgrindToolRunner::setValgrindPid); connect(&m_runner, &ValgrindRunner::extraProcessFinished, this, [this] { triggerParse(); }); - m_controller.setValgrindRunnable(runControl->runnable()); + setValgrindRunnable(runControl->runnable()); static int fileCount = 100; m_valgrindOutputFile = runControl->workingDirectory() / QString("callgrind.out.f%1").arg(++fileCount); - m_controller.setValgrindOutputFile(m_valgrindOutputFile); + setValgrindOutputFile(m_valgrindOutputFile); setupCallgrindRunner(this); } +CallgrindToolRunner::~CallgrindToolRunner() +{ + cleanupTempFile(); +} + QStringList CallgrindToolRunner::toolArguments() const { QStringList arguments = {"--tool=callgrind"}; @@ -123,7 +127,7 @@ void CallgrindToolRunner::start() void CallgrindToolRunner::dump() { - m_controller.run(CallgrindController::Dump); + run(Dump); } void CallgrindToolRunner::setPaused(bool paused) @@ -150,17 +154,17 @@ void CallgrindToolRunner::setToggleCollectFunction(const QString &toggleCollectF void CallgrindToolRunner::reset() { - m_controller.run(Callgrind::CallgrindController::ResetEventCounters); + run(ResetEventCounters); } void CallgrindToolRunner::pause() { - m_controller.run(Callgrind::CallgrindController::Pause); + run(Pause); } void CallgrindToolRunner::unpause() { - m_controller.run(Callgrind::CallgrindController::UnPause); + run(UnPause); } Callgrind::ParseData *CallgrindToolRunner::takeParserData() @@ -180,32 +184,166 @@ void CallgrindToolRunner::showStatusMessage(const QString &message) void CallgrindToolRunner::triggerParse() { - m_controller.getLocalDataFile(); + getLocalDataFile(); } -void CallgrindToolRunner::handleLocalParseData(const FilePath &outputFile) + +static QString toOptionString(CallgrindToolRunner::Option option) { - QTC_ASSERT(outputFile.exists(), return); - showStatusMessage(tr("Parsing Profile Data...")); - m_parser.parse(outputFile); + /* callgrind_control help from v3.9.0 + + Options: + -h --help Show this help text + --version Show version + -s --stat Show statistics + -b --back Show stack/back trace + -e [,...] Show event counters for ,... (default: all) + --dump[=] Request a dump optionally using as description + -z --zero Zero all event counters + -k --kill Kill + --instr= Switch instrumentation state on/off + */ + + switch (option) { + case CallgrindToolRunner::Dump: + return QLatin1String("--dump"); + case CallgrindToolRunner::ResetEventCounters: + return QLatin1String("--zero"); + case CallgrindToolRunner::Pause: + return QLatin1String("--instr=off"); + case CallgrindToolRunner::UnPause: + return QLatin1String("--instr=on"); + default: + return QString(); // never reached + } } -void CallgrindToolRunner::controllerFinished(CallgrindController::Option option) +void CallgrindToolRunner::run(Option option) { - switch (option) + if (m_controllerProcess) { + showStatusMessage(tr("Previous command has not yet finished.")); + return; + } + + // save back current running operation + m_lastOption = option; + + m_controllerProcess.reset(new QtcProcess); + + switch (option) { + case CallgrindToolRunner::Dump: + showStatusMessage(tr("Dumping profile data...")); + break; + case CallgrindToolRunner::ResetEventCounters: + showStatusMessage(tr("Resetting event counters...")); + break; + case CallgrindToolRunner::Pause: + showStatusMessage(tr("Pausing instrumentation...")); + break; + case CallgrindToolRunner::UnPause: + showStatusMessage(tr("Unpausing instrumentation...")); + break; + default: + break; + } + +#if CALLGRIND_CONTROL_DEBUG + m_controllerProcess->setProcessChannelMode(QProcess::ForwardedChannels); +#endif + connect(m_controllerProcess.get(), &QtcProcess::finished, + this, &CallgrindToolRunner::controllerProcessDone); + + const FilePath control = + FilePath(CALLGRIND_CONTROL_BINARY).onDevice(m_valgrindRunnable.command.executable()); + m_controllerProcess->setCommand({control, {toOptionString(option), QString::number(m_pid)}}); + m_controllerProcess->setWorkingDirectory(m_valgrindRunnable.workingDirectory); + m_controllerProcess->setEnvironment(m_valgrindRunnable.environment); + m_controllerProcess->start(); +} + +void CallgrindToolRunner::setValgrindPid(qint64 pid) +{ + m_pid = pid; +} + +void CallgrindToolRunner::controllerProcessDone() +{ + const QString error = m_controllerProcess->errorString(); + const ProcessResult result = m_controllerProcess->result(); + + m_controllerProcess.release()->deleteLater(); + + if (result != ProcessResult::FinishedWithSuccess) { + showStatusMessage(tr("An error occurred while trying to run %1: %2").arg(CALLGRIND_CONTROL_BINARY).arg(error)); + qWarning() << "Controller exited abnormally:" << error; + return; + } + + // this call went fine, we might run another task after this + switch (m_lastOption) { + case ResetEventCounters: + // lets dump the new reset profiling info + run(Dump); + return; + case Pause: + break; + case Dump: + showStatusMessage(tr("Callgrind dumped profiling info")); + break; + case UnPause: + showStatusMessage(tr("Callgrind unpaused.")); + break; + default: + break; + } + + switch (m_lastOption) { - case CallgrindController::Pause: + case Pause: m_paused = true; break; - case CallgrindController::UnPause: + case UnPause: m_paused = false; break; - case CallgrindController::Dump: + case Dump: triggerParse(); break; default: break; // do nothing } + + m_lastOption = Unknown; +} + +void CallgrindToolRunner::getLocalDataFile() +{ + cleanupTempFile(); + { + TemporaryFile dataFile("callgrind.out"); + dataFile.open(); + m_hostOutputFile = FilePath::fromString(dataFile.fileName()); + } + + const auto afterCopy = [this](bool res) { + QTC_CHECK(res); + QTC_ASSERT(m_hostOutputFile.exists(), return); + showStatusMessage(tr("Parsing Profile Data...")); + m_parser.parse(m_hostOutputFile); + }; + m_valgrindOutputFile.asyncCopyFile(afterCopy, m_hostOutputFile); +} + +void CallgrindToolRunner::cleanupTempFile() +{ + if (!m_hostOutputFile.isEmpty() && m_hostOutputFile.exists()) + m_hostOutputFile.removeFile(); + + m_hostOutputFile.clear(); +} + +void CallgrindToolRunner::setValgrindRunnable(const Runnable &runnable) +{ + m_valgrindRunnable = runnable; } } // Internal diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h index dfee79d95b2..d6357417cab 100644 --- a/src/plugins/valgrind/callgrindengine.h +++ b/src/plugins/valgrind/callgrindengine.h @@ -26,11 +26,11 @@ #pragma once #include "valgrindengine.h" -#include "valgrindrunner.h" #include "callgrind/callgrindparsedata.h" #include "callgrind/callgrindparser.h" -#include "callgrind/callgrindcontroller.h" + +#include namespace Valgrind { namespace Internal { @@ -41,6 +41,7 @@ class CallgrindToolRunner : public ValgrindToolRunner public: explicit CallgrindToolRunner(ProjectExplorer::RunControl *runControl); + ~CallgrindToolRunner() override; void start() override; @@ -58,6 +59,16 @@ public: void setToggleCollectFunction(const QString &toggleCollectFunction); + enum Option { + Unknown, + Dump, + ResetEventCounters, + Pause, + UnPause + }; + + Q_ENUM(Option) + protected: QStringList toolArguments() const override; QString progressTitle() const override; @@ -70,12 +81,36 @@ private: void showStatusMessage(const QString &message); void triggerParse(); - void handleLocalParseData(const Utils::FilePath &filePath); - void controllerFinished(Callgrind::CallgrindController::Option option); + void controllerFinished(Option option); + + void run(Option option); + + /** + * Make data file available locally, triggers @c localParseDataAvailable. + * + * If the valgrind process was run remotely, this transparently + * downloads the data file first and returns a local path. + */ + void getLocalDataFile(); + void setValgrindPid(qint64 pid); + void setValgrindRunnable(const ProjectExplorer::Runnable &runnable); + void setValgrindOutputFile(const Utils::FilePath &output) { m_valgrindOutputFile = output; } + + void cleanupTempFile(); + void controllerProcessDone(); bool m_markAsPaused = false; - Utils::FilePath m_valgrindOutputFile; - Callgrind::CallgrindController m_controller; + + std::unique_ptr m_controllerProcess; + ProjectExplorer::Runnable m_valgrindRunnable; + qint64 m_pid = 0; + + Option m_lastOption = Unknown; + + // remote callgrind support + Utils::FilePath m_valgrindOutputFile; // On the device that runs valgrind + Utils::FilePath m_hostOutputFile; // On the device that runs creator + Callgrind::Parser m_parser; bool m_paused = false; diff --git a/src/plugins/valgrind/valgrind.qbs b/src/plugins/valgrind/valgrind.qbs index bee21fdf738..8830ba2b8b3 100644 --- a/src/plugins/valgrind/valgrind.qbs +++ b/src/plugins/valgrind/valgrind.qbs @@ -44,7 +44,6 @@ QtcPlugin { files: [ "callgrindabstractmodel.h", "callgrindcallmodel.cpp", "callgrindcallmodel.h", - "callgrindcontroller.cpp", "callgrindcontroller.h", "callgrindcostitem.cpp", "callgrindcostitem.h", "callgrindcycledetection.cpp", "callgrindcycledetection.h", "callgrinddatamodel.cpp", "callgrinddatamodel.h", diff --git a/tests/auto/valgrind/callgrind/CMakeLists.txt b/tests/auto/valgrind/callgrind/CMakeLists.txt index 72df53a9d40..76859938aed 100644 --- a/tests/auto/valgrind/callgrind/CMakeLists.txt +++ b/tests/auto/valgrind/callgrind/CMakeLists.txt @@ -11,7 +11,6 @@ extend_qtc_test(tst_callgrindparsertests SOURCES_PREFIX "${PROJECT_SOURCE_DIR}/src/plugins/valgrind/" SOURCES callgrind/callgrindcallmodel.h callgrind/callgrindcallmodel.cpp - callgrind/callgrindcontroller.h callgrind/callgrindcontroller.cpp callgrind/callgrindcostitem.h callgrind/callgrindcostitem.cpp callgrind/callgrindcycledetection.h callgrind/callgrindcycledetection.cpp callgrind/callgrinddatamodel.h callgrind/callgrinddatamodel.cpp From 383ea8a3617f9de3f31d66096160e0e729d8c5e4 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Fri, 27 May 2022 23:05:50 -0400 Subject: [PATCH 54/58] Docker: Fix bug with cmake warnings for compiler path with docker kits Task-number: QTCREATORBUG-26857 Change-Id: I6400008d489167e43f0c34d95841140697dd62de Reviewed-by: Reviewed-by: hjk --- src/plugins/cmakeprojectmanager/cmakekitinformation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 05160ff152b..6c39ddef419 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -1219,7 +1219,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const if (!tcC || !tcC->isValid()) { addWarning(tr("CMake configuration has a path to a C compiler set, " "even though the kit has no valid tool chain.")); - } else if (tcCPath != tcC->compilerCommand()) { + } else if (tcCPath != tcC->compilerCommand() && tcCPath != tcC->compilerCommand().onDevice(tcCPath)) { addWarning(tr("CMake configuration has a path to a C compiler set " "that does not match the compiler path " "configured in the tool chain of the kit.")); @@ -1235,7 +1235,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const if (!tcCxx || !tcCxx->isValid()) { addWarning(tr("CMake configuration has a path to a C++ compiler set, " "even though the kit has no valid tool chain.")); - } else if (tcCxxPath != tcCxx->compilerCommand()) { + } else if (tcCxxPath != tcCxx->compilerCommand() && tcCxxPath != tcCxx->compilerCommand().onDevice(tcCxxPath)) { addWarning(tr("CMake configuration has a path to a C++ compiler set " "that does not match the compiler path " "configured in the tool chain of the kit.")); From 79236605fdd7829afb350695ce6fc9e24472f8a8 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 31 May 2022 18:33:41 +0200 Subject: [PATCH 55/58] RemoteLinux: Fix X11ForwardingAspect's data extraction structure Amends d325f56cc1c. Change-Id: I5d21e04037edd0677ee3cc8265f1d8706849f832 Reviewed-by: Jarek Kobus --- src/plugins/remotelinux/remotelinuxx11forwardingaspect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h b/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h index 2f7fa402d74..5e61ec36c30 100644 --- a/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h +++ b/src/plugins/remotelinux/remotelinuxx11forwardingaspect.h @@ -40,7 +40,7 @@ class REMOTELINUX_EXPORT X11ForwardingAspect : public Utils::StringAspect public: X11ForwardingAspect(const Utils::MacroExpander *macroExpander); - struct Data : BaseAspect::Data + struct Data : StringAspect::Data { QString display; }; From 72aa77ced7a0ac9bc9a9c6953ab31508f6b5e186 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 11 May 2022 13:26:57 +0200 Subject: [PATCH 56/58] QtSupport: Add support to register Qt versions via qtpaths qmake is a "build tool", and it is also a "query tool" when called with parameter "-query". Qt Creator, so far, assumes that building and querying with a Qt installation are done with one and the same tool: qmake. This change adds the ability to register a Qt version vie either qmake or qtpaths and still build with qmake, if that is installed (which is not anymore mandatory from Qt 6 on). 1) Distinguish between Qt query tool and qmake build tool: Add QtVersion::queryToolFilePath() to the existing QtVersion::qmakeFilePath(), and use queryToolFilePath in most "query" related code, and qmakeFilePath when building with qmake (e.g. in QmakeProjectManager). Also, a couple of functions and variables were renamed from *qmake* to *queryTool* in order to express that the affected code is about querying/managing Qt versions rather than about building with qmake. 2) Support manual Qt Version adding by qtpaths via file dialog This change adds qtpaths to the "Add" Qt Version file picker filter. After selection, "qtpaths -query" is executed for testing purposes. If that fails, (e.g. because it is an older Qt version), qmake is instead chosen, silently. Task-number: QTCREATORBUG-22175 Task-number: QTCREATORBUG-25546 Change-Id: I4d9c1e7eec7d5ae7c5a8d2e1a1ed95addff69966 Reviewed-by: Reviewed-by: hjk Reviewed-by: Qt CI Bot --- share/qtcreator/translations/qtcreator_cs.ts | 8 +- share/qtcreator/translations/qtcreator_da.ts | 8 +- share/qtcreator/translations/qtcreator_de.ts | 8 +- share/qtcreator/translations/qtcreator_fr.ts | 8 +- share/qtcreator/translations/qtcreator_hr.ts | 4 +- share/qtcreator/translations/qtcreator_ja.ts | 8 +- share/qtcreator/translations/qtcreator_pl.ts | 8 +- share/qtcreator/translations/qtcreator_ru.ts | 8 +- share/qtcreator/translations/qtcreator_sl.ts | 8 +- share/qtcreator/translations/qtcreator_uk.ts | 8 +- .../qtcreator/translations/qtcreator_zh_CN.ts | 8 +- .../qtcreator/translations/qtcreator_zh_TW.ts | 8 +- src/libs/utils/buildablehelperlibrary.cpp | 192 +++++++++--------- src/libs/utils/buildablehelperlibrary.h | 11 +- .../cmakekitinformation.cpp | 2 +- src/plugins/docker/kitdetector.cpp | 10 +- .../qbsprojectmanager/qbsprofilemanager.cpp | 2 +- .../qmakeprojectmanager/qmakeproject.cpp | 3 + src/plugins/qmakeprojectmanager/qmakestep.cpp | 8 +- .../designercore/instances/puppetcreator.cpp | 2 +- src/plugins/qtsupport/baseqtversion.cpp | 144 +++++++------ src/plugins/qtsupport/baseqtversion.h | 2 + src/plugins/qtsupport/qtkitinformation.cpp | 5 + src/plugins/qtsupport/qtoptionspage.cpp | 71 ++++--- src/plugins/qtsupport/qtprojectimporter.cpp | 10 +- src/plugins/qtsupport/qtversionfactory.h | 2 +- src/plugins/qtsupport/qtversioninfo.ui | 4 +- src/plugins/qtsupport/qtversionmanager.cpp | 45 ++-- 28 files changed, 338 insertions(+), 267 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_cs.ts b/share/qtcreator/translations/qtcreator_cs.ts index b7e0efc99e3..086b2dfea0b 100644 --- a/share/qtcreator/translations/qtcreator_cs.ts +++ b/share/qtcreator/translations/qtcreator_cs.ts @@ -32121,16 +32121,16 @@ Spustil jste Qemu? Qt %1 (%2) - qmake does not exist or is not executable - Soubor qmake neexistuje nebo není spustitelný + %1 does not exist or is not executable + Soubor %1 neexistuje nebo není spustitelný Qt version is not properly installed, please run make install Verze Qt není správně nainstalována. Proveďte, prosím, příkaz "make install" - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Cestu ke spustitelným souborům instalace Qt se nepodařilo určit. Možná je cesta k qmake chybná? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Cestu ke spustitelným souborům instalace Qt se nepodařilo určit. Možná je cesta k %1 chybná? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index 4cfd3841b60..21dd2fad82d 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -35382,16 +35382,16 @@ For flere detaljer, se /etc/sysctl.d/10-ptrace.conf Ingen qmake-sti sat - qmake does not exist or is not executable - qmake findes ikke eller er ikke eksekverbar + %1 does not exist or is not executable + %1 findes ikke eller er ikke eksekverbar Qt version is not properly installed, please run make install Qt version er ikke ordentligt installeret, kør venligst make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Kunne ikke beslutte stien til binærene af Qt installationen, måske er qmake-stien forkert? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Kunne ikke beslutte stien til binærene af Qt installationen, måske er %1-stien forkert? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 8013bb9b20e..49d5e966da9 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -10113,8 +10113,8 @@ Dies ist unabhängig vom Wert der Eigenschaft "visible" in QML.Es ist keine qmake-Pfad gesetzt - qmake does not exist or is not executable - Die qmake-Datei existiert nicht oder ist nicht ausführbar + %1 does not exist or is not executable + Die %1-Datei existiert nicht oder ist nicht ausführbar Qt version has no name @@ -10141,8 +10141,8 @@ Dies ist unabhängig vom Wert der Eigenschaft "visible" in QML.Die Qt-Version ist nicht richtig installiert, führen Sie bitte make install aus - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Der Pfad zu den ausführbaren Dateien der Qt-Installation konnte nicht bestimmt werden, möglicherweise ist der Pfad zu qmake falsch? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Der Pfad zu den ausführbaren Dateien der Qt-Installation konnte nicht bestimmt werden, möglicherweise ist der Pfad zu %1 falsch? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index ec42c365792..2e632c21390 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -33604,8 +33604,8 @@ Nous allons essayer de travailler avec cela mais vous pourrez rencontrer des pro Chemin de qmake non spécifié - qmake does not exist or is not executable - qmake n'existe pas ou n'est pas exécutable + %1 does not exist or is not executable + %1 n'existe pas ou n'est pas exécutable Qt version has no name @@ -33633,8 +33633,8 @@ Nous allons essayer de travailler avec cela mais vous pourrez rencontrer des pro La version de Qt n'est pas correctement installée, veuillez exécuter make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Impossible de déterminer le chemin vers les programmes de Qt, peut-être que le chemin vers qmake est faux ? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Impossible de déterminer le chemin vers les programmes de Qt, peut-être que le chemin vers %1 est faux ? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index e7a14f06a9c..c60ce6f3c59 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -41628,7 +41628,7 @@ Saving failed. - qmake does not exist or is not executable + %1 does not exist or is not executable @@ -41636,7 +41636,7 @@ Saving failed. - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts index c65ccab4138..017e3ebc611 100644 --- a/share/qtcreator/translations/qtcreator_ja.ts +++ b/share/qtcreator/translations/qtcreator_ja.ts @@ -26108,16 +26108,16 @@ Do you want to save the data first? qmake のパスが設定されていません - qmake does not exist or is not executable - qmake が存在しないか実行可能ではありません + %1 does not exist or is not executable + %1 が存在しないか実行可能ではありません Qt version is not properly installed, please run make install Qt バージョンが正しくインストールされていません。make install を実行してください - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Qt インストール先のパスが特定できませんでした。qmake のパスが間違っていませんか? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Qt インストール先のパスが特定できませんでした。%1 のパスが間違っていませんか? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts index 4bd2dfddc49..288c0333976 100644 --- a/share/qtcreator/translations/qtcreator_pl.ts +++ b/share/qtcreator/translations/qtcreator_pl.ts @@ -12382,8 +12382,8 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM Nie ustawiono ścieżki do qmake - qmake does not exist or is not executable - Brak qmake lub nie jest on plikiem wykonywalnym + %1 does not exist or is not executable + Brak %1 lub nie jest on plikiem wykonywalnym Qt version has no name @@ -12410,8 +12410,8 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM Wersja Qt zainstalowana niepoprawnie, uruchom komendę: make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Nie można określić ścieżki do plików binarnych instalacji Qt. Sprawdź ścieżkę do qmake. + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Nie można określić ścieżki do plików binarnych instalacji Qt. Sprawdź ścieżkę do %1. The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index ca1e07984e3..038c8b7d562 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -45319,8 +45319,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Путь к qmake не указан - qmake does not exist or is not executable - qmake отсутствует или не запускается + %1 does not exist or is not executable + %1 отсутствует или не запускается Qt version has no name @@ -45347,8 +45347,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Профиль Qt не установлен, пожалуйста выполните make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Не удалось определить путь к утилитам Qt. Может путь к qmake неверен? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Не удалось определить путь к утилитам Qt. Может путь к %1 неверен? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_sl.ts b/share/qtcreator/translations/qtcreator_sl.ts index ca8f7d46bb3..ed9bf70dfb4 100644 --- a/share/qtcreator/translations/qtcreator_sl.ts +++ b/share/qtcreator/translations/qtcreator_sl.ts @@ -21803,8 +21803,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi. - qmake does not exist or is not executable - Datoteka »qmake« ne obstaja ali pa ni izvršljiva + %1 does not exist or is not executable + Datoteka »%1« ne obstaja ali pa ni izvršljiva @@ -21813,8 +21813,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi. - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Ni bilo moč ugotoviti poti do programov namestitve Qt. Morda je pot do qmake napačna? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Ni bilo moč ugotoviti poti do programov namestitve Qt. Morda je pot do %1 napačna? diff --git a/share/qtcreator/translations/qtcreator_uk.ts b/share/qtcreator/translations/qtcreator_uk.ts index 7ed2f44691f..b3c68dd579c 100644 --- a/share/qtcreator/translations/qtcreator_uk.ts +++ b/share/qtcreator/translations/qtcreator_uk.ts @@ -22711,8 +22711,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Шлях до qmake не задано - qmake does not exist or is not executable - qmake не існує або не є виконуваним модулем + %1 does not exist or is not executable + %1 не існує або не є виконуваним модулем ABI detection failed: Make sure to use a matching compiler when building. @@ -22763,8 +22763,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Версія Qt не встановлена як слід, будь ласка, запустіть make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Не вдалось визначити шлях до виконуваних модулів встановлення Qt, можливо, шлях до qmake помилковий? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Не вдалось визначити шлях до виконуваних модулів встановлення Qt, можливо, шлях до %1 помилковий? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts index 8e6d46bd95c..64fa094ec3c 100644 --- a/share/qtcreator/translations/qtcreator_zh_CN.ts +++ b/share/qtcreator/translations/qtcreator_zh_CN.ts @@ -27948,8 +27948,8 @@ Did you start Qemu? 没有设置qmake路径 - qmake does not exist or is not executable - qmake不存在或者不可执行 + %1 does not exist or is not executable + %1不存在或者不可执行 Qt version has no name @@ -27976,8 +27976,8 @@ Did you start Qemu? Qt没有被正确安装,请运行make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - 无法确定Qt安装的二进制所在的路径,或许qmake的路径设置出现了错误? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + 无法确定Qt安装的二进制所在的路径,或许%1的路径设置出现了错误? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_zh_TW.ts b/share/qtcreator/translations/qtcreator_zh_TW.ts index 253df6a4499..3aa3e0d04ff 100644 --- a/share/qtcreator/translations/qtcreator_zh_TW.ts +++ b/share/qtcreator/translations/qtcreator_zh_TW.ts @@ -15636,8 +15636,8 @@ Requires <b>Qt 4.7.4</b> or newer. 沒有設定 qmake 路徑 - qmake does not exist or is not executable - qmake 不存在或無法執行 + %1 does not exist or is not executable + %1 不存在或無法執行 Qt version has no name @@ -15664,8 +15664,8 @@ Requires <b>Qt 4.7.4</b> or newer. Qt 沒有被正確安裝,請執行 make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - 無法決定 Qt 安裝版的路徑。也許是 qmake 的路徑設定有錯誤? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + 無法決定 Qt 安裝版的路徑。也許是 %1 的路徑設定有錯誤? The default mkspec symlink is broken. diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp index c1030fd5e78..0ad53e52a73 100644 --- a/src/libs/utils/buildablehelperlibrary.cpp +++ b/src/libs/utils/buildablehelperlibrary.cpp @@ -41,7 +41,61 @@ bool BuildableHelperLibrary::isQtChooser(const FilePath &filePath) return filePath.symLinkTarget().endsWith("/qtchooser"); } -FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) +static const QStringList &queryToolNames() +{ + static const QStringList names = {"qmake", "qtpaths"}; + return names; +} + +static bool isQueryTool(FilePath path) +{ + if (path.isEmpty()) + return false; + if (BuildableHelperLibrary::isQtChooser(path)) + path = BuildableHelperLibrary::qtChooserToQueryToolPath(path.symLinkTarget()); + if (!path.exists()) + return false; + QtcProcess proc; + proc.setCommand({path, {"-query"}}); + proc.runBlocking(); + if (proc.result() != ProcessResult::FinishedWithSuccess) + return false; + const QString output = proc.stdOut(); + // Exemplary output of "[qmake|qtpaths] -query": + // QT_SYSROOT:... + // QT_INSTALL_PREFIX:... + // [...] + // QT_VERSION:6.4.0 + return output.contains("QT_VERSION:"); +} + +static FilePath findQueryToolInDir(const FilePath &dir) +{ + if (dir.isEmpty()) + return {}; + + for (const QString &queryTool : queryToolNames()) { + FilePath queryToolPath = dir.pathAppended(queryTool).withExecutableSuffix(); + if (queryToolPath.exists()) { + if (isQueryTool(queryToolPath)) + return queryToolPath; + } + + // Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order. + const FilePaths candidates = dir.dirEntries( + {BuildableHelperLibrary::possibleQtQueryTools(queryTool), QDir::Files}, + QDir::Name | QDir::Reversed); + for (const FilePath &candidate : candidates) { + if (candidate == queryToolPath) + continue; + if (isQueryTool(candidate)) + return candidate; + } + } + return {}; +} + +FilePath BuildableHelperLibrary::qtChooserToQueryToolPath(const FilePath &qtChooser) { const QString toolDir = QLatin1String("QTTOOLDIR=\""); QtcProcess proc; @@ -51,6 +105,10 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) if (proc.result() != ProcessResult::FinishedWithSuccess) return {}; const QString output = proc.stdOut(); + // Exemplary output of "qtchooser -print-env": + // QT_SELECT="default" + // QTTOOLDIR="/usr/lib/qt5/bin" + // QTLIBDIR="/usr/lib/x86_64-linux-gnu" int pos = output.indexOf(toolDir); if (pos == -1) return {}; @@ -59,44 +117,13 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) if (end == -1) return {}; - FilePath qmake = qtChooser; - qmake.setPath(output.mid(pos, end - pos) + "/qmake"); - return qmake; -} - -static bool isQmake(FilePath path) -{ - if (path.isEmpty()) - return false; - if (BuildableHelperLibrary::isQtChooser(path)) - path = BuildableHelperLibrary::qtChooserToQmakePath(path.symLinkTarget()); - if (!path.exists()) - return false; - return !BuildableHelperLibrary::qtVersionForQMake(path).isEmpty(); -} - -static FilePath findQmakeInDir(const FilePath &dir) -{ - if (dir.isEmpty()) - return {}; - - FilePath qmakePath = dir.pathAppended("qmake").withExecutableSuffix(); - if (qmakePath.exists()) { - if (isQmake(qmakePath)) - return qmakePath; + FilePath queryToolPath = qtChooser; + for (const QString &queryTool : queryToolNames()) { + queryToolPath.setPath(output.mid(pos, end - pos) + "/" + queryTool); + if (queryToolPath.exists()) + return queryToolPath; } - - // Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order. - const FilePaths candidates = dir.dirEntries( - {BuildableHelperLibrary::possibleQMakeCommands(), QDir::Files}, - QDir::Name | QDir::Reversed); - for (const FilePath &candidate : candidates) { - if (candidate == qmakePath) - continue; - if (isQmake(candidate)) - return candidate; - } - return {}; + return queryToolPath; } FilePath BuildableHelperLibrary::findSystemQt(const Environment &env) @@ -107,86 +134,55 @@ FilePath BuildableHelperLibrary::findSystemQt(const Environment &env) FilePaths BuildableHelperLibrary::findQtsInEnvironment(const Environment &env, int maxCount) { - FilePaths qmakeList; + FilePaths queryToolList; std::set canonicalEnvPaths; const FilePaths paths = env.path(); for (const FilePath &path : paths) { if (!canonicalEnvPaths.insert(path.toFileInfo().canonicalFilePath()).second) continue; - const FilePath qmake = findQmakeInDir(path); - if (qmake.isEmpty()) + const FilePath queryTool = findQueryToolInDir(path); + if (queryTool.isEmpty()) continue; - qmakeList << qmake; - if (maxCount != -1 && qmakeList.size() == maxCount) + queryToolList << queryTool; + if (maxCount != -1 && queryToolList.size() == maxCount) break; } - return qmakeList; + return queryToolList; } -QString BuildableHelperLibrary::qtVersionForQMake(const FilePath &qmakePath) +QString BuildableHelperLibrary::filterForQtQueryToolsFileDialog() { - if (qmakePath.isEmpty()) - return QString(); - - QtcProcess qmake; - qmake.setTimeoutS(5); - qmake.setCommand({qmakePath, {"--version"}}); - qmake.runBlocking(); - if (qmake.result() != ProcessResult::FinishedWithSuccess) { - qWarning() << qmake.exitMessage(); - return QString(); + QStringList toolFilters; + for (const QString &queryTool : queryToolNames()) { + for (const QString &tool: BuildableHelperLibrary::possibleQtQueryTools(queryTool)) { + QString toolFilter; + if (HostOsInfo::isMacHost()) + // work around QTBUG-7739 that prohibits filters that don't start with * + toolFilter += QLatin1Char('*'); + toolFilter += tool; + if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) + // kde bug, we need at least one wildcard character + // see QTCREATORBUG-7771 + toolFilter += QLatin1Char('*'); + toolFilters.append(toolFilter); + } } - - const QString output = qmake.allOutput(); - static const QRegularExpression regexp("(QMake version:?)[\\s]*([\\d.]*)", - QRegularExpression::CaseInsensitiveOption); - const QRegularExpressionMatch match = regexp.match(output); - const QString qmakeVersion = match.captured(2); - if (qmakeVersion.startsWith(QLatin1String("2.")) - || qmakeVersion.startsWith(QLatin1String("3."))) { - static const QRegularExpression regexp2("Using Qt version[\\s]*([\\d\\.]*)", - QRegularExpression::CaseInsensitiveOption); - const QRegularExpressionMatch match2 = regexp2.match(output); - const QString version = match2.captured(1); - return version; - } - return QString(); + return queryToolNames().join(", ") + " (" + toolFilters.join(" ") + ")"; } -QString BuildableHelperLibrary::filterForQmakeFileDialog() +QStringList BuildableHelperLibrary::possibleQtQueryTools(const QString &tool) { - QString filter = QLatin1String("qmake ("); - const QStringList commands = possibleQMakeCommands(); - for (int i = 0; i < commands.size(); ++i) { - if (i) - filter += QLatin1Char(' '); - if (HostOsInfo::isMacHost()) - // work around QTBUG-7739 that prohibits filters that don't start with * - filter += QLatin1Char('*'); - filter += commands.at(i); - if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) - // kde bug, we need at least one wildcard character - // see QTCREATORBUG-7771 - filter += QLatin1Char('*'); - } - filter += QLatin1Char(')'); - return filter; -} - - -QStringList BuildableHelperLibrary::possibleQMakeCommands() -{ - // On Windows it is always "qmake.exe" + // On Windows it is ".exe" or ".bat" // On Unix some distributions renamed qmake with a postfix to avoid clashes // On OS X, Qt 4 binary packages also has renamed qmake. There are also symbolic links that are - // named "qmake", but the file dialog always checks against resolved links (native Cocoa issue) - QStringList commands(HostOsInfo::withExecutableSuffix("qmake*")); + // named , but the file dialog always checks against resolved links (native Cocoa issue) + QStringList tools(HostOsInfo::withExecutableSuffix(tool + "*")); // Qt 6 CMake built targets, such as Android, are dependent on the host installation - // and use a script wrapper around the host qmake executable + // and use a script wrapper around the host queryTool executable if (HostOsInfo::isWindowsHost()) - commands.append("qmake*.bat"); - return commands; + tools.append(tool + "*.bat"); + return tools; } } // namespace Utils diff --git a/src/libs/utils/buildablehelperlibrary.h b/src/libs/utils/buildablehelperlibrary.h index 8b47bef1012..2b07ac009e9 100644 --- a/src/libs/utils/buildablehelperlibrary.h +++ b/src/libs/utils/buildablehelperlibrary.h @@ -45,12 +45,11 @@ public: static FilePath findSystemQt(const Environment &env); static FilePaths findQtsInEnvironment(const Environment &env, int maxCount = -1); static bool isQtChooser(const FilePath &filePath); - static FilePath qtChooserToQmakePath(const FilePath &path); - // return true if the qmake at qmakePath is a Qt (used by QtVersion) - static QString qtVersionForQMake(const FilePath &qmakePath); - // returns something like qmake4, qmake, qmake-qt4 or whatever distributions have chosen (used by QtVersion) - static QStringList possibleQMakeCommands(); - static QString filterForQmakeFileDialog(); + static FilePath qtChooserToQueryToolPath(const FilePath &path); + // returns something like qmake4, qmake, qmake-qt4, qtpaths + // or whatever distributions have chosen (used by QtVersion) + static QStringList possibleQtQueryTools(const QString &tool); + static QString filterForQtQueryToolsFileDialog(); }; } // Utils diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 6c39ddef419..1c4112f882c 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -1196,7 +1196,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const if (!version || !version->isValid()) { addWarning(tr("CMake configuration has a path to a qmake binary set, " "even though the kit has no valid Qt version.")); - } else if (qmakePath != version->qmakeFilePath() && isQt4) { + } else if (qmakePath != version->queryToolFilePath() && isQt4) { addWarning(tr("CMake configuration has a path to a qmake binary set " "that does not match the qmake binary path " "configured in the Qt version.")); diff --git a/src/plugins/docker/kitdetector.cpp b/src/plugins/docker/kitdetector.cpp index 5e0c2e86598..474322a6043 100644 --- a/src/plugins/docker/kitdetector.cpp +++ b/src/plugins/docker/kitdetector.cpp @@ -214,10 +214,10 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const QString error; const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) { - if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, - false, - m_sharedId, - &error)) { + if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQueryToolPath(qmake, + false, + m_sharedId, + &error)) { if (qtVersion->isValid()) { if (!Utils::anyOf(qtVersions, [qtVersion](QtVersion* other) { @@ -227,7 +227,7 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const qtVersions.append(qtVersion); QtVersionManager::addVersion(qtVersion); emit q->logOutput( - tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput())); + tr("Found \"%1\"").arg(qtVersion->queryToolFilePath().toUserOutput())); } } } diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp index 7909de1981e..0fbfa346816 100644 --- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp +++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp @@ -181,7 +181,7 @@ void QbsProfileManager::addProfileFromKit(const ProjectExplorer::Kit *k) data = provider->properties(k, data); } if (const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k)) - data.insert("moduleProviders.Qt.qmakeFilePaths", qt->qmakeFilePath().toString()); + data.insert("moduleProviders.Qt.qmakeFilePaths", qt->queryToolFilePath().toString()); if (QbsSettings::qbsVersion() < QVersionNumber({1, 20})) { const QString keyPrefix = "profiles." + name + "."; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index c77f468ae23..d292f1fb5a5 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -745,6 +745,9 @@ Tasks QmakeProject::projectIssues(const Kit *k) const result.append(createProjectTask(Task::TaskType::Error, tr("Qt version is invalid."))); if (!ToolChainKitAspect::cxxToolChain(k)) result.append(createProjectTask(Task::TaskType::Error, tr("No C++ compiler set in kit."))); + if (!qtFromKit->qmakeFilePath().isExecutableFile()) + result.append(createProjectTask(Task::TaskType::Error, + tr("Qmake was not found or is not executable."))); // A project can be considered part of more than one Qt version, for instance if it is an // example shipped via the installer. diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index 2502297ef00..2444303cb4d 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -103,7 +103,8 @@ QMakeStep::QMakeStep(BuildStepList *bsl, Id id) auto updateSummary = [this] { QtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit()); if (!qtVersion) - return tr("qmake: No Qt version set. Cannot run qmake."); + return tr("Query tool: No Qt version set. " + "Cannot run neither qmake nor qtpaths."); const QString program = qtVersion->qmakeFilePath().fileName(); return tr("qmake: %1 %2").arg(program, project()->projectFilePath().fileName()); }; @@ -164,7 +165,7 @@ QString QMakeStep::allArguments(const QtVersion *v, ArgumentFlags flags) const QString args = ProcessArgs::joinArgs(arguments); // User arguments ProcessArgs::addArgs(&args, userArguments()); - for (QString arg : qAsConst(m_extraArgs)) + for (const QString &arg : qAsConst(m_extraArgs)) ProcessArgs::addArgs(&args, arg); return (flags & ArgumentFlag::Expand) ? bc->macroExpander()->expand(args) : args; } @@ -214,7 +215,8 @@ bool QMakeStep::init() else workingDirectory = qmakeBc->buildDirectory(); - m_qmakeCommand = CommandLine{qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw}; + m_qmakeCommand = + CommandLine{qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw}; m_runMakeQmake = (qtVersion->qtVersion() >= QtVersionNumber(5, 0 ,0)); // The Makefile is used by qmake and make on the build device, from that diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp index 38c88a1cee1..6f511472057 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp @@ -610,7 +610,7 @@ QString PuppetCreator::qmakeCommand() const { QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(m_target->kit()); if (currentQtVersion) - return currentQtVersion->qmakeFilePath().toString(); + return currentQtVersion->queryToolFilePath().toString(); return QString(); } diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 1aafe1f575c..324add0e7c6 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -81,6 +81,7 @@ const char QTVERSIONAUTODETECTED[] = "isAutodetected"; const char QTVERSIONDETECTIONSOURCE[] = "autodetectionSource"; const char QTVERSION_OVERRIDE_FEATURES[] = "overrideFeatures"; const char QTVERSIONQMAKEPATH[] = "QMakePath"; +const char QTVERSIONQUERYTOOLPATH[] = "QueryToolPath"; const char QTVERSIONSOURCEPATH[] = "SourcePath"; const char QTVERSION_ABIS[] = "Abis"; @@ -187,19 +188,19 @@ public: FilePath findHostBinary(HostBinaries binary) const; void updateMkspec(); QHash versionInfo(); - static bool queryQMakeVariables(const FilePath &binary, - const Environment &env, - QHash *versionInfo, - QString *error = nullptr); + static bool queryQtPaths(const FilePath &queryTool, + const Environment &env, + QHash *versionInfo, + QString *error = nullptr); enum PropertyVariant { PropertyVariantDev, PropertyVariantGet, PropertyVariantSrc }; QString qmakeProperty(const QByteArray &name, PropertyVariant variant = PropertyVariantGet); static QString qmakeProperty(const QHash &versionInfo, const QByteArray &name, PropertyVariant variant = PropertyVariantGet); static FilePath mkspecDirectoryFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand); + const FilePath &queryTool); static FilePath mkspecFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand); + const FilePath &queryTool); static FilePath sourcePath(const QHash &versionInfo); void setId(int id); // used by the qtversionmanager for legacy restore // and by the qtoptionspage to replace Qt versions @@ -221,7 +222,7 @@ public: bool m_defaultConfigIsDebugAndRelease = true; bool m_frameworkBuild = false; bool m_versionInfoUpToDate = false; - bool m_qmakeIsExecutable = true; + bool m_queryToolIsExecutable = true; QString m_detectionSource; QSet m_overrideFeatures; @@ -233,6 +234,7 @@ public: QHash m_versionInfo; + FilePath m_queryTool; FilePath m_qmakeCommand; FilePath m_rccPath; @@ -346,12 +348,12 @@ QtVersion::~QtVersion() QString QtVersion::defaultUnexpandedDisplayName() const { QString location; - if (qmakeFilePath().isEmpty()) { + if (queryToolFilePath().isEmpty()) { location = QCoreApplication::translate("QtVersion", ""); } else { // Deduce a description from '/foo/qt-folder/[qtbase]/bin/qmake' -> '/foo/qt-folder'. // '/usr' indicates System Qt 4.X on Linux. - for (FilePath dir = qmakeFilePath().parentDir(); !dir.isEmpty(); dir = dir.parentDir()) { + for (FilePath dir = queryToolFilePath().parentDir(); !dir.isEmpty(); dir = dir.parentDir()) { const QString dirName = dir.fileName(); if (dirName == "usr") { // System-installed Qt. location = QCoreApplication::translate("QtVersion", "System"); @@ -725,20 +727,23 @@ void QtVersion::fromMap(const QVariantMap &map) d->m_isAutodetected = map.value(QTVERSIONAUTODETECTED).toBool(); d->m_detectionSource = map.value(QTVERSIONDETECTIONSOURCE).toString(); d->m_overrideFeatures = Utils::Id::fromStringList(map.value(QTVERSION_OVERRIDE_FEATURES).toStringList()); - d->m_qmakeCommand = FilePath::fromVariant(map.value(QTVERSIONQMAKEPATH)); + d->m_queryTool = FilePath::fromVariant(map.value(QTVERSIONQUERYTOOLPATH, + map.value(QTVERSIONQMAKEPATH))); + if (!d->m_queryTool.baseName().contains("qtpaths")) + d->m_qmakeCommand = d->m_queryTool; - FilePath qmake = d->m_qmakeCommand; + FilePath queryTool = d->m_queryTool; // FIXME: Check this is still needed or whether ProcessArgs::splitArg handles it. - QString string = d->m_qmakeCommand.path(); + QString string = d->m_queryTool.path(); if (string.startsWith('~')) string.remove(0, 1).prepend(QDir::homePath()); - qmake.setPath(string); - if (!d->m_qmakeCommand.needsDevice()) { - if (BuildableHelperLibrary::isQtChooser(qmake)) { + queryTool.setPath(string); + if (!d->m_queryTool.needsDevice()) { + if (BuildableHelperLibrary::isQtChooser(queryTool)) { // we don't want to treat qtchooser as a normal qmake // see e.g. QTCREATORBUG-9841, also this lead to users changing what // qtchooser forwards too behind our backs, which will inadvertly lead to bugs - d->m_qmakeCommand = BuildableHelperLibrary::qtChooserToQmakePath(qmake); + d->m_queryTool = BuildableHelperLibrary::qtChooserToQueryToolPath(queryTool); } } @@ -769,6 +774,7 @@ QVariantMap QtVersion::toMap() const result.insert(QTVERSION_OVERRIDE_FEATURES, Utils::Id::toStringList(d->m_overrideFeatures)); result.insert(QTVERSIONQMAKEPATH, qmakeFilePath().toVariant()); + result.insert(QTVERSIONQUERYTOOLPATH, queryToolFilePath().toVariant()); return result; } @@ -779,8 +785,8 @@ bool QtVersion::isValid() const d->updateVersionInfo(); d->updateMkspec(); - return !qmakeFilePath().isEmpty() && d->m_data.installed && !binPath().isEmpty() - && !d->m_mkspecFullPath.isEmpty() && d->m_qmakeIsExecutable; + return !queryToolFilePath().isEmpty() && d->m_data.installed && !binPath().isEmpty() + && !d->m_mkspecFullPath.isEmpty() && d->m_queryToolIsExecutable; } QtVersion::Predicate QtVersion::isValidPredicate(const QtVersion::Predicate &predicate) @@ -794,15 +800,18 @@ QString QtVersion::invalidReason() const { if (displayName().isEmpty()) return QCoreApplication::translate("QtVersion", "Qt version has no name"); - if (qmakeFilePath().isEmpty()) - return QCoreApplication::translate("QtVersion", "No qmake path set"); - if (!d->m_qmakeIsExecutable) - return QCoreApplication::translate("QtVersion", "qmake does not exist or is not executable"); + if (queryToolFilePath().isEmpty()) + return QCoreApplication::translate("QtVersion", "No Qt query tool path set"); + if (!d->m_queryToolIsExecutable) + return QCoreApplication::translate("QtVersion", "%1 does not exist or is not executable") + .arg(queryToolFilePath().baseName()); if (!d->m_data.installed) return QCoreApplication::translate("QtVersion", "Qt version is not properly installed, please run make install"); if (binPath().isEmpty()) return QCoreApplication::translate("QtVersion", - "Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?"); + "Could not determine the path to the binaries of the " + "Qt installation, maybe the %1 path is wrong?") + .arg(queryToolFilePath().baseName()); if (d->m_mkspecUpToDate && d->m_mkspecFullPath.isEmpty()) return QCoreApplication::translate("QtVersion", "The default mkspec symlink is broken."); return QString(); @@ -820,8 +829,20 @@ QStringList QtVersion::warningReason() const return ret; } +FilePath QtVersion::queryToolFilePath() const +{ + return d->m_queryTool; +} + FilePath QtVersion::qmakeFilePath() const { + if (d->m_qmakeCommand.isEmpty() && d->m_queryTool.baseName().contains("qtpaths")) { + // TODO: might need a less lazy implementation + const FilePath qmake = + FilePath::fromString(d->m_queryTool.toString().replace("qtpaths", "qmake")); + if (qmake.exists()) + d->m_qmakeCommand = qmake; + } return d->m_qmakeCommand; } @@ -855,7 +876,7 @@ bool QtVersion::hasAbi(ProjectExplorer::Abi::OS os, ProjectExplorer::Abi::OSFlav bool QtVersion::equals(QtVersion *other) { - if (d->m_qmakeCommand != other->d->m_qmakeCommand) + if (d->m_queryTool != other->d->m_queryTool) return false; if (type() != other->type()) return false; @@ -933,13 +954,15 @@ QString QtVersion::toHtml(bool verbose) const str << "" << abis.at(i).toString() << ""; } } - const OsType osType = d->m_qmakeCommand.osType(); + const OsType osType = d->m_queryTool.osType(); str << "" << QCoreApplication::translate("QtVersion", "Source:") << "" << sourcePath().toUserOutput() << ""; str << "" << QCoreApplication::translate("QtVersion", "mkspec:") << "" << QDir::toNativeSeparators(mkspec()) << ""; str << "" << QCoreApplication::translate("QtVersion", "qmake:") - << "" << d->m_qmakeCommand.toUserOutput() << ""; + << "" << qmakeFilePath().toUserOutput() << ""; + str << "" << QCoreApplication::translate("QtVersion", "Query tool:") + << "" << d->m_queryTool.toUserOutput() << ""; ensureMkSpecParsed(); if (!mkspecPath().isEmpty()) { if (d->m_defaultConfigIsDebug || d->m_defaultConfigIsDebugAndRelease) { @@ -1164,13 +1187,13 @@ void QtVersionPrivate::updateMkspec() return; m_mkspecUpToDate = true; - m_mkspecFullPath = mkspecFromVersionInfo(versionInfo(), m_qmakeCommand); + m_mkspecFullPath = mkspecFromVersionInfo(versionInfo(), m_queryTool); m_mkspec = m_mkspecFullPath; if (m_mkspecFullPath.isEmpty()) return; - FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo(), m_qmakeCommand); + FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo(), m_queryTool); if (m_mkspec.isChildOf(baseMkspecDir)) { m_mkspec = m_mkspec.relativeChildPath(baseMkspecDir); @@ -1197,7 +1220,7 @@ void QtVersion::ensureMkSpecParsed() const QMakeVfs vfs; QMakeGlobals option; applyProperties(&option); - Environment env = d->m_qmakeCommand.deviceEnvironment(); + Environment env = d->m_queryTool.deviceEnvironment(); setupQmakeRunEnvironment(env); option.environment = env.toProcessEnvironment(); ProMessageHandler msgHandler(true); @@ -1310,7 +1333,7 @@ QtVersionNumber QtVersion::qtVersion() const void QtVersionPrivate::updateVersionInfo() { - if (m_versionInfoUpToDate || !m_qmakeIsExecutable || m_isUpdating) + if (m_versionInfoUpToDate || !m_queryToolIsExecutable || m_isUpdating) return; m_isUpdating = true; @@ -1321,16 +1344,16 @@ void QtVersionPrivate::updateVersionInfo() m_data.hasExamples = false; m_data.hasDocumentation = false; - if (!queryQMakeVariables(m_qmakeCommand, q->qmakeRunEnvironment(), &m_versionInfo)) { - m_qmakeIsExecutable = false; + if (!queryQtPaths(m_queryTool, q->qmakeRunEnvironment(), &m_versionInfo)) { + m_queryToolIsExecutable = false; qWarning("Cannot update Qt version information: %s cannot be run.", - qPrintable(m_qmakeCommand.toString())); + qPrintable(m_queryTool.toString())); return; } - m_qmakeIsExecutable = true; + m_queryToolIsExecutable = true; auto fileProperty = [this](const QByteArray &name) { - return FilePath::fromUserInput(qmakeProperty(name)).onDevice(m_qmakeCommand); + return FilePath::fromUserInput(qmakeProperty(name)).onDevice(m_queryTool); }; m_data.prefix = fileProperty("QT_INSTALL_PREFIX"); @@ -1385,8 +1408,8 @@ QHash QtVersionPrivate::versionInfo() } QString QtVersionPrivate::qmakeProperty(const QHash &versionInfo, - const QByteArray &name, - PropertyVariant variant) + const QByteArray &name, + PropertyVariant variant) { QString val = versionInfo .value(ProKey(QString::fromLatin1( @@ -1728,7 +1751,7 @@ void QtVersion::addToEnvironment(const Kit *k, Environment &env) const Environment QtVersion::qmakeRunEnvironment() const { - Environment env = d->m_qmakeCommand.deviceEnvironment(); + Environment env = d->m_queryTool.deviceEnvironment(); setupQmakeRunEnvironment(env); return env; } @@ -1756,7 +1779,7 @@ Tasks QtVersion::reportIssuesImpl(const QString &proFile, const QString &buildDi results.append(BuildSystemTask(Task::Error, msg)); } - FilePath qmake = qmakeFilePath(); + FilePath qmake = queryToolFilePath(); if (!qmake.isExecutableFile()) { //: %1: Path to qmake executable const QString msg = QCoreApplication::translate("QmakeProjectManager::QtVersion", @@ -1816,20 +1839,21 @@ static QByteArray runQmakeQuery(const FilePath &binary, const Environment &env, return process.readAllStandardOutput(); } -bool QtVersionPrivate::queryQMakeVariables(const FilePath &binary, const Environment &env, - QHash *versionInfo, QString *error) +bool QtVersionPrivate::queryQtPaths(const FilePath &queryTool, const Environment &env, + QHash *versionInfo, QString *error) { QString tmp; if (!error) error = &tmp; - if (!binary.isExecutableFile()) { - *error = QCoreApplication::translate("QtVersion", "qmake \"%1\" is not an executable.").arg(binary.toUserOutput()); + if (!queryTool.isExecutableFile()) { + *error = QCoreApplication::translate("QtVersion", "\"%1\" is not an executable.") + .arg(queryTool.toUserOutput()); return false; } QByteArray output; - output = runQmakeQuery(binary, env, error); + output = runQmakeQuery(queryTool, env, error); if (!output.contains("QMAKE_VERSION:")) { // Some setups pass error messages via stdout, fooling the logic below. @@ -1847,14 +1871,14 @@ bool QtVersionPrivate::queryQMakeVariables(const FilePath &binary, const Environ // Try running qmake with all kinds of tool chains set up in the environment. // This is required to make non-static qmakes work on windows where every tool chain // tries to be incompatible with any other. - const Abis abiList = Abi::abisOfBinary(binary); + const Abis abiList = Abi::abisOfBinary(queryTool); const Toolchains tcList = ToolChainManager::toolchains([&abiList](const ToolChain *t) { return abiList.contains(t->targetAbi()); }); for (ToolChain *tc : tcList) { Environment realEnv = env; tc->addToEnvironment(realEnv); - output = runQmakeQuery(binary, realEnv, error); + output = runQmakeQuery(queryTool, realEnv, error); if (error->isEmpty()) break; } @@ -1876,18 +1900,18 @@ QString QtVersionPrivate::qmakeProperty(const QByteArray &name, } FilePath QtVersionPrivate::mkspecDirectoryFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand) + const FilePath &queryTool) { QString dataDir = qmakeProperty(versionInfo, "QT_HOST_DATA", PropertyVariantSrc); if (dataDir.isEmpty()) return FilePath(); - return FilePath::fromUserInput(dataDir + "/mkspecs").onDevice(qmakeCommand); + return FilePath::fromUserInput(dataDir + "/mkspecs").onDevice(queryTool); } FilePath QtVersionPrivate::mkspecFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand) + const FilePath &queryTool) { - FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo, qmakeCommand); + FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo, queryTool); if (baseMkspecDir.isEmpty()) return FilePath(); @@ -2319,14 +2343,16 @@ void QtVersion::resetCache() const static QList g_qtVersionFactories; -QtVersion *QtVersionFactory::createQtVersionFromQMakePath - (const FilePath &qmakePath, bool isAutoDetected, const QString &detectionSource, QString *error) +QtVersion *QtVersionFactory::createQtVersionFromQueryToolPath(const FilePath &queryTool, + bool isAutoDetected, + const QString &detectionSource, + QString *error) { QHash versionInfo; - const Environment env = qmakePath.deviceEnvironment(); - if (!QtVersionPrivate::queryQMakeVariables(qmakePath, env, &versionInfo, error)) + const Environment env = queryTool.deviceEnvironment(); + if (!QtVersionPrivate::queryQtPaths(queryTool, env, &versionInfo, error)) return nullptr; - FilePath mkspec = QtVersionPrivate::mkspecFromVersionInfo(versionInfo, qmakePath); + FilePath mkspec = QtVersionPrivate::mkspecFromVersionInfo(versionInfo, queryTool); QMakeVfs vfs; QMakeGlobals globals; @@ -2342,7 +2368,7 @@ QtVersion *QtVersionFactory::createQtVersionFromQMakePath return l->m_priority > r->m_priority; }); - if (!qmakePath.isExecutableFile()) + if (!queryTool.isExecutableFile()) return nullptr; QtVersionFactory::SetupData setup; @@ -2355,8 +2381,8 @@ QtVersion *QtVersionFactory::createQtVersionFromQMakePath QtVersion *ver = factory->create(); QTC_ASSERT(ver, continue); ver->d->m_id = QtVersionManager::getUniqueId(); - QTC_CHECK(ver->d->m_qmakeCommand.isEmpty()); // Should only be used once. - ver->d->m_qmakeCommand = qmakePath; + QTC_CHECK(ver->d->m_queryTool.isEmpty()); // Should only be used once. + ver->d->m_queryTool = queryTool; ver->d->m_detectionSource = detectionSource; ver->d->m_isAutodetected = isAutoDetected; ver->updateDefaultDisplayName(); @@ -2367,7 +2393,7 @@ QtVersion *QtVersionFactory::createQtVersionFromQMakePath ProFileCacheManager::instance()->decRefCount(); if (error) { *error = QCoreApplication::translate("QtSupport::QtVersionFactory", - "No factory found for qmake: \"%1\"").arg(qmakePath.toUserOutput()); + "No factory found for query tool \"%1\"").arg(queryTool.toUserOutput()); } return nullptr; } diff --git a/src/plugins/qtsupport/baseqtversion.h b/src/plugins/qtsupport/baseqtversion.h index 269a6507168..f0400bc99b5 100644 --- a/src/plugins/qtsupport/baseqtversion.h +++ b/src/plugins/qtsupport/baseqtversion.h @@ -150,6 +150,8 @@ public: bool hasDocs() const; bool hasDemos() const; + Utils::FilePath queryToolFilePath() const; + // former local functions Utils::FilePath qmakeFilePath() const; diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 43bbc07c1c3..672ad493df4 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -324,6 +324,11 @@ void QtKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const QtVersion *version = qtVersion(kit); return version ? version->qmakeFilePath().path() : QString(); }); + expander->registerVariable("Qt:queryToolExecutable", tr("Path to the query tool executable"), + [kit]() -> QString { + QtVersion *version = qtVersion(kit); + return version ? version->queryToolFilePath().path() : QString(); + }); } Id QtKitAspect::id() diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index c0049058abd..06a8a6bbd52 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -111,7 +112,7 @@ public: if (column == 0) return m_version->displayName(); if (column == 1) - return m_version->qmakeFilePath().toUserOutput(); + return m_version->queryToolFilePath().toUserOutput(); } if (role == Qt::FontRole && m_changed) { @@ -128,7 +129,8 @@ public: "
%2
"; return QString("
" + row.arg(tr("Qt Version"), m_version->qtVersionString()) - + row.arg(tr("Location of qmake"), m_version->qmakeFilePath().toUserOutput()) + + row.arg(tr("Location of the query tool"), + m_version->queryToolFilePath().toUserOutput()) + "
"); } @@ -600,27 +602,42 @@ QtOptionsPageWidget::~QtOptionsPageWidget() delete m_configurationWidget; } +static bool isIncompatibleQtPathsTool(const FilePath &tool) +{ + if (!tool.baseName().startsWith("qtpaths")) + return false; + QtcProcess process; + process.setTimeoutS(1); + process.setCommand({tool, {"-query"}}); + process.runBlocking(); + return process.result() != ProcessResult::FinishedWithSuccess; +} + void QtOptionsPageWidget::addQtDir() { - FilePath qtVersion = FileUtils::getOpenFilePath(this, - tr("Select a qmake Executable"), - {}, - BuildableHelperLibrary::filterForQmakeFileDialog(), - 0, - QFileDialog::DontResolveSymlinks); - if (qtVersion.isEmpty()) + FilePath qtQueryTool = + FileUtils::getOpenFilePath(this, + tr("Select a qmake or qtpaths Executable"), + {}, + BuildableHelperLibrary::filterForQtQueryToolsFileDialog(), + 0, + QFileDialog::DontResolveSymlinks); + if (qtQueryTool.isEmpty()) return; - // should add all qt versions here ? - if (BuildableHelperLibrary::isQtChooser(qtVersion)) - qtVersion = BuildableHelperLibrary::qtChooserToQmakePath(qtVersion.symLinkTarget()); + if (isIncompatibleQtPathsTool(qtQueryTool)) + qtQueryTool = qtQueryTool.parentDir() / HostOsInfo::withExecutableSuffix("qmake"); - auto checkAlreadyExists = [qtVersion](TreeItem *parent) { + // should add all qt versions here ? + if (BuildableHelperLibrary::isQtChooser(qtQueryTool)) + qtQueryTool = BuildableHelperLibrary::qtChooserToQueryToolPath(qtQueryTool.symLinkTarget()); + + auto checkAlreadyExists = [qtQueryTool](TreeItem *parent) { for (int i = 0; i < parent->childCount(); ++i) { auto item = static_cast(parent->childAt(i)); - if (item->version()->qmakeFilePath() == qtVersion) { + // Compare parent dirs, since it could be either qmake or qtpaths + if (item->version()->queryToolFilePath().parentDir() == qtQueryTool.parentDir()) return std::make_pair(true, item->version()->displayName()); - } } return std::make_pair(false, QString()); }; @@ -640,7 +657,8 @@ void QtOptionsPageWidget::addQtDir() } QString error; - QtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion, false, QString(), &error); + QtVersion *version = QtVersionFactory::createQtVersionFromQueryToolPath(qtQueryTool, false, + QString(), &error); if (version) { auto item = new QtVersionItem(version); item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon); @@ -650,8 +668,9 @@ void QtOptionsPageWidget::addQtDir() m_versionUi.nameEdit->setFocus(); m_versionUi.nameEdit->selectAll(); } else { - QMessageBox::warning(this, tr("Qmake Not Executable"), - tr("The qmake executable %1 could not be added: %2").arg(qtVersion.toUserOutput()).arg(error)); + QMessageBox::warning(this, tr("Not Executable"), + tr("The executable %1 could not be added: %2").arg( + qtQueryTool.toUserOutput()).arg(error)); return; } updateCleanUpButton(); @@ -671,16 +690,16 @@ void QtOptionsPageWidget::removeQtDir() void QtOptionsPageWidget::editPath() { QtVersion *current = currentVersion(); - FilePath qtVersion = + const FilePath queryTool = FileUtils::getOpenFilePath(this, - tr("Select a qmake Executable"), - current->qmakeFilePath().absolutePath(), - BuildableHelperLibrary::filterForQmakeFileDialog(), + tr("Select a qmake or qtpaths Executable"), + current->queryToolFilePath().absolutePath(), + BuildableHelperLibrary::filterForQtQueryToolsFileDialog(), nullptr, QFileDialog::DontResolveSymlinks); - if (qtVersion.isEmpty()) + if (queryTool.isEmpty()) return; - QtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion); + QtVersion *version = QtVersionFactory::createQtVersionFromQueryToolPath(queryTool); if (!version) return; // Same type? then replace! @@ -768,7 +787,7 @@ void QtOptionsPageWidget::updateWidgets() QtVersion *version = currentVersion(); if (version) { m_versionUi.nameEdit->setText(version->unexpandedDisplayName()); - m_versionUi.qmakePath->setText(version->qmakeFilePath().toUserOutput()); + m_versionUi.queryToolPath->setText(version->queryToolFilePath().toUserOutput()); m_configurationWidget = version->createConfigurationWidget(); if (m_configurationWidget) { m_versionUi.formLayout->addRow(m_configurationWidget); @@ -778,7 +797,7 @@ void QtOptionsPageWidget::updateWidgets() } } else { m_versionUi.nameEdit->clear(); - m_versionUi.qmakePath->clear(); + m_versionUi.queryToolPath->clear(); } const bool enabled = version != nullptr; diff --git a/src/plugins/qtsupport/qtprojectimporter.cpp b/src/plugins/qtsupport/qtprojectimporter.cpp index 9014aa71f08..fc44f3b7d37 100644 --- a/src/plugins/qtsupport/qtprojectimporter.cpp +++ b/src/plugins/qtsupport/qtprojectimporter.cpp @@ -57,7 +57,7 @@ QtProjectImporter::QtVersionData QtProjectImporter::findOrCreateQtVersion(const Utils::FilePath &qmakePath) const { QtVersionData result; - result.qt = QtVersionManager::version(Utils::equal(&QtVersion::qmakeFilePath, qmakePath)); + result.qt = QtVersionManager::version(Utils::equal(&QtVersion::queryToolFilePath, qmakePath)); if (result.qt) { // Check if version is a temporary qt const int qtId = result.qt->uniqueId(); @@ -67,7 +67,7 @@ QtProjectImporter::findOrCreateQtVersion(const Utils::FilePath &qmakePath) const // Create a new version if not found: // Do not use the canonical path here... - result.qt = QtVersionFactory::createQtVersionFromQMakePath(qmakePath); + result.qt = QtVersionFactory::createQtVersionFromQueryToolPath(qmakePath); result.isTemporary = true; if (result.qt) { UpdateGuard guard(*this); @@ -281,7 +281,7 @@ static QStringList additionalFilesToCopy(const QtVersion *qt) } else if (HostOsInfo::isWindowsHost()) { const QString release = QString("bin/Qt%1Core.dll").arg(major); const QString debug = QString("bin/Qt%1Cored.dll").arg(major); - const FilePath base = qt->qmakeFilePath().parentDir().parentDir(); + const FilePath base = qt->queryToolFilePath().parentDir().parentDir(); if (base.pathAppended(release).exists()) return {release}; if (base.pathAppended(debug).exists()) @@ -289,7 +289,7 @@ static QStringList additionalFilesToCopy(const QtVersion *qt) return {release}; } else if (HostOsInfo::isLinuxHost()) { const QString core = QString("lib/libQt%1Core.so.%1").arg(major); - const QDir base(qt->qmakeFilePath().parentDir().parentDir().pathAppended("lib").toString()); + const QDir base(qt->queryToolFilePath().parentDir().parentDir().pathAppended("lib").toString()); const QStringList icuLibs = Utils::transform(base.entryList({"libicu*.so.*"}), [](const QString &lib) { return QString("lib/" + lib); }); return QStringList(core) + icuLibs; } @@ -300,7 +300,7 @@ static QStringList additionalFilesToCopy(const QtVersion *qt) static Utils::FilePath setupQmake(const QtVersion *qt, const QString &path) { // This is a hack and only works with local, "standard" installations of Qt - const FilePath qmake = qt->qmakeFilePath().canonicalPath(); + const FilePath qmake = qt->queryToolFilePath().canonicalPath(); const QString qmakeFile = "bin/" + qmake.fileName(); const FilePath source = qmake.parentDir().parentDir(); const FilePath target = FilePath::fromString(path); diff --git a/src/plugins/qtsupport/qtversionfactory.h b/src/plugins/qtsupport/qtversionfactory.h index bc01ad7601a..96bf8e2088a 100644 --- a/src/plugins/qtsupport/qtversionfactory.h +++ b/src/plugins/qtsupport/qtversionfactory.h @@ -51,7 +51,7 @@ public: /// the desktop factory claims to handle all paths int priority() const { return m_priority; } - static QtVersion *createQtVersionFromQMakePath(const Utils::FilePath &qmakePath, + static QtVersion *createQtVersionFromQueryToolPath(const Utils::FilePath &qmakePath, bool isAutoDetected = false, const QString &detectionSource = {}, QString *error = nullptr); diff --git a/src/plugins/qtsupport/qtversioninfo.ui b/src/plugins/qtsupport/qtversioninfo.ui index 467e898a20e..815be818177 100644 --- a/src/plugins/qtsupport/qtversioninfo.ui +++ b/src/plugins/qtsupport/qtversioninfo.ui @@ -39,14 +39,14 @@ - qmake path: + Query tool path: - + 0 diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp index 2ca6c3a1237..c5f00e96189 100644 --- a/src/plugins/qtsupport/qtversionmanager.cpp +++ b/src/plugins/qtsupport/qtversionmanager.cpp @@ -265,7 +265,8 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) if (log().isDebugEnabled()) { qCDebug(log) << "======= Existing Qt versions ======="; for (QtVersion *version : qAsConst(m_versions)) { - qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:"<uniqueId(); + qCDebug(log) << version->queryToolFilePath().toUserOutput() + << "id:" <uniqueId(); qCDebug(log) << " autodetection source:" << version->detectionSource(); qCDebug(log) << ""; } @@ -341,7 +342,8 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) if (log().isDebugEnabled()) { qCDebug(log) << "======= Before removing outdated sdk versions ======="; for (QtVersion *version : qAsConst(m_versions)) { - qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:" << version->uniqueId(); + qCDebug(log) << version->queryToolFilePath().toUserOutput() + << "id:" << version->uniqueId(); qCDebug(log) << " autodetection source:" << version->detectionSource(); qCDebug(log) << ""; } @@ -360,7 +362,8 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) if (log().isDebugEnabled()) { qCDebug(log)<< "======= End result ======="; for (QtVersion *version : qAsConst(m_versions)) { - qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:" << version->uniqueId(); + qCDebug(log) << version->queryToolFilePath().toUserOutput() + << "id:" << version->uniqueId(); qCDebug(log) << " autodetection source:" << version->detectionSource(); qCDebug(log) << ""; } @@ -401,14 +404,20 @@ static QList runQtChooser(const QString &qtchooser, const QStringLis } // Asks qtchooser for the qmake path of a given version +// TODO: Extend to qtpaths if qtchooser is also used for Qt 6 static QString qmakePath(const QString &qtchooser, const QString &version) { const QList outputs = runQtChooser(qtchooser, {QStringLiteral("-qt=%1").arg(version), QStringLiteral("-print-env")}); + // Exemplary output of "qtchooser -qt=qt5-x86_64-linux-gnu -print-env": + // QT_SELECT="qt5-x86_64-linux-gnu" + // QTTOOLDIR="/usr/lib/qt5/bin" + // QTLIBDIR="/usr/lib/x86_64-linux-gnu" + const QByteArray qtToolDirPrefix("QTTOOLDIR=\""); for (const QByteArray &output : outputs) { - if (output.startsWith("QTTOOLDIR=\"")) { - QByteArray withoutVarName = output.mid(11); // remove QTTOOLDIR=" + if (output.startsWith(qtToolDirPrefix)) { + QByteArray withoutVarName = output.mid(qtToolDirPrefix.size()); // remove QTTOOLDIR=" withoutVarName.chop(1); // remove trailing quote return QStandardPaths::findExecutable(QStringLiteral("qmake"), QStringList() << QString::fromLocal8Bit(withoutVarName)); @@ -424,6 +433,15 @@ static FilePaths gatherQmakePathsFromQtChooser() return FilePaths(); const QList versions = runQtChooser(qtchooser, QStringList("-l")); + // Exemplary output of "qtchooser -l": + // 4 + // 5 + // default + // qt4-x86_64-linux-gnu + // qt4 + // qt5-x86_64-linux-gnu + // qt5 + // "" QSet foundQMakes; for (const QByteArray &version : versions) { FilePath possibleQMake = FilePath::fromString( @@ -436,19 +454,20 @@ static FilePaths gatherQmakePathsFromQtChooser() static void findSystemQt() { - FilePaths systemQMakes + FilePaths systemQueryTools = BuildableHelperLibrary::findQtsInEnvironment(Environment::systemEnvironment()); - systemQMakes.append(gatherQmakePathsFromQtChooser()); - for (const FilePath &qmakePath : qAsConst(systemQMakes)) { - if (BuildableHelperLibrary::isQtChooser(qmakePath)) + systemQueryTools.append(gatherQmakePathsFromQtChooser()); + for (const FilePath &queryToolPath : qAsConst(systemQueryTools)) { + if (BuildableHelperLibrary::isQtChooser(queryToolPath)) continue; - const auto isSameQmake = [qmakePath](const QtVersion *version) { + const auto isSameQueryTool = [queryToolPath](const QtVersion *version) { return Environment::systemEnvironment(). - isSameExecutable(qmakePath.toString(), version->qmakeFilePath().toString()); + isSameExecutable(queryToolPath.toString(), + version->queryToolFilePath().toString()); }; - if (contains(m_versions, isSameQmake)) + if (contains(m_versions, isSameQueryTool)) continue; - QtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qmakePath, + QtVersion *version = QtVersionFactory::createQtVersionFromQueryToolPath(queryToolPath, false, "PATH"); if (version) From 16aaf8c59c06748d3121e1adde262714440c1df4 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 31 May 2022 13:43:14 +0200 Subject: [PATCH 57/58] Valgrind: Clean up a bit after class merge Change-Id: I1df02a93ebca318424461c3e48b9bb51133eec8d Reviewed-by: Qt CI Bot Reviewed-by: Jarek Kobus --- src/plugins/valgrind/callgrindengine.cpp | 64 +++--------------------- src/plugins/valgrind/callgrindengine.h | 21 +++----- 2 files changed, 16 insertions(+), 69 deletions(-) diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp index c7660468d54..aa9b3fad546 100644 --- a/src/plugins/valgrind/callgrindengine.cpp +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -62,18 +62,18 @@ CallgrindToolRunner::CallgrindToolRunner(RunControl *runControl) connect(&m_parser, &Callgrind::Parser::parserDataReady, this, &CallgrindToolRunner::slotFinished); - connect(&m_runner, &ValgrindRunner::valgrindStarted, - this, &CallgrindToolRunner::setValgrindPid); + connect(&m_runner, &ValgrindRunner::valgrindStarted, this, [this](qint64 pid) { + m_pid = pid; + }); connect(&m_runner, &ValgrindRunner::extraProcessFinished, this, [this] { triggerParse(); }); - setValgrindRunnable(runControl->runnable()); + m_valgrindRunnable = runControl->runnable(); static int fileCount = 100; m_valgrindOutputFile = runControl->workingDirectory() / QString("callgrind.out.f%1").arg(++fileCount); - setValgrindOutputFile(m_valgrindOutputFile); setupCallgrindRunner(this); } @@ -125,11 +125,6 @@ void CallgrindToolRunner::start() return ValgrindToolRunner::start(); } -void CallgrindToolRunner::dump() -{ - run(Dump); -} - void CallgrindToolRunner::setPaused(bool paused) { if (m_markAsPaused == paused) @@ -152,21 +147,6 @@ void CallgrindToolRunner::setToggleCollectFunction(const QString &toggleCollectF m_argumentForToggleCollect = "--toggle-collect=" + toggleCollectFunction; } -void CallgrindToolRunner::reset() -{ - run(ResetEventCounters); -} - -void CallgrindToolRunner::pause() -{ - run(Pause); -} - -void CallgrindToolRunner::unpause() -{ - run(UnPause); -} - Callgrind::ParseData *CallgrindToolRunner::takeParserData() { return m_parser.takeData(); @@ -182,12 +162,6 @@ void CallgrindToolRunner::showStatusMessage(const QString &message) Debugger::showPermanentStatusMessage(message); } -void CallgrindToolRunner::triggerParse() -{ - getLocalDataFile(); -} - - static QString toOptionString(CallgrindToolRunner::Option option) { /* callgrind_control help from v3.9.0 @@ -261,11 +235,6 @@ void CallgrindToolRunner::run(Option option) m_controllerProcess->start(); } -void CallgrindToolRunner::setValgrindPid(qint64 pid) -{ - m_pid = pid; -} - void CallgrindToolRunner::controllerProcessDone() { const QString error = m_controllerProcess->errorString(); @@ -286,36 +255,24 @@ void CallgrindToolRunner::controllerProcessDone() run(Dump); return; case Pause: + m_paused = true; break; case Dump: showStatusMessage(tr("Callgrind dumped profiling info")); + triggerParse(); break; case UnPause: + m_paused = false; showStatusMessage(tr("Callgrind unpaused.")); break; default: break; } - switch (m_lastOption) - { - case Pause: - m_paused = true; - break; - case UnPause: - m_paused = false; - break; - case Dump: - triggerParse(); - break; - default: - break; // do nothing - } - m_lastOption = Unknown; } -void CallgrindToolRunner::getLocalDataFile() +void CallgrindToolRunner::triggerParse() { cleanupTempFile(); { @@ -341,10 +298,5 @@ void CallgrindToolRunner::cleanupTempFile() m_hostOutputFile.clear(); } -void CallgrindToolRunner::setValgrindRunnable(const Runnable &runnable) -{ - m_valgrindRunnable = runnable; -} - } // Internal } // Valgrind diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h index d6357417cab..21f7abcce0a 100644 --- a/src/plugins/valgrind/callgrindengine.h +++ b/src/plugins/valgrind/callgrindengine.h @@ -48,10 +48,10 @@ public: Valgrind::Callgrind::ParseData *takeParserData(); /// controller actions - void dump(); - void reset(); - void pause(); - void unpause(); + void dump() { run(Dump); } + void reset() { run(ResetEventCounters); } + void pause() { run(Pause); } + void unpause() { run(UnPause); } /// marks the callgrind process as paused /// calls pause() and unpause() if there's an active run @@ -80,21 +80,16 @@ private: void slotFinished(); void showStatusMessage(const QString &message); - void triggerParse(); - void controllerFinished(Option option); - - void run(Option option); - /** * Make data file available locally, triggers @c localParseDataAvailable. * * If the valgrind process was run remotely, this transparently * downloads the data file first and returns a local path. */ - void getLocalDataFile(); - void setValgrindPid(qint64 pid); - void setValgrindRunnable(const ProjectExplorer::Runnable &runnable); - void setValgrindOutputFile(const Utils::FilePath &output) { m_valgrindOutputFile = output; } + void triggerParse(); + void controllerFinished(Option option); + + void run(Option option); void cleanupTempFile(); void controllerProcessDone(); From 4ab935349fc2b953468e7475bd943a11da46baad Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 23 May 2022 12:33:01 +0200 Subject: [PATCH 58/58] LanguageClient: disable diagnostics after document changes Calculating new diagnostics after a document change can take a while. The old ClangCodeModel disabled diagnostics (changed the color to gray) that were reported before the last document change after some time. Adapt this behavior to Clangd and other LSP based codemodels. Change-Id: I0589ba0d0c023f6e7ea3e97fc5b45eb156ddcd37 Reviewed-by: Christian Kandeler --- src/plugins/languageclient/client.cpp | 1 + .../languageclient/diagnosticmanager.cpp | 30 +++++++++++++------ .../languageclient/diagnosticmanager.h | 10 ++++++- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index b77961bcf8d..e997a564c5d 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -918,6 +918,7 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, { if (!d->m_openedDocument.contains(document) || !reachable()) return; + d->m_diagnosticManager->disableDiagnostics(document); const QString method(DidChangeTextDocumentNotification::methodName); TextDocumentSyncKind syncKind = d->m_serverCapabilities.textDocumentSyncKindHelper(); if (Utils::optional registered = d->m_dynamicCapabilities.isRegistered(method)) { diff --git a/src/plugins/languageclient/diagnosticmanager.cpp b/src/plugins/languageclient/diagnosticmanager.cpp index b80ea70b95e..4ebb7f6c304 100644 --- a/src/plugins/languageclient/diagnosticmanager.cpp +++ b/src/plugins/languageclient/diagnosticmanager.cpp @@ -90,7 +90,7 @@ void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath) for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc)) editor->editorWidget()->setExtraSelections(m_extraSelectionsId, {}); } - qDeleteAll(m_marks.take(filePath)); + m_marks.remove(filePath); } QList DiagnosticManager::filteredDiagnostics(const QList &diagnostics) const @@ -98,6 +98,17 @@ QList DiagnosticManager::filteredDiagnostics(const QList return diagnostics; } +void DiagnosticManager::disableDiagnostics(TextEditor::TextDocument *document) +{ + + Marks &marks = m_marks[document->filePath()]; + if (!marks.enabled) + return; + for (TextEditor::TextMark *mark : marks.marks) + mark->setColor(Utils::Theme::Color::IconsDisabledColor); + marks.enabled = false; +} + void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version) { const FilePath &filePath = uri.toFilePath(); @@ -106,7 +117,7 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version) const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(uri); if (versionedDiagnostics.version.value_or(version) == version && !versionedDiagnostics.diagnostics.isEmpty()) { - QList &marks = m_marks[filePath]; + Marks &marks = m_marks[filePath]; const bool isProjectFile = m_client->project() && m_client->project()->isKnownFile(filePath); for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) { @@ -115,9 +126,9 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version) if (!selection.cursor.isNull()) extraSelections << selection; if (TextEditor::TextMark *mark = createTextMark(filePath, diagnostic, isProjectFile)) - marks.append(mark); + marks.marks.append(mark); } - if (!marks.isEmpty()) + if (!marks.marks.isEmpty()) emit textMarkCreated(filePath); } @@ -170,11 +181,7 @@ void DiagnosticManager::clearDiagnostics() for (const DocumentUri &uri : m_diagnostics.keys()) hideDiagnostics(uri.toFilePath()); m_diagnostics.clear(); - if (!QTC_GUARD(m_marks.isEmpty())) { - for (const QList &marks : qAsConst(m_marks)) - qDeleteAll(marks); - m_marks.clear(); - } + QTC_ASSERT(m_marks.isEmpty(), m_marks.clear()); } QList DiagnosticManager::diagnosticsAt(const DocumentUri &uri, @@ -218,4 +225,9 @@ bool DiagnosticManager::hasDiagnostics(const TextDocument *doc) const return !it->diagnostics.isEmpty(); } +DiagnosticManager::Marks::~Marks() +{ + qDeleteAll(marks); +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/diagnosticmanager.h b/src/plugins/languageclient/diagnosticmanager.h index c4972524c00..aa229b91c00 100644 --- a/src/plugins/languageclient/diagnosticmanager.h +++ b/src/plugins/languageclient/diagnosticmanager.h @@ -61,6 +61,7 @@ public: virtual QList filteredDiagnostics( const QList &diagnostics) const; + void disableDiagnostics(TextEditor::TextDocument *document); void clearDiagnostics(); QList diagnosticsAt( @@ -91,7 +92,14 @@ private: QList diagnostics; }; QMap m_diagnostics; - QMap> m_marks; + class Marks + { + public: + ~Marks(); + bool enabled = true; + QList marks; + }; + QMap m_marks; Client *m_client; Utils::Id m_extraSelectionsId; };