diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 4fd693b22a0..9c117ea0c5a 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -243,7 +243,8 @@ expected_str DeviceShell::checkCommand(const QByteArray &command) if (out.contains("")) { m_shellScriptState = State::Failed; m_missingFeatures.append(QString::fromUtf8(command)); - return make_unexpected(Tr::tr("Command %1 was not found.").arg(QString::fromUtf8(command))); + return make_unexpected( + Tr::tr("Command \"%1\" was not found.").arg(QString::fromUtf8(command))); } return out; diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 1cd57e581b6..a489db6c1a0 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -508,13 +508,16 @@ std::optional FilePath::refersToExecutableFile(MatchScope matchScope) expected_str FilePath::tmpDir() const { if (needsDevice()) { - const Environment env = deviceEnvironment(); - if (env.hasKey("TMPDIR")) - return withNewPath(env.value("TMPDIR")).cleanPath(); - if (env.hasKey("TEMP")) - return withNewPath(env.value("TEMP")).cleanPath(); - if (env.hasKey("TMP")) - return withNewPath(env.value("TMP")).cleanPath(); + const expected_str env = deviceEnvironmentWithError(); + if (!env) + return make_unexpected(env.error()); + + if (env->hasKey("TMPDIR")) + return withNewPath(env->value("TMPDIR")).cleanPath(); + if (env->hasKey("TEMP")) + return withNewPath(env->value("TEMP")).cleanPath(); + if (env->hasKey("TMP")) + return withNewPath(env->value("TMP")).cleanPath(); if (osType() != OsTypeWindows) return withNewPath("/tmp"); @@ -1707,6 +1710,13 @@ FilePaths FilePath::searchAllInPath(const FilePaths &additionalDirs, } Environment FilePath::deviceEnvironment() const +{ + expected_str env = deviceEnvironmentWithError(); + QTC_ASSERT_EXPECTED(env, return {}); + return *env; +} + +expected_str FilePath::deviceEnvironmentWithError() const { if (needsDevice()) { QTC_ASSERT(s_deviceHooks.environment, return {}); diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 9d7b3ce5862..a35a3a09403 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -164,6 +164,7 @@ public: [[nodiscard]] FilePath relativeChildPath(const FilePath &parent) const; [[nodiscard]] FilePath relativePathFrom(const FilePath &anchor) const; [[nodiscard]] Environment deviceEnvironment() const; + [[nodiscard]] expected_str deviceEnvironmentWithError() const; [[nodiscard]] FilePaths devicePathEnvironmentVariable() const; [[nodiscard]] FilePath withNewPath(const QString &newPath) const; [[nodiscard]] FilePath withNewMappedPath(const FilePath &newPath) const; @@ -301,7 +302,7 @@ public: std::function(const FilePath &)> fileAccess; std::function deviceDisplayName; std::function ensureReachable; - std::function environment; + std::function(const FilePath &)> environment; std::function isSameDevice; std::function(const FilePath &)> localSource; std::function openTerminal; diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp index f1420755c59..6072e168e8a 100644 --- a/src/libs/utils/terminalhooks.cpp +++ b/src/libs/utils/terminalhooks.cpp @@ -6,27 +6,28 @@ #include "externalterminalprocessimpl.h" #include "filepath.h" #include "process.h" +#include "utilstr.h" #include namespace Utils::Terminal { -FilePath defaultShellForDevice(const FilePath &deviceRoot) +expected_str defaultShellForDevice(const FilePath &deviceRoot) { if (deviceRoot.osType() == OsTypeWindows) return deviceRoot.withNewPath("cmd.exe").searchInPath(); - const Environment env = deviceRoot.deviceEnvironment(); - if (!env.hasChanges()) - return {}; + const expected_str env = deviceRoot.deviceEnvironmentWithError(); + if (!env) + return make_unexpected(env.error()); - FilePath shell = FilePath::fromUserInput(env.value_or("SHELL", "/bin/sh")); + FilePath shell = FilePath::fromUserInput(env->value_or("SHELL", "/bin/sh")); if (!shell.isAbsolutePath()) - shell = env.searchInPath(shell.nativePath()); + shell = env->searchInPath(shell.nativePath()); if (shell.isEmpty()) - return shell; + return make_unexpected(Tr::tr("Could not find any shell")); return deviceRoot.withNewMappedPath(shell); } diff --git a/src/libs/utils/terminalhooks.h b/src/libs/utils/terminalhooks.h index f5b55111c1e..449c23daa93 100644 --- a/src/libs/utils/terminalhooks.h +++ b/src/libs/utils/terminalhooks.h @@ -71,7 +71,7 @@ struct NameAndCommandLine CommandLine commandLine; }; -QTCREATOR_UTILS_EXPORT FilePath defaultShellForDevice(const FilePath &deviceRoot); +QTCREATOR_UTILS_EXPORT expected_str defaultShellForDevice(const FilePath &deviceRoot); class QTCREATOR_UTILS_EXPORT Hooks { diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index cdb2e8b95e8..7129c6acd14 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -302,7 +303,7 @@ public: RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - bool updateContainerAccess(); + expected_str updateContainerAccess(); void changeMounts(QStringList newMounts); bool ensureReachable(const FilePath &other); void shutdown(); @@ -314,7 +315,7 @@ public: QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); } QString dockerImageId() const { return deviceSettings->imageId(); } - Environment environment(); + expected_str environment(); CommandLine withDockerExecCmd(const CommandLine &cmd, const std::optional &env = std::nullopt, @@ -329,7 +330,7 @@ public: expected_str createContainer(); expected_str startContainer(); void stopCurrentContainer(); - void fetchSystemEnviroment(); + expected_str fetchSystemEnviroment(); std::optional clangdExecutable() const { @@ -587,32 +588,43 @@ DockerDevice::DockerDevice(std::unique_ptr deviceSettings) setMachineType(IDevice::Hardware); setAllowEmptyCommand(true); - setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { + setOpenTerminal([this](const Environment &env, + const FilePath &workingDir) -> expected_str { Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below. - if (!updateContainerAccess()) - return; - if (d->containerId().isEmpty()) { - MessageManager::writeDisrupting(Tr::tr("Error starting remote shell. No container.")); - return; - } + expected_str result = d->updateContainerAccess(); + + if (!result) + return result; + + if (d->containerId().isEmpty()) + return make_unexpected(Tr::tr("Error starting remote shell. No container.")); + + expected_str shell = Terminal::defaultShellForDevice(rootPath()); + if (!shell) + return make_unexpected(shell.error()); Process proc; proc.setTerminalMode(TerminalMode::Detached); proc.setEnvironment(env); proc.setWorkingDirectory(workingDir); - proc.setCommand({Terminal::defaultShellForDevice(rootPath()), {}}); + proc.setCommand({*shell, {}}); proc.start(); - if (proc.error() != QProcess::UnknownError && MessageManager::instance()) { - MessageManager::writeDisrupting( - Tr::tr("Error starting remote shell: %1").arg(proc.errorString())); - } + return {}; }); - addDeviceAction({Tr::tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) { - device->openTerminal(device->systemEnvironment(), FilePath()); - }}); + addDeviceAction( + {Tr::tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) { + expected_str env = device->systemEnvironmentWithError(); + if (!env) { + QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error"), env.error()); + return; + } + expected_str result = device->openTerminal(*env, FilePath()); + if (!result) + QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error"), result.error()); + }}); } DockerDevice::~DockerDevice() @@ -625,7 +637,7 @@ void DockerDevice::shutdown() d->shutdown(); } -bool DockerDevice::updateContainerAccess() const +expected_str DockerDevice::updateContainerAccess() const { return d->updateContainerAccess(); } @@ -909,10 +921,10 @@ expected_str DockerDevicePrivate::startContainer() return m_shell->start(); } -bool DockerDevicePrivate::updateContainerAccess() +expected_str DockerDevicePrivate::updateContainerAccess() { if (QThread::currentThread() != thread()) { - bool result = false; + expected_str result; QMetaObject::invokeMethod(this, &DockerDevicePrivate::updateContainerAccess, Qt::BlockingQueuedConnection, @@ -921,23 +933,23 @@ bool DockerDevicePrivate::updateContainerAccess() } if (m_isShutdown) - return false; + return make_unexpected(Tr::tr("Device is shutdown")); if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false) - return false; + return make_unexpected(Tr::tr("Docker system is not reachable")); - if (m_shell) - return true; + if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) + return {}; - auto result = startContainer(); + expected_str result = startContainer(); if (result) { deviceSettings->containerStatus.setText(Tr::tr("Running")); - return true; + return result; } - qCWarning(dockerDeviceLog) << "Failed to start container:" << result.error(); - deviceSettings->containerStatus.setText(result.error()); - return false; + const QString error = QString("Failed to start container: %1").arg(result.error()); + deviceSettings->containerStatus.setText(result.error().trimmed()); + return make_unexpected(error); } void DockerDevice::setMounts(const QStringList &mounts) const @@ -1025,7 +1037,7 @@ expected_str DockerDevice::localSource(const FilePath &other) const return d->localSource(other); } -Environment DockerDevice::systemEnvironment() const +expected_str DockerDevice::systemEnvironmentWithError() const { return d->environment(); } @@ -1036,29 +1048,34 @@ void DockerDevice::aboutToBeRemoved() const detector.undoAutoDetect(id().toString()); } -void DockerDevicePrivate::fetchSystemEnviroment() +expected_str DockerDevicePrivate::fetchSystemEnviroment() { - if (!updateContainerAccess()) - return; + expected_str result = updateContainerAccess(); + if (!result) + return result; + + QString stdErr; if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) { const RunResult result = runInShell({"env", {}}); const QString out = QString::fromUtf8(result.stdOut); m_cachedEnviroment = Environment(out.split('\n', Qt::SkipEmptyParts), q->osType()); - return; + stdErr = QString::fromUtf8(result.stdErr); + } else { + Process proc; + proc.setCommand(withDockerExecCmd({"env", {}})); + proc.start(); + proc.waitForFinished(); + const QString remoteOutput = proc.cleanedStdOut(); + + m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType()); + stdErr = proc.cleanedStdErr(); } - Process proc; - proc.setCommand(withDockerExecCmd({"env", {}})); - proc.start(); - proc.waitForFinished(); - const QString remoteOutput = proc.cleanedStdOut(); + if (stdErr.isEmpty()) + return {}; - m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType()); - - const QString remoteError = proc.cleanedStdErr(); - if (!remoteError.isEmpty()) - qCWarning(dockerDeviceLog) << "Cannot read container environment:", qPrintable(remoteError); + return make_unexpected("Could not read container environment: " + stdErr); } RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData) @@ -1313,10 +1330,13 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath return true; } -Environment DockerDevicePrivate::environment() +expected_str DockerDevicePrivate::environment() { - if (!m_cachedEnviroment) - fetchSystemEnviroment(); + if (!m_cachedEnviroment) { + expected_str result = fetchSystemEnviroment(); + if (!result) + return make_unexpected(result.error()); + } QTC_ASSERT(m_cachedEnviroment, return {}); return m_cachedEnviroment.value(); diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 477de04168a..ab88d0b9d00 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -74,9 +74,9 @@ public: bool ensureReachable(const Utils::FilePath &other) const override; Utils::expected_str localSource(const Utils::FilePath &other) const override; - Utils::Environment systemEnvironment() const override; + Utils::expected_str systemEnvironmentWithError() const override; - bool updateContainerAccess() const; + Utils::expected_str updateContainerAccess() const; void setMounts(const QStringList &mounts) const; bool prepareForBuild(const ProjectExplorer::Target *target) override; diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp index fbb80bf640e..01d80106b68 100644 --- a/src/plugins/docker/dockerdevicewidget.cpp +++ b/src/plugins/docker/dockerdevicewidget.cpp @@ -106,7 +106,13 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) this, [this, logView, dockerDevice, searchPaths, deviceSettings] { logView->clear(); - dockerDevice->updateContainerAccess(); + expected_str startResult = dockerDevice->updateContainerAccess(); + + if (!startResult) { + logView->append(Tr::tr("Failed to start container.")); + logView->append(startResult.error()); + return; + } const FilePath clangdPath = dockerDevice->filePath("clangd") diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index cc9649cfca7..08880aff3b7 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -55,17 +55,21 @@ DesktopDevice::DesktopDevice() = QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END); setFreePorts(Utils::PortList::fromString(portRange)); - setOpenTerminal([](const Environment &env, const FilePath &path) { + setOpenTerminal([](const Environment &env, const FilePath &path) -> expected_str { const Environment realEnv = env.hasChanges() ? env : Environment::systemEnvironment(); - const FilePath shell = Terminal::defaultShellForDevice(path); + const expected_str shell = Terminal::defaultShellForDevice(path); + if (!shell) + return make_unexpected(shell.error()); Process process; process.setTerminalMode(TerminalMode::Detached); process.setEnvironment(realEnv); - process.setCommand({shell, {}}); + process.setCommand({*shell, {}}); process.setWorkingDirectory(path); process.start(); + + return {}; }); } @@ -117,7 +121,7 @@ FilePath DesktopDevice::filePath(const QString &pathOnDevice) const return FilePath::fromParts({}, {}, pathOnDevice); } -Environment DesktopDevice::systemEnvironment() const +expected_str DesktopDevice::systemEnvironmentWithError() const { return Environment::systemEnvironment(); } diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index fd9b81f123c..437a4f8c074 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -31,7 +31,7 @@ public: bool usableAsBuildDevice() const override; bool handlesFile(const Utils::FilePath &filePath) const override; - Utils::Environment systemEnvironment() const override; + Utils::expected_str systemEnvironmentWithError() const override; Utils::FilePath rootPath() const override; Utils::FilePath filePath(const QString &pathOnDevice) const override; diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 83f07a70792..57052d354f6 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -425,10 +425,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniquefileAccess(); }; - deviceHooks.environment = [](const FilePath &filePath) { + deviceHooks.environment = [](const FilePath &filePath) -> expected_str { auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, qDebug() << filePath.toString(); return Environment{}); - return device->systemEnvironment(); + if (!device) { + return make_unexpected( + Tr::tr("No device found for path \"%1\"").arg(filePath.toUserOutput())); + } + return device->systemEnvironmentWithError(); }; deviceHooks.deviceDisplayName = [](const FilePath &filePath) { diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 85d32b7a0eb..05c013c4bd5 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -226,10 +226,11 @@ bool IDevice::canOpenTerminal() const return bool(d->openTerminal); } -void IDevice::openTerminal(const Environment &env, const FilePath &workingDir) const +expected_str IDevice::openTerminal(const Environment &env, const FilePath &workingDir) const { - QTC_ASSERT(canOpenTerminal(), return); - d->openTerminal(env, workingDir); + QTC_ASSERT(canOpenTerminal(), + return make_unexpected(Tr::tr("Opening a terminal is not supported."))); + return d->openTerminal(env, workingDir); } bool IDevice::isEmptyCommandAllowed() const @@ -302,6 +303,13 @@ FileTransferInterface *IDevice::createFileTransferInterface( } Environment IDevice::systemEnvironment() const +{ + expected_str env = systemEnvironmentWithError(); + QTC_ASSERT_EXPECTED(env, return {}); + return *env; +} + +expected_str IDevice::systemEnvironmentWithError() const { DeviceFileAccess *access = fileAccess(); QTC_ASSERT(access, return Environment::systemEnvironment()); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index fb9366860e4..a6f9d860b84 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -192,7 +192,8 @@ public: void setupId(Origin origin, Utils::Id id = Utils::Id()); bool canOpenTerminal() const; - void openTerminal(const Utils::Environment &env, const Utils::FilePath &workingDir) const; + Utils::expected_str openTerminal(const Utils::Environment &env, + const Utils::FilePath &workingDir) const; bool isEmptyCommandAllowed() const; void setAllowEmptyCommand(bool allow); @@ -212,7 +213,9 @@ public: virtual Utils::ProcessInterface *createProcessInterface() const; virtual FileTransferInterface *createFileTransferInterface( const FileTransferSetupData &setup) const; - virtual Utils::Environment systemEnvironment() const; + + Utils::Environment systemEnvironment() const; + virtual Utils::expected_str systemEnvironmentWithError() const; virtual void aboutToBeRemoved() const {} @@ -230,7 +233,8 @@ protected: virtual void fromMap(const Utils::Store &map); virtual Utils::Store toMap() const; - using OpenTerminal = std::function; + using OpenTerminal = std::function(const Utils::Environment &, + const Utils::FilePath &)>; void setOpenTerminal(const OpenTerminal &openTerminal); void setDisplayType(const QString &type); void setOsType(Utils::OsType osType); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 6a4eba09116..98c8690900c 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -104,6 +104,7 @@ #include #include #include +#include #include #include #include @@ -3641,13 +3642,18 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env && !buildDevice->ensureReachable(workingDir)) workingDir.clear(); - const FilePath shell = Terminal::defaultShellForDevice(buildDevice->rootPath()); + const expected_str shell = Terminal::defaultShellForDevice(buildDevice->rootPath()); - if (!shell.isEmpty() && buildDevice->rootPath().needsDevice()) { - Terminal::Hooks::instance().openTerminal({CommandLine{shell, {}}, workingDir, environment}); - } else { - Terminal::Hooks::instance().openTerminal({workingDir, environment}); + if (!shell) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed opening terminal.\n%1").arg(shell.error())); + return; } + + if (buildDevice->rootPath().needsDevice()) + Terminal::Hooks::instance().openTerminal({CommandLine{*shell, {}}, workingDir, environment}); + else + Terminal::Hooks::instance().openTerminal({workingDir, environment}); } void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() @@ -3676,10 +3682,17 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() if (!device->filePath(workingDir.path()).exists() && !device->ensureReachable(workingDir)) workingDir.clear(); - const FilePath shell = Terminal::defaultShellForDevice(device->rootPath()); - if (!shell.isEmpty() && device->rootPath().needsDevice()) { + const expected_str shell = Terminal::defaultShellForDevice(device->rootPath()); + + if (!shell) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed opening terminal.\n%1").arg(shell.error())); + return; + } + + if (device->rootPath().needsDevice()) { Terminal::Hooks::instance().openTerminal( - {CommandLine{shell, {}}, workingDir, runnable.environment}); + {CommandLine{*shell, {}}, workingDir, runnable.environment}); } else { Terminal::Hooks::instance().openTerminal({workingDir, runnable.environment}); } diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 1b4af90f912..8386175e82c 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -971,7 +972,8 @@ LinuxDevice::LinuxDevice() } }}); - setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { + setOpenTerminal([this](const Environment &env, + const FilePath &workingDir) -> expected_str { Process proc; // If we will not set any environment variables, we can leave out the shell executable @@ -985,10 +987,15 @@ LinuxDevice::LinuxDevice() proc.setEnvironment(env); proc.setWorkingDirectory(workingDir); proc.start(); + + return {}; }); addDeviceAction({Tr::tr("Open Remote Shell"), [](const IDevice::Ptr &device, QWidget *) { - device->openTerminal(Environment(), FilePath()); + expected_str result = device->openTerminal(Environment(), FilePath()); + + if (!result) + QMessageBox::warning(nullptr, Tr::tr("Error"), result.error()); }}); } diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 681ad9c538f..604a0d82bc8 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -87,25 +87,28 @@ void TerminalWidget::setupPty() if (shellCommand.executable().isRootPath()) { writeToTerminal(Tr::tr("Connecting ...\r\n").toUtf8(), true); // We still have to find the shell to start ... - m_findShellWatcher.reset(new QFutureWatcher()); + m_findShellWatcher.reset(new QFutureWatcher>()); connect(m_findShellWatcher.get(), &QFutureWatcher::finished, this, [this] { - const FilePath result = m_findShellWatcher->result(); - if (!result.isEmpty()) { - m_openParameters.shellCommand->setExecutable(m_findShellWatcher->result()); + const expected_str result = m_findShellWatcher->result(); + if (result) { + m_openParameters.shellCommand->setExecutable(*result); restart(m_openParameters); return; } - writeToTerminal( - ("\r\n\033[31m" + Tr::tr("Could not find shell to start.") + "\r\n").toUtf8(), true); + writeToTerminal(("\r\n\033[31m" + + Tr::tr("Failed to start shell: %1").arg(result.error()) + "\r\n") + .toUtf8(), + true); }); - m_findShellWatcher->setFuture(Utils::asyncRun([shellCommand] { - const FilePath result = Utils::Terminal::defaultShellForDevice( + m_findShellWatcher->setFuture(Utils::asyncRun([shellCommand]() -> expected_str { + const expected_str result = Utils::Terminal::defaultShellForDevice( shellCommand.executable()); - if (result.isExecutableFile()) - return result; - return FilePath{}; + if (result && !result->isExecutableFile()) + return make_unexpected( + Tr::tr("'%1' is not executable.").arg(result->toUserOutput())); + return result; })); return; diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index b696d8af1c0..e30366bf0cc 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -110,7 +110,7 @@ private: Internal::ShortcutMap m_shortcutMap; - std::unique_ptr> m_findShellWatcher; + std::unique_ptr>> m_findShellWatcher; }; } // namespace Terminal