forked from qt-creator/qt-creator
Change-Id: I544763d3b4d521f6bbed0dc5a767c15c49055a19 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
408 lines
13 KiB
C++
408 lines
13 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "projectimporter.h"
|
|
|
|
#include "buildinfo.h"
|
|
#include "kit.h"
|
|
#include "kitaspects.h"
|
|
#include "kitmanager.h"
|
|
#include "projectexplorerconstants.h"
|
|
#include "projectexplorertr.h"
|
|
#include "target.h"
|
|
#include "toolchain.h"
|
|
#include "toolchainmanager.h"
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
#include <utils/algorithm.h>
|
|
#include <utils/environment.h>
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <QLoggingCategory>
|
|
#include <QMessageBox>
|
|
#include <QPushButton>
|
|
#include <QString>
|
|
|
|
namespace ProjectExplorer {
|
|
|
|
static const Utils::Id KIT_IS_TEMPORARY("PE.tmp.isTemporary");
|
|
static const Utils::Id KIT_TEMPORARY_NAME("PE.tmp.Name");
|
|
static const Utils::Id KIT_FINAL_NAME("PE.tmp.FinalName");
|
|
static const Utils::Id TEMPORARY_OF_PROJECTS("PE.tmp.ForProjects");
|
|
|
|
static Utils::Id fullId(Utils::Id id)
|
|
{
|
|
const QString prefix = "PE.tmp.";
|
|
|
|
const QString idStr = id.toString();
|
|
QTC_ASSERT(!idStr.startsWith(prefix), return Utils::Id::fromString(idStr));
|
|
|
|
return Utils::Id::fromString(prefix + idStr);
|
|
}
|
|
|
|
static bool hasOtherUsers(Utils::Id id, const QVariant &v, Kit *k)
|
|
{
|
|
return Utils::contains(KitManager::kits(), [id, v, k](Kit *in) -> bool {
|
|
if (in == k)
|
|
return false;
|
|
const QVariantList tmp = in->value(id).toList();
|
|
return tmp.contains(v);
|
|
});
|
|
}
|
|
|
|
ProjectImporter::ProjectImporter(const Utils::FilePath &path) : m_projectPath(path)
|
|
{
|
|
useTemporaryKitAspect(ToolchainKitAspect::id(),
|
|
[this](Kit *k, const QVariantList &vl) { cleanupTemporaryToolChains(k, vl); },
|
|
[this](Kit *k, const QVariantList &vl) { persistTemporaryToolChains(k, vl); });
|
|
}
|
|
|
|
ProjectImporter::~ProjectImporter()
|
|
{
|
|
const QList<Kit *> kits = KitManager::kits();
|
|
for (Kit *k : kits)
|
|
removeProject(k);
|
|
}
|
|
|
|
const QList<BuildInfo> ProjectImporter::import(const Utils::FilePath &importPath, bool silent)
|
|
{
|
|
QList<BuildInfo> result;
|
|
|
|
const QLoggingCategory log("qtc.projectexplorer.import", QtWarningMsg);
|
|
qCDebug(log) << "ProjectImporter::import" << importPath << silent;
|
|
|
|
QFileInfo fi = importPath.toFileInfo();
|
|
if (!fi.exists() && !fi.isDir()) {
|
|
qCDebug(log) << "**doesn't exist";
|
|
return result;
|
|
}
|
|
|
|
const Utils::FilePath absoluteImportPath = Utils::FilePath::fromString(fi.absoluteFilePath());
|
|
|
|
const auto handleFailure = [this, importPath, silent] {
|
|
if (silent)
|
|
return;
|
|
QMessageBox::critical(Core::ICore::dialogParent(),
|
|
Tr::tr("No Build Found"),
|
|
Tr::tr("No build found in %1 matching project %2.")
|
|
.arg(importPath.toUserOutput(), projectFilePath().toUserOutput()));
|
|
};
|
|
qCDebug(log) << "Examining directory" << absoluteImportPath.toString();
|
|
QString warningMessage;
|
|
QList<void *> dataList = examineDirectory(absoluteImportPath, &warningMessage);
|
|
if (dataList.isEmpty()) {
|
|
qCDebug(log) << "Nothing to import found in" << absoluteImportPath.toString();
|
|
handleFailure();
|
|
return result;
|
|
}
|
|
if (!warningMessage.isEmpty()) {
|
|
qCDebug(log) << "Warning when examining" << absoluteImportPath.toString();
|
|
// we should ask user before importing
|
|
if (silent)
|
|
return result;
|
|
QMessageBox dialog(Core::ICore::dialogParent());
|
|
dialog.setWindowTitle(Tr::tr("Import Warning"));
|
|
dialog.setText(warningMessage);
|
|
dialog.setIcon(QMessageBox::Warning);
|
|
QPushButton *acceptButton = dialog.addButton(Tr::tr("Import Build"), QMessageBox::AcceptRole);
|
|
dialog.addButton(QMessageBox::Cancel);
|
|
dialog.exec();
|
|
if (dialog.clickedButton() != acceptButton)
|
|
return result;
|
|
}
|
|
|
|
qCDebug(log) << "Looking for kits";
|
|
for (void *data : std::as_const(dataList)) {
|
|
QTC_ASSERT(data, continue);
|
|
QList<Kit *> kitList;
|
|
const QList<Kit *> tmp
|
|
= Utils::filtered(KitManager::kits(), [this, data](Kit *k) { return matchKit(data, k); });
|
|
if (tmp.isEmpty()) {
|
|
Kit *k = createKit(data);
|
|
if (k)
|
|
kitList.append(k);
|
|
qCDebug(log) << " no matching kit found, temporary kit created.";
|
|
} else {
|
|
kitList += tmp;
|
|
qCDebug(log) << " " << tmp.count() << "matching kits found.";
|
|
}
|
|
|
|
for (Kit *k : std::as_const(kitList)) {
|
|
qCDebug(log) << "Creating buildinfos for kit" << k->displayName();
|
|
const QList<BuildInfo> infoList = buildInfoList(data);
|
|
if (infoList.isEmpty()) {
|
|
qCDebug(log) << "No build infos for kit" << k->displayName();
|
|
continue;
|
|
}
|
|
|
|
auto factory = BuildConfigurationFactory::find(k, projectFilePath());
|
|
for (BuildInfo i : infoList) {
|
|
i.kitId = k->id();
|
|
i.factory = factory;
|
|
if (!result.contains(i))
|
|
result += i;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (void *dd : std::as_const(dataList))
|
|
deleteDirectoryData(dd);
|
|
dataList.clear();
|
|
|
|
if (result.isEmpty())
|
|
handleFailure();
|
|
|
|
return result;
|
|
}
|
|
|
|
Target *ProjectImporter::preferredTarget(const QList<Target *> &possibleTargets)
|
|
{
|
|
// Select active target
|
|
// a) The default target
|
|
// c) Desktop target
|
|
// d) the first target
|
|
Target *activeTarget = nullptr;
|
|
if (possibleTargets.isEmpty())
|
|
return activeTarget;
|
|
|
|
activeTarget = possibleTargets.at(0);
|
|
bool pickedFallback = false;
|
|
for (Target *t : possibleTargets) {
|
|
if (t->kit() == KitManager::defaultKit())
|
|
return t;
|
|
if (pickedFallback)
|
|
continue;
|
|
if (DeviceTypeKitAspect::deviceTypeId(t->kit()) == Constants::DESKTOP_DEVICE_TYPE) {
|
|
activeTarget = t;
|
|
pickedFallback = true;
|
|
}
|
|
}
|
|
return activeTarget;
|
|
}
|
|
|
|
void ProjectImporter::markKitAsTemporary(Kit *k) const
|
|
{
|
|
QTC_ASSERT(!k->hasValue(KIT_IS_TEMPORARY), return);
|
|
|
|
UpdateGuard guard(*this);
|
|
|
|
const QString name = k->displayName();
|
|
k->setUnexpandedDisplayName(Tr::tr("%1 - temporary").arg(name));
|
|
|
|
k->setValue(KIT_TEMPORARY_NAME, k->displayName());
|
|
k->setValue(KIT_FINAL_NAME, name);
|
|
k->setValue(KIT_IS_TEMPORARY, true);
|
|
}
|
|
|
|
void ProjectImporter::makePersistent(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
if (!k->hasValue(KIT_IS_TEMPORARY))
|
|
return;
|
|
|
|
UpdateGuard guard(*this);
|
|
|
|
KitGuard kitGuard(k);
|
|
k->removeKey(KIT_IS_TEMPORARY);
|
|
k->removeKey(TEMPORARY_OF_PROJECTS);
|
|
const QString tempName = k->value(KIT_TEMPORARY_NAME).toString();
|
|
if (!tempName.isNull() && k->displayName() == tempName)
|
|
k->setUnexpandedDisplayName(k->value(KIT_FINAL_NAME).toString());
|
|
k->removeKey(KIT_TEMPORARY_NAME);
|
|
k->removeKey(KIT_FINAL_NAME);
|
|
|
|
for (const TemporaryInformationHandler &tih : std::as_const(m_temporaryHandlers)) {
|
|
const Utils::Id fid = fullId(tih.id);
|
|
const QVariantList temporaryValues = k->value(fid).toList();
|
|
|
|
// Mark permanent in all other kits:
|
|
const QList<Kit *> kits = KitManager::kits();
|
|
for (Kit *ok : kits) {
|
|
if (ok == k || !ok->hasValue(fid))
|
|
continue;
|
|
const QVariantList otherTemporaryValues
|
|
= Utils::filtered(ok->value(fid).toList(), [&temporaryValues](const QVariant &v) {
|
|
return !temporaryValues.contains(v);
|
|
});
|
|
ok->setValueSilently(fid, otherTemporaryValues);
|
|
}
|
|
|
|
// persist:
|
|
tih.persist(k, temporaryValues);
|
|
k->removeKeySilently(fid);
|
|
}
|
|
}
|
|
|
|
void ProjectImporter::cleanupKit(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
for (const TemporaryInformationHandler &tih : std::as_const(m_temporaryHandlers)) {
|
|
const Utils::Id fid = fullId(tih.id);
|
|
const QVariantList temporaryValues
|
|
= Utils::filtered(k->value(fid).toList(), [fid, k](const QVariant &v) {
|
|
return !hasOtherUsers(fid, v, k);
|
|
});
|
|
tih.cleanup(k, temporaryValues);
|
|
k->removeKeySilently(fid);
|
|
}
|
|
|
|
// remove keys to manage temporary state of kit:
|
|
k->removeKeySilently(KIT_IS_TEMPORARY);
|
|
k->removeKeySilently(TEMPORARY_OF_PROJECTS);
|
|
k->removeKeySilently(KIT_FINAL_NAME);
|
|
k->removeKeySilently(KIT_TEMPORARY_NAME);
|
|
}
|
|
|
|
void ProjectImporter::addProject(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
if (!k->hasValue(KIT_IS_TEMPORARY))
|
|
return;
|
|
|
|
UpdateGuard guard(*this);
|
|
QStringList projects = k->value(TEMPORARY_OF_PROJECTS, QStringList()).toStringList();
|
|
projects.append(m_projectPath.toString()); // note: There can be more than one instance of the project added!
|
|
k->setValueSilently(TEMPORARY_OF_PROJECTS, projects);
|
|
}
|
|
|
|
void ProjectImporter::removeProject(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
if (!k->hasValue(KIT_IS_TEMPORARY))
|
|
return;
|
|
|
|
UpdateGuard guard(*this);
|
|
QStringList projects = k->value(TEMPORARY_OF_PROJECTS, QStringList()).toStringList();
|
|
projects.removeOne(m_projectPath.toString());
|
|
|
|
if (projects.isEmpty()) {
|
|
cleanupKit(k);
|
|
KitManager::deregisterKit(k);
|
|
} else {
|
|
k->setValueSilently(TEMPORARY_OF_PROJECTS, projects);
|
|
}
|
|
}
|
|
|
|
bool ProjectImporter::isTemporaryKit(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return false);
|
|
return k->hasValue(KIT_IS_TEMPORARY);
|
|
}
|
|
|
|
Kit *ProjectImporter::createTemporaryKit(const KitSetupFunction &setup) const
|
|
{
|
|
UpdateGuard guard(*this);
|
|
const auto init = [&](Kit *k) {
|
|
KitGuard kitGuard(k);
|
|
k->setUnexpandedDisplayName(Tr::tr("Imported Kit"));
|
|
k->setup();
|
|
setup(k);
|
|
k->fix();
|
|
markKitAsTemporary(k);
|
|
addProject(k);
|
|
}; // ~KitGuard, sending kitUpdated
|
|
return KitManager::registerKit(init); // potentially adds kits to other targetsetuppages
|
|
}
|
|
|
|
bool ProjectImporter::findTemporaryHandler(Utils::Id id) const
|
|
{
|
|
return Utils::contains(m_temporaryHandlers, [id](const TemporaryInformationHandler &ch) { return ch.id == id; });
|
|
}
|
|
|
|
static Toolchain *toolChainFromVariant(const QVariant &v)
|
|
{
|
|
const QByteArray tcId = v.toByteArray();
|
|
return ToolchainManager::findToolchain(tcId);
|
|
}
|
|
|
|
void ProjectImporter::cleanupTemporaryToolChains(Kit *k, const QVariantList &vl)
|
|
{
|
|
for (const QVariant &v : vl) {
|
|
Toolchain *tc = toolChainFromVariant(v);
|
|
QTC_ASSERT(tc, continue);
|
|
ToolchainManager::deregisterToolchain(tc);
|
|
ToolchainKitAspect::setToolChain(k, nullptr);
|
|
}
|
|
}
|
|
|
|
void ProjectImporter::persistTemporaryToolChains(Kit *k, const QVariantList &vl)
|
|
{
|
|
for (const QVariant &v : vl) {
|
|
Toolchain *tmpTc = toolChainFromVariant(v);
|
|
QTC_ASSERT(tmpTc, continue);
|
|
Toolchain *actualTc = ToolchainKitAspect::toolChain(k, tmpTc->language());
|
|
if (tmpTc && actualTc != tmpTc)
|
|
ToolchainManager::deregisterToolchain(tmpTc);
|
|
}
|
|
}
|
|
|
|
void ProjectImporter::useTemporaryKitAspect(Utils::Id id,
|
|
ProjectImporter::CleanupFunction cleanup,
|
|
ProjectImporter::PersistFunction persist)
|
|
{
|
|
QTC_ASSERT(!findTemporaryHandler(id), return);
|
|
m_temporaryHandlers.append({id, cleanup, persist});
|
|
}
|
|
|
|
void ProjectImporter::addTemporaryData(Utils::Id id, const QVariant &cleanupData, Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
QTC_ASSERT(findTemporaryHandler(id), return);
|
|
const Utils::Id fid = fullId(id);
|
|
|
|
KitGuard guard(k);
|
|
QVariantList tmp = k->value(fid).toList();
|
|
QTC_ASSERT(!tmp.contains(cleanupData), return);
|
|
tmp.append(cleanupData);
|
|
k->setValue(fid, tmp);
|
|
}
|
|
|
|
bool ProjectImporter::hasKitWithTemporaryData(Utils::Id id, const QVariant &data) const
|
|
{
|
|
Utils::Id fid = fullId(id);
|
|
return Utils::contains(KitManager::kits(), [data, fid](Kit *k) {
|
|
return k->value(fid).toList().contains(data);
|
|
});
|
|
}
|
|
|
|
static ProjectImporter::ToolChainData createToolChains(const ToolchainDescription &tcd)
|
|
{
|
|
ProjectImporter::ToolChainData data;
|
|
|
|
for (ToolchainFactory *factory : ToolchainFactory::allToolchainFactories()) {
|
|
data.tcs = factory->detectForImport(tcd);
|
|
if (data.tcs.isEmpty())
|
|
continue;
|
|
|
|
for (Toolchain *tc : std::as_const(data.tcs))
|
|
ToolchainManager::registerToolchain(tc);
|
|
|
|
data.areTemporary = true;
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
ProjectImporter::ToolChainData
|
|
ProjectImporter::findOrCreateToolChains(const ToolchainDescription &tcd) const
|
|
{
|
|
ToolChainData result;
|
|
result.tcs = ToolchainManager::toolchains([&tcd](const Toolchain *tc) {
|
|
return tc->language() == tcd.language && tc->matchesCompilerCommand(tcd.compilerPath);
|
|
});
|
|
for (const Toolchain *tc : std::as_const(result.tcs)) {
|
|
const QByteArray tcId = tc->id();
|
|
result.areTemporary = result.areTemporary ? true : hasKitWithTemporaryData(ToolchainKitAspect::id(), tcId);
|
|
}
|
|
if (!result.tcs.isEmpty())
|
|
return result;
|
|
|
|
// Create a new toolchain:
|
|
UpdateGuard guard(*this);
|
|
return createToolChains(tcd);
|
|
}
|
|
|
|
} // namespace ProjectExplorer
|