ProjectExplorer: reduce the perceived startup time

... by moving restoreKits to the delayed initialize phase.

Change-Id: If72e41b64ee71f2917b3f7a317d9887afc6e29e8
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
David Schulz
2023-09-11 08:58:51 +02:00
parent f1fa1ad3d9
commit 0b85fc5aa0
13 changed files with 103 additions and 26 deletions

View File

@@ -224,13 +224,19 @@ CMakeKitAspectFactory::CMakeKitAspectFactory()
"This setting is ignored when using other build systems."));
setPriority(20000);
auto updateKits = [this] {
if (KitManager::isLoaded()) {
for (Kit *k : KitManager::kits())
fix(k);
}
};
//make sure the default value is set if a selected CMake is removed
connect(CMakeToolManager::instance(), &CMakeToolManager::cmakeRemoved,
this, [this] { for (Kit *k : KitManager::kits()) fix(k); });
connect(CMakeToolManager::instance(), &CMakeToolManager::cmakeRemoved, this, updateKits);
//make sure the default value is set if a new default CMake is set
connect(CMakeToolManager::instance(), &CMakeToolManager::defaultCMakeChanged,
this, [this] { for (Kit *k : KitManager::kits()) fix(k); });
this, updateKits);
}
Id CMakeKitAspect::id()

View File

