Commit ac1d9abd authored by Tobias Hunger's avatar Tobias Hunger

ToolChainManager: Refactor toolchain restoration

This should be simpler to follow now.

Fix autodetection to return not only the newly detected toolchains,
but also those that are re-detected (taking their definition from
the alreadyKnown list passed to the autodetect method where possible).

This avoids running lots of toolchains during start-up, but still
enables us to fix QTCREATORBUG-12751

Task-number: QTCREATORBUG-12751
Change-Id: Ie74e7cffb2b014a6132cc8559db232397344f2f1
Reviewed-by: default avatarEike Ziller <eike.ziller@theqtcompany.com>
parent e5d62f3e
......@@ -1233,21 +1233,15 @@ static bool equalKits(Kit *a, Kit *b)
void AndroidConfigurations::registerNewToolChains()
{
QList<ToolChain *> existingToolChains = ToolChainManager::toolChains();
QList<ToolChain *> toolchains = AndroidToolChainFactory::createToolChainsForNdk(AndroidConfigurations::currentConfig().ndkLocation());
foreach (ToolChain *tc, toolchains) {
bool found = false;
for (int i = 0; i < existingToolChains.count(); ++i) {
if (*(existingToolChains.at(i)) == *tc) {
found = true;
break;
}
}
if (found)
delete tc;
else
const QList<ToolChain *> existingAndroidToolChains
= Utils::filtered(ToolChainManager::toolChains(),
Utils::equal(&ToolChain::typeId, Core::Id(Constants::ANDROID_TOOLCHAIN_ID)));
const QList<ToolChain *> newToolchains
= AndroidToolChainFactory::autodetectToolChainsForNdk(AndroidConfigurations::currentConfig().ndkLocation(),
existingAndroidToolChains);
foreach (ToolChain *tc, newToolchains)
ToolChainManager::registerToolChain(tc);
}
}
void AndroidConfigurations::removeOldToolChains()
......
......@@ -42,6 +42,7 @@
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
......@@ -260,8 +261,7 @@ AndroidToolChainFactory::AndroidToolChainFactory()
QList<ToolChain *> AndroidToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
{
Q_UNUSED(alreadyKnown);
return createToolChainsForNdk(AndroidConfigurations::currentConfig().ndkLocation());
return autodetectToolChainsForNdk(AndroidConfigurations::currentConfig().ndkLocation(), alreadyKnown);
}
bool AndroidToolChainFactory::canRestore(const QVariantMap &data)
......@@ -352,11 +352,23 @@ bool AndroidToolChainFactory::versionCompareLess(AndroidToolChain *atc, AndroidT
return versionCompareLess(a, b);
}
QList<ToolChain *> AndroidToolChainFactory::createToolChainsForNdk(const FileName &ndkPath)
static AndroidToolChain *findToolChain(Utils::FileName &compilerPath, const QList<ToolChain *> &alreadyKnown)
{
return static_cast<AndroidToolChain *>(
Utils::findOrDefault(alreadyKnown, [compilerPath](ToolChain *tc) {
return tc->compilerCommand() == compilerPath
&& tc->typeId() == Constants::ANDROID_TOOLCHAIN_ID;
}));
}
QList<ToolChain *>
AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath,
const QList<ToolChain *> &alreadyKnown)
{
QList<ToolChain *> result;
if (ndkPath.isEmpty())
return result;
QRegExp versionRegExp(NDKGccVersionRegExp);
FileName path = ndkPath;
QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(),
......@@ -373,13 +385,16 @@ QList<ToolChain *> AndroidToolChainFactory::createToolChainsForNdk(const FileNam
Abi abi = AndroidConfig::abiForToolChainPrefix(platform);
if (abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported
continue;
AndroidToolChain *tc = new AndroidToolChain(abi, version, ToolChain::AutoDetection);
FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, version);
tc->resetToolChain(compilerPath);
AndroidToolChain *tc = findToolChain(compilerPath, alreadyKnown);
if (!tc) {
tc = new AndroidToolChain(abi, version, ToolChain::AutoDetection);
tc->resetToolChain(compilerPath);
}
result.append(tc);
QHash<Abi, AndroidToolChain *>::const_iterator it
= newestToolChainForArch.constFind(abi);
auto it = newestToolChainForArch.constFind(abi);
if (it == newestToolChainForArch.constEnd())
newestToolChainForArch.insert(abi, tc);
else if (versionCompareLess(it.value(), tc))
......@@ -388,8 +403,7 @@ QList<ToolChain *> AndroidToolChainFactory::createToolChainsForNdk(const FileNam
foreach (ToolChain *tc, result) {
AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
if (newestToolChainForArch.value(atc->targetAbi()) != atc)
atc->setSecondaryToolChain(true);
atc->setSecondaryToolChain(newestToolChainForArch.value(atc->targetAbi()) != atc);
}
return result;
......
......@@ -113,7 +113,9 @@ public:
QString version;
};
static QList<ProjectExplorer::ToolChain *> createToolChainsForNdk(const Utils::FileName &ndkPath);
static QList<ProjectExplorer::ToolChain *>
autodetectToolChainsForNdk(const Utils::FileName &ndkPath,
const QList<ProjectExplorer::ToolChain *> &alreadyKnown);
static QList<AndroidToolChainInformation> toolchainPathsForNdk(const Utils::FileName &ndkPath);
static QList<int> versionNumberFromString(const QString &version);
......
......@@ -375,10 +375,10 @@ QList<ToolChain *> IosToolChainFactory::autoDetect(const QList<ToolChain *> &exi
foreach (const Platform &platform, platforms) {
ClangToolChain *toolChain = findToolChainForPlatform(platform, existingClangToolChains);
if (!toolChain) {
ClangToolChain *newToolChain = createToolChain(platform);
toolChains.append(newToolChain);
existingClangToolChains.append(newToolChain);
toolChain = createToolChain(platform);
existingClangToolChains.append(toolChain);
}
toolChains.append(toolChain);
}
return Utils::transform(toolChains, [](ClangToolChain *tc) -> ToolChain * { return tc; });
}
......
......@@ -819,7 +819,8 @@ QList<ToolChain *> GccToolChainFactory::autoDetectToolchains(const QString &comp
if (compilerPath.isEmpty())
return result;
if (Utils::findOrDefault(alreadyKnown, Utils::equal(&ToolChain::compilerCommand, compilerPath)))
result = Utils::filtered(alreadyKnown, Utils::equal(&ToolChain::compilerCommand, compilerPath));
if (!result.isEmpty())
return result;
GccToolChain::addCommandPathToEnvironment(compilerPath, systemEnvironment);
......
......@@ -33,6 +33,7 @@
#include "msvcparser.h"
#include "projectexplorerconstants.h"
#include <utils/algorithm.h>
#include <utils/synchronousprocess.h>
#include <utils/winutils.h>
#include <utils/qtcassert.h>
......@@ -526,9 +527,26 @@ QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, MsvcToolChai
return vcVarsBatFor(basePath, platformName(platform));
}
static ToolChain *findOrCreateToolChain(const QList<ToolChain *> &alreadyKnown,
const QString &name, const Abi &abi,
const QString &varsBat, const QString &varsBatArg,
ToolChain::Detection d = ToolChain::ManualDetection)
{
ToolChain *tc = Utils::findOrDefault(alreadyKnown,
[&varsBat, &varsBatArg](ToolChain *tc) -> bool {
if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID)
return false;
auto mtc = static_cast<MsvcToolChain *>(tc);
return mtc->varsBat() == varsBat
&& mtc->varsBatArg() == varsBatArg;
});
if (!tc)
tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, d);
return tc;
}
QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
{
Q_UNUSED(alreadyKnown);
QList<ToolChain *> results;
// 1) Installed SDKs preferred over standalone Visual studio
......@@ -550,16 +568,19 @@ QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &al
continue;
QList<ToolChain *> tmp;
tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::x86),
findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::x86, sdkKey),
fi.absoluteFilePath(), QLatin1String("/x86"), ToolChain::AutoDetection));
tmp.append(findOrCreateToolChain(alreadyKnown,
generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::x86),
findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::x86, sdkKey),
fi.absoluteFilePath(), QLatin1String("/x86"), ToolChain::AutoDetection));
// Add all platforms, cross-compiler is automatically selected by SetEnv.cmd if needed
tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::amd64),
findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::amd64, sdkKey),
fi.absoluteFilePath(), QLatin1String("/x64"), ToolChain::AutoDetection));
tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64),
findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, sdkKey),
fi.absoluteFilePath(), QLatin1String("/ia64"), ToolChain::AutoDetection));
tmp.append(findOrCreateToolChain(alreadyKnown,
generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::amd64),
findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::amd64, sdkKey),
fi.absoluteFilePath(), QLatin1String("/x64"), ToolChain::AutoDetection));
tmp.append(findOrCreateToolChain(alreadyKnown,
generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64),
findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, sdkKey),
fi.absoluteFilePath(), QLatin1String("/ia64"), ToolChain::AutoDetection));
// Make sure the default is front.
if (folder == defaultSdkPath)
results = tmp + results;
......@@ -601,11 +622,11 @@ QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &al
foreach (const MsvcToolChain::Platform &platform, platforms) {
if (hostSupportsPlatform(platform)
&& QFileInfo(vcVarsBatFor(path, platform)).isFile()) {
results.append(new MsvcToolChain(
results.append(findOrCreateToolChain(
alreadyKnown,
generateDisplayName(vsName, MsvcToolChain::VS, platform),
findAbiOfMsvc(MsvcToolChain::VS, platform, vsName),
vcvarsAllbat,
platformName(platform),
vcvarsAllbat, platformName(platform),
ToolChain::AutoDetection));
}
}
......
......@@ -50,7 +50,6 @@ static const char TOOLCHAIN_DATA_KEY[] = "ToolChain.";
static const char TOOLCHAIN_COUNT_KEY[] = "ToolChain.Count";
static const char TOOLCHAIN_FILE_VERSION_KEY[] = "Version";
static const char TOOLCHAIN_FILENAME[] = "/qtcreator/toolchains.xml";
static const char LEGACY_TOOLCHAIN_FILENAME[] = "/toolChains.xml";
using namespace Utils;
......@@ -167,98 +166,146 @@ static QList<ToolChain *> restoreFromFile(const FileName &fileName)
return result;
}
static QList<ToolChain *> autoDetectToolChains(const QList<ToolChain *> alreadyKnownTcs)
{
QList<ToolChain *> result;
const QList<ToolChainFactory *> factories
= ExtensionSystem::PluginManager::getObjects<ToolChainFactory>();
foreach (ToolChainFactory *f, factories)
result.append(f->autoDetect(alreadyKnownTcs));
return result;
}
static QList<ToolChain *> subtractByEqual(const QList<ToolChain *> &a, const QList<ToolChain *> &b)
{
return Utils::filtered(a, [&b](ToolChain *atc) {
return !Utils::anyOf(b, [atc](ToolChain *btc) { return *atc == *btc; });
});
}
static QList<ToolChain *> subtractByPointerEqual(const QList<ToolChain *> &a, const QList<ToolChain *> &b)
{
return Utils::filtered(a, [&b](ToolChain *atc) { return !b.contains(atc); });
}
static QList<ToolChain *> subtractById(const QList<ToolChain *> &a, const QList<ToolChain *> &b)
{
return Utils::filtered(a, [&b](ToolChain *atc) {
return !Utils::anyOf(b, Utils::equal(&ToolChain::id, atc->id()));
});
}
static QList<ToolChain *> intersectByEqual(const QList<ToolChain *> &a, const QList<ToolChain *> &b)
{
return Utils::filtered(a, [&b](ToolChain *atc) {
return Utils::anyOf(b, [atc](ToolChain *btc) { return *atc == *btc; });
});
}
static QList<ToolChain *> makeUnique(const QList<ToolChain *> &a)
{
return QSet<ToolChain *>::fromList(a).toList();
}
namespace {
struct ToolChainOperations
{
QList<ToolChain *> toDemote;
QList<ToolChain *> toRegister;
QList<ToolChain *> toDelete;
};
} // namespace
static ToolChainOperations mergeToolChainLists(const QList<ToolChain *> &systemFileTcs,
const QList<ToolChain *> &userFileTcs,
const QList<ToolChain *> &autodetectedTcs)
{
const QList<ToolChain *> manualUserTcs
= Utils::filtered(userFileTcs, [](ToolChain *t) { return !t->isAutoDetected(); });
// Remove systemFileTcs from autodetectedUserTcs based on id-matches:
const QList<ToolChain *> autodetectedUserFileTcs
= Utils::filtered(userFileTcs, &ToolChain::isAutoDetected);
const QList<ToolChain *> autodetectedUserTcs = subtractById(autodetectedUserFileTcs, systemFileTcs);
// Calculate a set of Tcs that were detected before (and saved to userFile) and that
// got re-detected again. Take the userTcs (to keep Ids) over the same in autodetectedTcs.
const QList<ToolChain *> redetectedUserTcs
= intersectByEqual(autodetectedUserTcs, autodetectedTcs);
// Remove redetected tcs from autodetectedUserTcs:
const QList<ToolChain *> notRedetectedUserTcs
= subtractByPointerEqual(autodetectedUserTcs, redetectedUserTcs);
// Remove redetected tcs from autodetectedTcs:
const QList<ToolChain *> newlyAutodetectedTcs
= subtractByEqual(autodetectedTcs, redetectedUserTcs);
const QList<ToolChain *> notRedetectedButValidUserTcs
= Utils::filtered(notRedetectedUserTcs, &ToolChain::isValid);
const QList<ToolChain *> validManualUserTcs
= Utils::filtered(manualUserTcs, &ToolChain::isValid);
ToolChainOperations result;
result.toDemote = notRedetectedButValidUserTcs;
result.toRegister = result.toDemote + systemFileTcs + redetectedUserTcs + newlyAutodetectedTcs
+ validManualUserTcs;
result.toDelete = makeUnique(subtractByPointerEqual(systemFileTcs + userFileTcs + autodetectedTcs,
result.toRegister));
return result;
}
void ToolChainManager::restoreToolChains()
{
QTC_ASSERT(!d->m_writer, return);
d->m_writer =
new PersistentSettingsWriter(settingsFileName(QLatin1String(TOOLCHAIN_FILENAME)), QLatin1String("QtCreatorToolChains"));
QList<ToolChain *> tcsToRegister;
QList<ToolChain *> tcsToCheck;
new PersistentSettingsWriter(settingsFileName(QLatin1String(TOOLCHAIN_FILENAME)),
QLatin1String("QtCreatorToolChains"));
// read all tool chains from SDK
QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName());
QList<ToolChain *> readTcs =
restoreFromFile(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(TOOLCHAIN_FILENAME)));
// make sure we mark these as autodetected!
foreach (ToolChain *tc, readTcs)
tc->setDetection(ToolChain::AutoDetection);
tcsToRegister = readTcs; // SDK TCs are always considered to be up-to-date, so no need to
// recheck them.
const QList<ToolChain *> systemFileTcs = readSystemFileToolChains();
// read all tool chains from user file.
// Read legacy settings once and keep them around...
FileName fileName = settingsFileName(QLatin1String(TOOLCHAIN_FILENAME));
if (!fileName.exists())
fileName = settingsFileName(QLatin1String(LEGACY_TOOLCHAIN_FILENAME));
readTcs = restoreFromFile(fileName);
foreach (ToolChain *tc, readTcs) {
if (tc->isAutoDetected())
tcsToCheck.append(tc);
else
tcsToRegister.append(tc);
}
readTcs.clear();
// Remove TCs configured by the SDK:
foreach (ToolChain *tc, tcsToRegister) {
for (int i = tcsToCheck.count() - 1; i >= 0; --i) {
if (tcsToCheck.at(i)->id() == tc->id()) {
delete tcsToCheck.at(i);
tcsToCheck.removeAt(i);
}
}
}
const QList<ToolChain *> userFileTcs
= restoreFromFile(settingsFileName(QLatin1String(TOOLCHAIN_FILENAME)));
// Then auto detect
QList<ToolChain *> detectedTcs = tcsToCheck;
QList<ToolChainFactory *> factories = ExtensionSystem::PluginManager::getObjects<ToolChainFactory>();
foreach (ToolChainFactory *f, factories)
detectedTcs.append(f->autoDetect(tcsToCheck));
// Find/update autodetected tool chains:
ToolChain *toStore = 0;
foreach (ToolChain *currentDetected, detectedTcs) {
toStore = currentDetected;
// Check whether we had this TC stored and prefer the old one with the old id, marked
// as auto-detection.
for (int i = 0; i < tcsToCheck.count(); ++i) {
if (tcsToCheck.at(i) == currentDetected) {
tcsToCheck.removeAt(i);
break;
} else if (*(tcsToCheck.at(i)) == *currentDetected) {
toStore = tcsToCheck.at(i);
toStore->setDetection(ToolChain::AutoDetection);
tcsToCheck.removeAt(i);
delete currentDetected;
break;
}
}
tcsToRegister += toStore;
}
// Autodetect: Pass autodetected toolchains from user file so the information can be reused:
const QList<ToolChain *> autodetectedUserFileTcs
= Utils::filtered(userFileTcs, &ToolChain::isAutoDetected);
const QList<ToolChain *> autodetectedTcs = autoDetectToolChains(autodetectedUserFileTcs);
// Keep toolchains that were not rediscovered but are still executable and delete the rest
foreach (ToolChain *tc, tcsToCheck) {
if (!tc->isValid()) {
qWarning() << QString::fromLatin1("ToolChain \"%1\" (%2) dropped since it is not valid")
.arg(tc->displayName()).arg(QString::fromUtf8(tc->id()));
delete tc;
} else {
tc->setDetection(ToolChain::ManualDetection); // "demote" to manual toolchain
tcsToRegister += tc;
}
}
// merge tool chains and register those that we need to keep:
ToolChainOperations ops = mergeToolChainLists(systemFileTcs, userFileTcs, autodetectedTcs);
// Store manual tool chains
foreach (ToolChain *tc, tcsToRegister)
// Process ops:
foreach (ToolChain *tc, ops.toDemote)
tc->setDetection(ToolChain::ManualDetection);
foreach (ToolChain *tc, ops.toRegister)
registerToolChain(tc);
qDeleteAll(ops.toDelete);
emit m_instance->toolChainsLoaded();
}
QList<ToolChain *> ToolChainManager::readSystemFileToolChains()
{
QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName());
QList<ToolChain *> systemTcs
= restoreFromFile(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(TOOLCHAIN_FILENAME)));
foreach (ToolChain *tc, systemTcs)
tc->setDetection(ToolChain::AutoDetection);
return systemTcs;
}
void ToolChainManager::saveToolChains()
{
QVariantMap data;
......
......@@ -87,6 +87,9 @@ private:
// Make sure the this is only called after all
// Tool chain Factories are registered!
static void restoreToolChains();
static QList<ToolChain *> readSystemFileToolChains();
static void notifyAboutUpdate(ToolChain *);
friend class ProjectExplorerPlugin; // for constructor
......
......@@ -33,6 +33,7 @@
#include "msvcparser.h"
#include "projectexplorerconstants.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QDir>
......@@ -352,10 +353,33 @@ WinCEToolChainFactory::WinCEToolChainFactory()
setDisplayName(tr("WinCE"));
}
static ToolChain *findOrCreateToolChain(const QList<ToolChain *> &alreadyKnown,
const QString &name, const Abi &abi,
const QString &vcvarsBat, const QString &msvcVer,
const QString &ceVer, const QString &binPath,
const QString &includePath, const QString &libPath,
ToolChain::Detection d = ToolChain::ManualDetection)
{
ToolChain *tc
= Utils::findOrDefault(alreadyKnown, [&](ToolChain *tc) -> bool {
if (tc->typeId() != Constants::WINCE_TOOLCHAIN_TYPEID)
return false;
auto cetc = static_cast<WinCEToolChain *>(tc);
return cetc->targetAbi() == abi
&& cetc->varsBat() == vcvarsBat
&& cetc->msvcVer() == msvcVer
&& cetc->ceVer() == ceVer
&& cetc->binPath() == binPath
&& cetc->includePath() == includePath
&& cetc->libPath() == libPath;
});
if (!tc)
tc = new WinCEToolChain(name, abi, vcvarsBat, msvcVer, ceVer, binPath, includePath, libPath, d);
return tc;
}
QList<ToolChain *> WinCEToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
{
Q_UNUSED(alreadyKnown);
QList<ToolChain *> results;
// 1) Installed WinCEs
......@@ -398,16 +422,16 @@ QList<ToolChain *> WinCEToolChainFactory::autoDetect(const QList<ToolChain *> &a
QString ceVer;
if (parseSDK(platformReader, theArch, thePlat, ceVer, binPath, includePath, libPath)) {
WinCEToolChain *pChain = new WinCEToolChain(thePlat,
Abi(theArch, Abi::WindowsOS, Abi::WindowsCEFlavor, Abi::PEFormat, 32),
vcvars32bat,
msvcVer,
ceVer,
binPath,
includePath,
libPath,
ToolChain::AutoDetection);
results.append(pChain);
results.append(findOrCreateToolChain(alreadyKnown,
thePlat,
Abi(theArch, Abi::WindowsOS, Abi::WindowsCEFlavor, Abi::PEFormat, 32),
vcvars32bat,
msvcVer,
ceVer,
binPath,
includePath,
libPath,
ToolChain::AutoDetection));
}
}
}
......
......@@ -57,8 +57,13 @@ public:
QString typeDisplayName() const override;
QString msvcVer() const { return m_msvcVer; }
QString ceVer() const;
QString binPath() const { return m_binPath; }
QString includePath() const { return m_includePath; }
QString libPath() const { return m_libPath; }
QVariantMap toMap() const override;
bool fromMap(const QVariantMap &data) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment