docker: Correctly initialize Kit during autodetection

* Added returning the Id after detecting CMake so it can be set in the autodetected kit
* Trying to keep autodetect from adding the same Qt installation twice if qmake is linked in /bin and /usr/bin
* Added FIXME for RunControlPrivate::runConfiguration as it is used after free in rare cases
* Fixed IosCompilerDetector to not just run if a device is set
* Fixed QnxCompilerDetector to not just run if a device is set
* Fixed auto-detected debuggers not being set as auto-detected, as they now can be removed from the device screen

Change-Id: Ia7772c454d70e147e4326efacc4a6a888fa26782
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Marcus Tillmanns
2022-04-07 14:04:20 +02:00
parent aa3f467ee2
commit 2c4553366c
13 changed files with 113 additions and 54 deletions

View File

@@ -87,8 +87,6 @@ static Id defaultCMakeToolId()
return defaultTool ? defaultTool->id() : Id();
}
const char TOOL_ID[] = "CMakeProjectManager.CMakeKitInformation";
class CMakeKitAspectWidget final : public KitAspectWidget
{
Q_DECLARE_TR_FUNCTIONS(CMakeProjectManager::Internal::CMakeKitAspect)
@@ -218,7 +216,7 @@ private:
CMakeKitAspect::CMakeKitAspect()
{
setObjectName(QLatin1String("CMakeKitAspect"));
setId(TOOL_ID);
setId(Constants::TOOL_ID);
setDisplayName(tr("CMake Tool"));
setDescription(tr("The CMake Tool to use when building a project with CMake.<br>"
"This setting is ignored when using other build systems."));
@@ -235,14 +233,14 @@ CMakeKitAspect::CMakeKitAspect()
Id CMakeKitAspect::id()
{
return TOOL_ID;
return Constants::TOOL_ID;
}
Id CMakeKitAspect::cmakeToolId(const Kit *k)
{
if (!k)
return {};
return Id::fromSetting(k->value(TOOL_ID));
return Id::fromSetting(k->value(Constants::TOOL_ID));
}
CMakeTool *CMakeKitAspect::cmakeTool(const Kit *k)
@@ -255,7 +253,7 @@ void CMakeKitAspect::setCMakeTool(Kit *k, const Id id)
const Id toSet = id.isValid() ? id : defaultCMakeToolId();
QTC_ASSERT(!id.isValid() || CMakeToolManager::findById(toSet), return);
if (k)
k->setValue(TOOL_ID, toSet.toSetting());
k->setValue(Constants::TOOL_ID, toSet.toSetting());
}
Tasks CMakeKitAspect::validate(const Kit *k) const

View File

@@ -65,5 +65,9 @@ const char CMAKE_BUILD_STEP_ID[] = "CMakeProjectManager.MakeStep";
// Features
const char CMAKE_FEATURE_ID[] = "CMakeProjectManager.Wizard.FeatureCMake";
// Tool
const char TOOL_ID[] = "CMakeProjectManager.CMakeKitInformation";
} // namespace Constants
} // namespace CMakeProjectManager

View File

@@ -37,6 +37,7 @@
#include <QDir>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
#include <QRegularExpression>
#include <QSet>
#include <QUuid>
@@ -47,6 +48,9 @@ using namespace Utils;
namespace CMakeProjectManager {
static Q_LOGGING_CATEGORY(cmakeToolLog, "qtc.cmake.tool", QtWarningMsg);
const char CMAKE_INFORMATION_ID[] = "Id";
const char CMAKE_INFORMATION_COMMAND[] = "Binary";
const char CMAKE_INFORMATION_DISPLAYNAME[] = "DisplayName";
@@ -521,6 +525,7 @@ void CMakeTool::fetchFromCapabilities() const
m_introspection->m_didRun = true;
parseFromCapabilities(cmake.stdOut());
} else {
qCCritical(cmakeToolLog) << "Fetching capabilities failed: " << cmake.allOutput() << cmake.error();
m_introspection->m_didRun = false;
}
}

View File

