/**************************************************************************** ** ** 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 "kitdetector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; using namespace QtSupport; using namespace Utils; namespace Docker { namespace Internal { class KitDetectorPrivate { Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::KitItemDetector) public: KitDetectorPrivate(KitDetector *parent, const IDevice::ConstPtr &device) : q(parent) , m_device(device) {} void autoDetect(); void undoAutoDetect() const; void listAutoDetected() const; void setSharedId(const QString &sharedId) { m_sharedId = sharedId; } void setSearchPaths(const FilePaths &searchPaths) { m_searchPaths = searchPaths; } private: QtVersions autoDetectQtVersions() const; QList autoDetectToolChains(); QList autoDetectCMake(); void autoDetectDebugger(); KitDetector *q; IDevice::ConstPtr m_device; QString m_sharedId; FilePaths m_searchPaths; }; KitDetector::KitDetector(const IDevice::ConstPtr &device) : d(new KitDetectorPrivate(this, device)) {} KitDetector::~KitDetector() { delete d; } void KitDetector::autoDetect(const QString &sharedId, const FilePaths &searchPaths) const { d->setSharedId(sharedId); d->setSearchPaths(searchPaths); d->autoDetect(); } void KitDetector::undoAutoDetect(const QString &sharedId) const { d->setSharedId(sharedId); d->undoAutoDetect(); } void KitDetector::listAutoDetected(const QString &sharedId) const { d->setSharedId(sharedId); d->listAutoDetected(); } void KitDetectorPrivate::undoAutoDetect() const { emit q->logOutput(tr("Start removing auto-detected items associated with this docker image.")); emit q->logOutput('\n' + tr("Removing kits...")); for (Kit *kit : KitManager::kits()) { if (kit->autoDetectionSource() == m_sharedId) { emit q->logOutput(tr("Removed \"%1\"").arg(kit->displayName())); KitManager::deregisterKit(kit); } }; emit q->logOutput('\n' + tr("Removing Qt version entries...")); for (QtVersion *qtVersion : QtVersionManager::versions()) { if (qtVersion->detectionSource() == m_sharedId) { emit q->logOutput(tr("Removed \"%1\"").arg(qtVersion->displayName())); QtVersionManager::removeVersion(qtVersion); } }; emit q->logOutput('\n' + tr("Removing toolchain entries...")); const Toolchains toolchains = ToolChainManager::toolchains(); for (ToolChain *toolChain : toolchains) { if (toolChain && toolChain->detectionSource() == m_sharedId) { emit q->logOutput(tr("Removed \"%1\"").arg(toolChain->displayName())); ToolChainManager::deregisterToolChain(toolChain); } }; if (auto cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager")) { QString logMessage; const bool res = QMetaObject::invokeMethod(cmakeManager, "removeDetectedCMake", Q_ARG(QString, m_sharedId), Q_ARG(QString *, &logMessage)); QTC_CHECK(res); emit q->logOutput('\n' + logMessage); } if (auto debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin")) { QString logMessage; const bool res = QMetaObject::invokeMethod(debuggerPlugin, "removeDetectedDebuggers", Q_ARG(QString, m_sharedId), Q_ARG(QString *, &logMessage)); QTC_CHECK(res); emit q->logOutput('\n' + logMessage); } emit q->logOutput('\n' + tr("Removal of previously auto-detected kit items finished.") + "\n\n"); } void KitDetectorPrivate::listAutoDetected() const { emit q->logOutput(tr("Start listing auto-detected items associated with this docker image.")); emit q->logOutput('\n' + tr("Kits:")); for (Kit *kit : KitManager::kits()) { if (kit->autoDetectionSource() == m_sharedId) emit q->logOutput(kit->displayName()); } emit q->logOutput('\n' + tr("Qt versions:")); for (QtVersion *qtVersion : QtVersionManager::versions()) { if (qtVersion->detectionSource() == m_sharedId) emit q->logOutput(qtVersion->displayName()); } emit q->logOutput('\n' + tr("Toolchains:")); for (ToolChain *toolChain : ToolChainManager::toolchains()) { if (toolChain->detectionSource() == m_sharedId) emit q->logOutput(toolChain->displayName()); } if (QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName( "CMakeToolManager")) { QString logMessage; const bool res = QMetaObject::invokeMethod(cmakeManager, "listDetectedCMake", Q_ARG(QString, m_sharedId), Q_ARG(QString *, &logMessage)); QTC_CHECK(res); emit q->logOutput('\n' + logMessage); } if (QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName( "DebuggerPlugin")) { QString logMessage; const bool res = QMetaObject::invokeMethod(debuggerPlugin, "listDetectedDebuggers", Q_ARG(QString, m_sharedId), Q_ARG(QString *, &logMessage)); QTC_CHECK(res); emit q->logOutput('\n' + logMessage); } emit q->logOutput('\n' + tr("Listing of previously auto-detected kit items finished.") + "\n\n"); } QtVersions KitDetectorPrivate::autoDetectQtVersions() const { QtVersions qtVersions; QString error; const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) { if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQueryToolPath(qmake, 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->queryToolFilePath().toUserOutput())); } } } return true; }; emit q->logOutput(tr("Searching for qmake executables...")); const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"}; for (const FilePath &searchPath : m_searchPaths) { searchPath.iterateDirectory(handleQmake, {candidates, QDir::Files | QDir::Executable, QDirIterator::Subdirectories}); } if (!error.isEmpty()) emit q->logOutput(tr("Error: %1.").arg(error)); if (qtVersions.isEmpty()) emit q->logOutput(tr("No Qt installation found.")); return qtVersions; } Toolchains KitDetectorPrivate::autoDetectToolChains() { const QList factories = ToolChainFactory::allToolChainFactories(); Toolchains alreadyKnown = ToolChainManager::toolchains(); Toolchains allNewToolChains; QApplication::processEvents(); emit q->logOutput('\n' + tr("Searching toolchains...")); for (ToolChainFactory *factory : factories) { emit q->logOutput(tr("Searching toolchains of type %1").arg(factory->displayName())); const ToolchainDetector detector(alreadyKnown, m_device, m_searchPaths); const Toolchains newToolChains = factory->autoDetect(detector); for (ToolChain *toolChain : newToolChains) { emit q->logOutput(tr("Found \"%1\"").arg(toolChain->compilerCommand().toUserOutput())); toolChain->setDetectionSource(m_sharedId); ToolChainManager::registerToolChain(toolChain); alreadyKnown.append(toolChain); } allNewToolChains.append(newToolChains); } emit q->logOutput(tr("%1 new toolchains found.").arg(allNewToolChains.size())); return allNewToolChains; } QList KitDetectorPrivate::autoDetectCMake() { QList result; QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager"); if (!cmakeManager) return {}; QString logMessage; const bool res = QMetaObject::invokeMethod(cmakeManager, "autoDetectCMakeForDevice", Q_RETURN_ARG(QList, 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() { QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin"); if (!debuggerPlugin) return; QString logMessage; const bool res = QMetaObject::invokeMethod(debuggerPlugin, "autoDetectDebuggersForDevice", Q_ARG(Utils::FilePaths, m_searchPaths), Q_ARG(QString, m_sharedId), Q_ARG(QString *, &logMessage)); QTC_CHECK(res); emit q->logOutput('\n' + logMessage); } void KitDetectorPrivate::autoDetect() { QApplication::setOverrideCursor(Qt::WaitCursor); undoAutoDetect(); emit q->logOutput(tr("Starting auto-detection. This will take a while...")); const Toolchains toolchains = autoDetectToolChains(); const QtVersions qtVersions = autoDetectQtVersions(); const QList cmakeIds = autoDetectCMake(); const Id cmakeId = cmakeIds.empty() ? Id() : cmakeIds.first(); autoDetectDebugger(); 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, m_device->type()); DeviceKitAspect::setDevice(k, m_device); BuildDeviceKitAspect::setDevice(k, m_device); QtVersion *qt = nullptr; if (!qtVersions.isEmpty()) { qt = qtVersions.at(0); QtSupport::QtKitAspect::setQtVersion(k, qt); } Toolchains toolchainsToSet; toolchainsToSet = ToolChainManager::toolchains([qt, this](const ToolChain *tc) { return tc->detectionSource() == m_sharedId && (!qt || qt->qtAbis().contains(tc->targetAbi())); }); 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); emit q->logOutput('\n' + tr("Registered kit %1").arg(kit->displayName())); QApplication::restoreOverrideCursor(); } } // namespace Internal } // namespace Docker