@@ -37,6 +37,8 @@
#include <QStyle>
#include <QStyledItemDelegate>
#include <extensionsystem/pluginmanager.h>
const int kInitialWidth = 750;
const int kInitialHeight = 450;
const int kMaxMinimumWidth = 250;
@@ -800,6 +802,14 @@ bool SettingsDialog::execDialog()
bool executeSettingsDialog(QWidget *parent, Id initialPage)
{
if (!ExtensionSystem::PluginManager::isInitializationDone()) {
QObject::connect(ExtensionSystem::PluginManager::instance(),
&ExtensionSystem::PluginManager::initializationDone,
parent,
[parent, initialPage]() { executeSettingsDialog(parent, initialPage); });
return false;
}
// Make sure all wizards are there when the user might access the keyboard shortcuts:
(void) IWizardFactory::allWizardFactories();

View File

@@ -1316,6 +1316,9 @@ static Kit *guessKitFromAbis(const Abis &abis)
{
Kit *kit = nullptr;
if (!KitManager::waitForLoaded())
return kit;
// Try to find a kit via ABI.
if (!abis.isEmpty()) {
// Try exact abis.
@@ -1376,9 +1379,11 @@ bool DebuggerPluginPrivate::parseArgument(QStringList::const_iterator &it,
return false;
}
} else if (key == "kit") {
kit = KitManager::kit(Id::fromString(val));
if (!kit)
kit = KitManager::kit(Utils::equal(&Kit::displayName, val));
if (KitManager::waitForLoaded()) {
kit = KitManager::kit(Id::fromString(val));
if (!kit)
kit = KitManager::kit(Utils::equal(&Kit::displayName, val));
}
} else if (key == "server") {
startMode = AttachToRemoteServer;
remoteChannel = val;

View File

@@ -84,7 +84,7 @@ UnstartedAppWatcherDialog::UnstartedAppWatcherDialog(QWidget *parent)
if (kit)
m_kitChooser->setCurrentKitId(kit->id());
else if (KitManager::defaultKit())
else if (KitManager::waitForLoaded() && KitManager::defaultKit())
m_kitChooser->setCurrentKitId(KitManager::defaultKit()->id());
auto pathLayout = new QHBoxLayout;

View File

@@ -38,6 +38,8 @@
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/kitmanager.h>
#include <texteditor/texteditorconstants.h>
#include <utils/algorithm.h>
@@ -283,7 +285,12 @@ void HelpPlugin::extensionsInitialized()
bool HelpPlugin::delayedInitialize()
{
HelpManager::setupHelpManager();
if (ProjectExplorer::KitManager::isLoaded()) {
HelpManager::setupHelpManager();
} else {
connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitsLoaded,
this, &HelpManager::setupHelpManager);
}
return true;
}

View File

@@ -13,6 +13,7 @@
#include "toolchainmanager.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <android/androidconstants.h>
#include <baremetal/baremetalconstants.h>
@@ -421,10 +422,10 @@ void KitManager::restoreKits()
if (!k)
k = Utils::findOrDefault(resultList, &Kit::isValid);
std::swap(resultList, d->m_kitList);
d->m_initialized = true;
setDefaultKit(k);
d->m_writer = std::make_unique<PersistentSettingsWriter>(settingsFileName(), "QtCreatorProfiles");
d->m_initialized = true;
kitAspectFactoriesStorage().onKitsLoaded();
@@ -467,6 +468,33 @@ bool KitManager::isLoaded()
return d->m_initialized;
}
bool KitManager::waitForLoaded(const int timeout)
{
if (isLoaded())
return true;
showLoadingProgress();
QElapsedTimer timer;
timer.start();
while (!isLoaded() && !timer.hasExpired(timeout))
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return KitManager::isLoaded();
}
void KitManager::showLoadingProgress()
{
if (isLoaded())
return;
static QFutureInterface<void> futureInterface;
if (futureInterface.isRunning())
return;
futureInterface.reportStarted();
Core::ProgressManager::addTimedTask(futureInterface.future(),
Tr::tr("Loading Kits"),
"LoadingKitsProgress",
5);
connect(instance(), &KitManager::kitsLoaded, []() { futureInterface.reportFinished(); });
}
void KitManager::setBinaryForKit(const FilePath &binary)
{
QTC_ASSERT(d, return);
@@ -475,6 +503,7 @@ void KitManager::setBinaryForKit(const FilePath &binary)
const QList<Kit *> KitManager::sortedKits()
{
QTC_ASSERT(KitManager::isLoaded(), return {});
// This method was added to delay the sorting of kits as long as possible.
// Since the displayName can contain variables it can be costly (e.g. involve
// calling executables to find version information, etc.) to call that
@@ -548,6 +577,7 @@ static KitList restoreKitsHelper(const FilePath &fileName)
const QList<Kit *> KitManager::kits()
{
QTC_ASSERT(KitManager::isLoaded(), return {});
return Utils::toRawPointer<QList>(d->m_kitList);
}
@@ -556,6 +586,7 @@ Kit *KitManager::kit(Id id)
if (!id.isValid())
return nullptr;
QTC_ASSERT(KitManager::isLoaded(), return {});
return Utils::findOrDefault(d->m_kitList, Utils::equal(&Kit::id, id));
}
@@ -566,6 +597,7 @@ Kit *KitManager::kit(const Kit::Predicate &predicate)
Kit *KitManager::defaultKit()
{
QTC_ASSERT(KitManager::isLoaded(), return {});
return d->m_defaultKit;
}
@@ -597,7 +629,7 @@ void KitManager::notifyAboutUpdate(Kit *k)
Kit *KitManager::registerKit(const std::function<void (Kit *)> &init, Utils::Id id)
{
QTC_ASSERT(isLoaded(), return nullptr);
QTC_ASSERT(isLoaded(), return {});
auto k = std::make_unique<Kit>(id);
QTC_ASSERT(k->id().isValid(), return nullptr);
@@ -620,6 +652,8 @@ Kit *KitManager::registerKit(const std::function<void (Kit *)> &init, Utils::Id
void KitManager::deregisterKit(Kit *k)
{
QTC_ASSERT(KitManager::isLoaded(), return);
if (!k || !Utils::contains(d->m_kitList, k))
return;
auto taken = Utils::take(d->m_kitList, k);
@@ -632,6 +666,8 @@ void KitManager::deregisterKit(Kit *k)
void KitManager::setDefaultKit(Kit *k)
{
QTC_ASSERT(KitManager::isLoaded(), return);
if (defaultKit() == k)
return;
if (k && !Utils::contains(d->m_kitList, k))

View File

@@ -155,6 +155,8 @@ public:
static void saveKits();
static bool isLoaded();
static bool waitForLoaded(const int timeout = 60 * 1000); // timeout in ms
static void showLoadingProgress();
signals:
void kitAdded(ProjectExplorer::Kit *);

View File

@@ -197,6 +197,7 @@ signals:
void kitStateChanged();
private:
void initializeFromKitManager();
void addKit(Kit *k);
void updateKit(Kit *k);
void removeKit(Kit *k);
@@ -227,10 +228,11 @@ KitModel::KitModel(QBoxLayout *parentLayout, QObject *parent)
rootItem()->appendChild(m_autoRoot);
rootItem()->appendChild(m_manualRoot);
for (Kit *k : KitManager::sortedKits())
addKit(k);
changeDefaultKit();
if (KitManager::isLoaded()) {
for (Kit *k : KitManager::sortedKits())
addKit(k);
changeDefaultKit();
}
connect(KitManager::instance(), &KitManager::kitAdded,
this, &KitModel::addKit);

View File

@@ -24,13 +24,13 @@
#include "toolchainmanager.h"
#include "userfileaccessor.h"
#include <coreplugin/idocument.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/kitmanager.h>
@@ -643,6 +643,12 @@ void Project::saveSettings()
Project::RestoreResult Project::restoreSettings(QString *errorMessage)
{
if (!KitManager::waitForLoaded()) {
if (errorMessage)
*errorMessage = Tr::tr("Could not load kits in a reasonable amount of time.");
return RestoreResult::Error;
}
if (!d->m_accessor)
d->m_accessor = std::make_unique<Internal::UserFileAccessor>(this);
Store map(d->m_accessor->restoreSettings(ICore::dialogParent()));

View File

@@ -2121,20 +2121,15 @@ void ProjectExplorerPlugin::extensionsInitialized()
// Load devices immediately, as other plugins might want to use them
DeviceManager::instance()->load();
// delay restoring kits until UI is shown for improved perceived startup performance
QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits);
}
void ProjectExplorerPlugin::restoreKits()
bool ProjectExplorerPlugin::delayedInitialize()
{
NANOTRACE_SCOPE("ProjectExplorer", "ProjectExplorerPlugin::restoreKits");
ExtraAbi::load(); // Load this before Toolchains!
ToolChainManager::restoreToolChains();
KitManager::restoreKits();
// restoring startup session is supposed to be done as a result of ICore::coreOpened,
// and that is supposed to happen after restoring kits:
QTC_CHECK(!SessionManager::isStartupSessionRestored());
return true;
}
void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu()
@@ -2379,8 +2374,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
dd->updateActions();
const bool switchToProjectsMode = Utils::anyOf(openedPro, &Project::needsConfiguration);
const bool switchToEditMode = Utils::allOf(openedPro,
[](Project *p) { return p->isEditModePreferred(); });
const bool switchToEditMode = Utils::allOf(openedPro, &Project::isEditModePreferred);
if (!openedPro.isEmpty()) {
if (switchToProjectsMode)
ModeManager::activateMode(Constants::MODE_SESSION);

View File

@@ -106,7 +106,7 @@ public:
//PluginInterface
bool initialize(const QStringList &arguments, QString *errorMessage) override;
void extensionsInitialized() override;
void restoreKits();
bool delayedInitialize() override;
ShutdownFlag aboutToShutdown() override;
static void setProjectExplorerSettings(const ProjectExplorerSettings &pes);

View File

@@ -129,6 +129,7 @@ void ToolChainManager::saveToolChains()
const Toolchains &ToolChainManager::toolchains()
{
QTC_CHECK(d->m_loaded);
return d->m_toolChains;
}
@@ -140,11 +141,13 @@ Toolchains ToolChainManager::toolchains(const ToolChain::Predicate &predicate)
ToolChain *ToolChainManager::toolChain(const ToolChain::Predicate &predicate)
{
QTC_CHECK(d->m_loaded);
return Utils::findOrDefault(d->m_toolChains, predicate);
}
Toolchains ToolChainManager::findToolChains(const Abi &abi)
{
QTC_CHECK(d->m_loaded);
Toolchains result;
for (ToolChain *tc : std::as_const(d->m_toolChains)) {
bool isCompatible = Utils::anyOf(tc->supportedAbis(), [abi](const Abi &supportedAbi) {
@@ -159,6 +162,7 @@ Toolchains ToolChainManager::findToolChains(const Abi &abi)
ToolChain *ToolChainManager::findToolChain(const QByteArray &id)
{
QTC_CHECK(d->m_loaded);
if (id.isEmpty())
return nullptr;
@@ -214,6 +218,7 @@ bool ToolChainManager::registerToolChain(ToolChain *tc)
void ToolChainManager::deregisterToolChain(ToolChain *tc)
{
QTC_CHECK(d->m_loaded);
if (!tc || !d->m_toolChains.contains(tc))
return;
d->m_toolChains.removeOne(tc);

View File

@@ -179,6 +179,10 @@ static bool getEditorLaunchData(const CommandForQtVersion &commandForQtVersion,
// - default kit
// - any other kit
// As fallback check PATH
if (!KitManager::waitForLoaded()) {
*errorMessage = Tr::tr("Could not load kits in a reasonable amount of time.");
return false;
}
data->workingDirectory.clear();
QVector<QtSupport::QtVersion *> qtVersionsToCheck; // deduplicated after being filled
if (const Project *project = ProjectManager::projectForFile(filePath)) {