@@ -180,36 +180,44 @@ void CMakeToolManager::updateDocumentation()
Core::HelpManager::registerDocumentation(docs);
}
void CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
const QString &detectionSource,
QString *logMessage)
{
QList<Id> result;
QStringList messages{tr("Searching CMake binaries...")};
for (const FilePath &path : searchPaths) {
const FilePath cmake = path.pathAppended("cmake").withExecutableSuffix();
if (cmake.isExecutableFile()) {
registerCMakeByPath(cmake, detectionSource);
const Id currentId = registerCMakeByPath(cmake, detectionSource);
if (currentId.isValid())
result.push_back(currentId);
messages.append(tr("Found \"%1\"").arg(cmake.toUserOutput()));
}
}
if (logMessage)
*logMessage = messages.join('\n');
return result;
}
void CMakeToolManager::registerCMakeByPath(const FilePath &cmakePath, const QString &detectionSource)
Id CMakeToolManager::registerCMakeByPath(const FilePath &cmakePath, const QString &detectionSource)
{
const Id id = Id::fromString(cmakePath.toUserOutput());
Id id = Id::fromString(cmakePath.toUserOutput());
CMakeTool *cmakeTool = findById(id);
if (cmakeTool)
return;
return cmakeTool->id();
auto newTool = std::make_unique<CMakeTool>(CMakeTool::ManualDetection, id);
newTool->setFilePath(cmakePath);
newTool->setDetectionSource(detectionSource);
newTool->setDisplayName(cmakePath.toUserOutput());
id = newTool->id();
registerCMakeTool(std::move(newTool));
return id;
}
void CMakeToolManager::removeDetectedCMake(const QString &detectionSource, QString *logMessage)

View File

@@ -63,10 +63,10 @@ public:
static void updateDocumentation();
public slots:
void autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
const QString &detectionSource,
QString *logMessage);
void registerCMakeByPath(const Utils::FilePath &cmakePath,
Utils::Id registerCMakeByPath(const Utils::FilePath &cmakePath,
const QString &detectionSource);
void removeDetectedCMake(const QString &detectionSource, QString *logMessage);
void listDetectedCMake(const QString &detectionSource, QString *logMessage);

View File

@@ -796,9 +796,7 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s
DebuggerItem item;
item.createId();
item.setDetectionSource(detectionSource);
// Intentionally set items with non-empty source as manual for now to
// give the user a chance to remove them. FIXME: Think of a better way.
item.setAutoDetected(detectionSource.isEmpty());
item.setAutoDetected(true);
item.setCommand(command);
item.reinitializeFromFile();
if (item.engineType() == NoEngineType)
@@ -937,7 +935,8 @@ void DebuggerItemManagerPrivate::readDebuggers(const FilePath &fileName, bool is
.arg(item.command().toUserOutput(), item.id().toString(), fileName.toUserOutput());
continue;
}
if (!item.command().isExecutableFile()) {
// FIXME: During startup, devices are not yet available, so we cannot check if the file still exists.
if (!item.command().needsDevice() && !item.command().isExecutableFile()) {
qWarning() << QString("DebuggerItem \"%1\" (%2) read from \"%3\" dropped since the command is not executable.")
.arg(item.command().toUserOutput(), item.id().toString(), fileName.toUserOutput());
continue;

View File

@@ -74,21 +74,31 @@ bool DockerApi::canConnect()
return result;
}
void DockerApi::checkCanConnect()
void DockerApi::checkCanConnect(bool async)
{
std::unique_lock lk(m_daemonCheckGuard, std::try_to_lock);
if (!lk.owns_lock())
return;
if (async) {
std::unique_lock lk(m_daemonCheckGuard, std::try_to_lock);
if (!lk.owns_lock())
return;
m_dockerDaemonAvailable = nullopt;
dockerDaemonAvailableChanged();
auto future = Utils::runAsync([lk = std::move(lk), this] {
m_dockerDaemonAvailable = canConnect();
m_dockerDaemonAvailable = nullopt;
dockerDaemonAvailableChanged();
});
Core::ProgressManager::addTask(future, tr("Checking docker daemon"), "DockerPlugin");
auto future = Utils::runAsync([lk = std::move(lk), this] {
m_dockerDaemonAvailable = canConnect();
dockerDaemonAvailableChanged();
});
Core::ProgressManager::addTask(future, tr("Checking docker daemon"), "DockerPlugin");
return;
}
std::unique_lock lk(m_daemonCheckGuard);
bool isAvailable = canConnect();
if (!m_dockerDaemonAvailable.has_value() || isAvailable != m_dockerDaemonAvailable) {
m_dockerDaemonAvailable = isAvailable;
dockerDaemonAvailableChanged();
}
}
void DockerApi::recheckDockerDaemon()
@@ -97,17 +107,17 @@ void DockerApi::recheckDockerDaemon()
s_instance->checkCanConnect();
}
Utils::optional<bool> DockerApi::dockerDaemonAvailable()
Utils::optional<bool> DockerApi::dockerDaemonAvailable(bool async)
{
if (!m_dockerDaemonAvailable.has_value())
checkCanConnect();
checkCanConnect(async);
return m_dockerDaemonAvailable;
}
Utils::optional<bool> DockerApi::isDockerDaemonAvailable()
Utils::optional<bool> DockerApi::isDockerDaemonAvailable(bool async)
{
QTC_ASSERT(s_instance, return nullopt);
return s_instance->dockerDaemonAvailable();
return s_instance->dockerDaemonAvailable(async);
}
FilePath DockerApi::findDockerClient()

View File

@@ -45,15 +45,15 @@ public:
static DockerApi *instance();
bool canConnect();
void checkCanConnect();
void checkCanConnect(bool async = true);
static void recheckDockerDaemon();
signals:
void dockerDaemonAvailableChanged();
public:
Utils::optional<bool> dockerDaemonAvailable();
static Utils::optional<bool> isDockerDaemonAvailable();
Utils::optional<bool> dockerDaemonAvailable(bool async = true);
static Utils::optional<bool> isDockerDaemonAvailable(bool async = true);
private:
Utils::FilePath findDockerClient();

View File

@@ -317,7 +317,7 @@ void DockerDevice::updateContainerAccess() const
void DockerDevicePrivate::stopCurrentContainer()
{
if (m_container.isEmpty() || !DockerApi::isDockerDaemonAvailable().value_or(false))
if (m_container.isEmpty() || !DockerApi::isDockerDaemonAvailable(false).value_or(false))
return;
if (m_shell) {
@@ -439,7 +439,7 @@ void DockerDevicePrivate::updateContainerAccess()
if (!m_container.isEmpty())
return;
if (DockerApi::isDockerDaemonAvailable().value_or(true) == false)
if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false)
return;
if (m_shell)
@@ -897,7 +897,7 @@ bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray
void DockerDevice::runProcess(QtcProcess &process) const
{
updateContainerAccess();
if (!DockerApi::isDockerDaemonAvailable().value_or(false))
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return;
if (d->m_container.isEmpty()) {
LOG("No container set to run " << process.commandLine().toUserOutput());
@@ -977,7 +977,7 @@ void DockerDevicePrivate::fetchSystemEnviroment()
bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
{
if (!DockerApi::isDockerDaemonAvailable().value_or(false))
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return false;
CommandLine dcmd{"docker", {"exec", m_container}};
dcmd.addCommandLineAsArgs(cmd);
@@ -995,7 +995,7 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const
{
if (!QTC_GUARD(DockerApi::isDockerDaemonAvailable().value_or(false))) {
if (!QTC_GUARD(DockerApi::isDockerDaemonAvailable(false).value_or(false))) {
LOG("No daemon. Could not run " << cmd.toUserOutput());
return false;
}
@@ -1021,7 +1021,7 @@ static QByteArray randomHex()
QByteArray DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const
{
if (!DockerApi::isDockerDaemonAvailable().value_or(false))
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return {};
QTC_ASSERT(m_shell && m_shell->isRunning(), return {});
QMutexLocker l(&m_shellMutex);

View File

@@ -27,6 +27,8 @@
#include "dockerconstants.h"
#include <cmakeprojectmanager/cmakeprojectconstants.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/toolchain.h>
@@ -39,6 +41,7 @@
#include <utils/filepath.h>
#include <utils/qtcassert.h>
#include <utils/algorithm.h>
#include <QApplication>
@@ -69,7 +72,7 @@ public:
private:
QtVersions autoDetectQtVersions() const;
QList<ToolChain *> autoDetectToolChains();
void autoDetectCMake();
QList<Id> autoDetectCMake();
void autoDetectDebugger();
KitDetector *q;
@@ -213,12 +216,21 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const
const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) {
if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake,
false,
m_sharedId,
&error)) {
qtVersions.append(qtVersion);
QtVersionManager::addVersion(qtVersion);
emit q->logOutput(tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput()));
false,
m_sharedId,
&error)) {
if (qtVersion->isValid()) {
if (!Utils::anyOf(qtVersions,
[qtVersion](QtVersion* other) {
return qtVersion->mkspecPath() == other->mkspecPath();
})) {
qtVersions.append(qtVersion);
QtVersionManager::addVersion(qtVersion);
emit q->logOutput(
tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput()));
}
}
}
return true;
};
@@ -265,20 +277,24 @@ Toolchains KitDetectorPrivate::autoDetectToolChains()
return allNewToolChains;
}
void KitDetectorPrivate::autoDetectCMake()
QList<Id> KitDetectorPrivate::autoDetectCMake()
{
QList<Id> result;
QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager");
if (!cmakeManager)
return;
return {};
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"autoDetectCMakeForDevice",
Q_RETURN_ARG(QList<Utils::Id>, result),
Q_ARG(Utils::FilePaths, m_searchPaths),
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
return result;
}
void KitDetectorPrivate::autoDetectDebugger()
@@ -308,16 +324,21 @@ void KitDetectorPrivate::autoDetect()
const Toolchains toolchains = autoDetectToolChains();
const QtVersions qtVersions = autoDetectQtVersions();
autoDetectCMake();
const QList<Id> cmakeIds = autoDetectCMake();
const Id cmakeId = cmakeIds.empty() ? Id() : cmakeIds.first();
autoDetectDebugger();
const auto initializeKit = [this, toolchains, qtVersions](Kit *k) {
const auto initializeKit = [this, toolchains, qtVersions, cmakeId](Kit *k) {
k->setAutoDetected(false);
k->setAutoDetectionSource(m_sharedId);
k->setUnexpandedDisplayName("%{Device:Name}");
if (cmakeId.isValid())
k->setValue(CMakeProjectManager::Constants::TOOL_ID, cmakeId.toSetting());
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE);
DeviceKitAspect::setDevice(k, m_device);
BuildDeviceKitAspect::setDevice(k, m_device);
QtVersion *qt = nullptr;
if (!qtVersions.isEmpty()) {
@@ -332,10 +353,14 @@ void KitDetectorPrivate::autoDetect()
for (ToolChain *toolChain : toolchainsToSet)
ToolChainKitAspect::setToolChain(k, toolChain);
if (cmakeId.isValid())
k->setSticky(CMakeProjectManager::Constants::TOOL_ID, true);
k->setSticky(ToolChainKitAspect::id(), true);
k->setSticky(QtSupport::QtKitAspect::id(), true);
k->setSticky(DeviceKitAspect::id(), true);
k->setSticky(DeviceTypeKitAspect::id(), true);
k->setSticky(BuildDeviceKitAspect::id(), true);
};
Kit *kit = KitManager::registerKit(initializeKit);

View File

@@ -590,6 +590,9 @@ IosToolChainFactory::IosToolChainFactory()
Toolchains IosToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
if (detector.device)
return {};
QList<ClangToolChain *> existingClangToolChains = clangToolChains(detector.alreadyKnown);
const QList<XcodePlatform> platforms = XcodeProbe::detectPlatforms().values();
Toolchains toolChains;

View File

@@ -211,8 +211,11 @@ Toolchains ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) const
const ToolChainOperations ops = mergeToolChainLists(systemFileTcs, userFileTcs, autodetectedTcs);
// Process ops:
for (ToolChain *tc : ops.toDemote)
tc->setDetection(ToolChain::ManualDetection);
for (ToolChain *tc : ops.toDemote) {
// FIXME: We currently only demote local toolchains, as they are not redetected.
if (tc->detectionSource().isEmpty())
tc->setDetection(ToolChain::ManualDetection);
}
qDeleteAll(ops.toDelete);

View File

@@ -221,6 +221,10 @@ QnxToolChainFactory::QnxToolChainFactory()
Toolchains QnxToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
// FIXME: Support detecting toolchains on remote devices
if (detector.device)
return {};
Toolchains tcs;
const auto configurations = QnxConfigurationManager::instance()->configurations();
for (QnxConfiguration *configuration : configurations)