summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
authorKatja Marttila <katja.marttila@qt.io>2022-10-24 14:20:31 +0300
committerKatja Marttila <katja.marttila@qt.io>2022-11-04 10:45:40 +0200
commit32f16fc0cfb64a2623570ff0447b1aa9e3b2385a (patch)
tree9f37eead434a42369539f78a34579cdee4784b00 /src/libs/installer
parentcb0b27ef66ceb2de84f46c3de1307765366f3182 (diff)
Add possibility to post load install scripts
This change adds attribute, postLoad, to existing <Script> -element. Using <Script postLoad="True"> will call the script loading and evaluation only to those components which are selected for install or update right before the components installation start. Task-number: QTIFW-2820 Change-Id: Ic1967d329cbb5de6a0216ff3f76cc2ede178db80 Reviewed-by: Arttu Tarkiainen <arttu.tarkiainen@qt.io>
Diffstat (limited to 'src/libs/installer')
-rw-r--r--src/libs/installer/component.cpp91
-rw-r--r--src/libs/installer/component.h7
-rw-r--r--src/libs/installer/component_p.cpp5
-rw-r--r--src/libs/installer/component_p.h5
-rw-r--r--src/libs/installer/packagemanagercore.cpp12
-rw-r--r--src/libs/installer/packagemanagercore.h2
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp16
-rw-r--r--src/libs/installer/packagemanagercore_p.h2
-rw-r--r--src/libs/installer/packagemanagergui.cpp18
-rw-r--r--src/libs/installer/packagemanagergui.h1
10 files changed, 110 insertions, 49 deletions
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index e6c8eac34..08ab36a61 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -342,8 +342,7 @@ void Component::loadDataFromPackage(const Package &package)
setValue(scUpdateText, package.data(scUpdateText).toString());
setValue(scNewComponent, package.data(scNewComponent).toString());
setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString());
-
- setValue(scScriptTag, package.data(scScriptTag).toString());
+ d->m_scriptHash = package.data(QLatin1String(scScriptTag)).toHash();
setValue(scReplaces, package.data(scReplaces).toString());
setValue(scReleaseDate, package.data(scReleaseDate).toString());
setValue(scCheckable, package.data(scCheckable).toString());
@@ -594,38 +593,47 @@ bool Component::treeNameMoveChildren() const
}
/*!
- Loads the component script into the script engine.
+ Loads the component script into the script engine. Call this method with
+ \c postLoad \c true to a list of components that are updated or installed
+ to improve performance if the amount of components is huge and there are no script
+ functions that needs to be called before the installation starts.
*/
-void Component::loadComponentScript()
+void Component::loadComponentScript(const bool postLoad)
{
- const QString script = d->m_vars.value(scScriptTag);
- if (!localTempPath().isEmpty() && !script.isEmpty())
- loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script));
+ const QString installScript(!postLoad ? d->m_scriptHash.value(QLatin1String("installScript")).toString()
+ : d->m_scriptHash.value(QLatin1String("postLoadScript")).toString());
+
+ if (!localTempPath().isEmpty() && !installScript.isEmpty()) {
+ evaluateComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name()
+ , installScript), postLoad);
+ }
}
/*!
- Loads the script at \a fileName into the script engine. The installer and all its
- components as well as other useful things are being exported into the script.
- For more information, see \l{Component Scripting}.
-
- Throws an error when either the script at \a fileName could not be opened, or QScriptEngine
- could not evaluate the script.
+ \internal
*/
-void Component::loadComponentScript(const QString &fileName)
+void Component::evaluateComponentScript(const QString &fileName, const bool postScriptContent)
{
// introduce the component object as javascript value and call the name to check that it
// was successful
try {
- d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
- } catch (const Error &error) {
- if (packageManagerCore()->settings().allowUnstableComponents()) {
- setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
- qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ if (postScriptContent) {
+ d->m_postScriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
+ QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
+ .arg(name()));
} else {
- throw error;
+ d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
+ QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
+ .arg(name()));
}
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
+ // evaluateComponentScript is called with postScriptContent after we have selected components
+ // and are about to install. Do not allow install if unstable components are allowed
+ // as we then end up installing a component which has invalid script.
+ if (!packageManagerCore()->settings().allowUnstableComponents() || postScriptContent)
+ throw error;
}
emit loaded();
@@ -639,7 +647,7 @@ void Component::loadComponentScript(const QString &fileName)
*/
void Component::languageChanged()
{
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("retranslateUi"));
+ callScriptMethod(QLatin1String("retranslateUi"));
}
/*!
@@ -840,10 +848,8 @@ void Component::createOperationsForPath(const QString &path)
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined())
+ return;
QString target;
static const QString prefix = QString::fromLatin1("installer://");
@@ -886,10 +892,8 @@ void Component::createOperationsForArchive(const QString &archive)
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined())
+ return;
QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive));
const bool isZip = (archiveFile && archiveFile->open(QIODevice::ReadOnly) && archiveFile->isSupported());
@@ -911,7 +915,7 @@ void Component::createOperationsForArchive(const QString &archive)
void Component::beginInstallation()
{
// the script can override this method
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("beginInstallation"));
+ callScriptMethod(QLatin1String("beginInstallation"));
}
/*!
@@ -921,10 +925,9 @@ void Component::beginInstallation()
void Component::createOperations()
{
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperations"))
- .isUndefined()) {
- d->m_operationsCreated = true;
- return;
+ if (!callScriptMethod(QLatin1String("createOperations")).isUndefined()) {
+ d->m_operationsCreated = true;
+ return;
}
loadXMLExtractOperations();
foreach (const QString &archive, archives())
@@ -1193,6 +1196,17 @@ void Component::markComponentUnstable(Component::UnstableError error, const QStr
emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name());
}
+QJSValue Component::callScriptMethod(const QString &methodName, const QJSValueList &arguments) const
+{
+ QJSValue scriptContext;
+ if (!d->m_postScriptContext.isUndefined() && d->m_postScriptContext.property(methodName).isCallable())
+ scriptContext = d->m_postScriptContext;
+ else
+ scriptContext = d->m_scriptContext;
+ return d->scriptEngine()->callScriptMethod(scriptContext,
+ methodName, arguments);
+}
+
namespace {
inline bool convert(QQmlV4Function *func, QStringList *toArgs)
@@ -1341,7 +1355,7 @@ void Component::setValidatorCallbackName(const QString &name)
bool Component::validatePage()
{
if (!validatorCallbackName.isEmpty())
- return d->scriptEngine()->callScriptMethod(d->m_scriptContext, validatorCallbackName).toBool();
+ return callScriptMethod(validatorCallbackName).toBool();
return true;
}
@@ -1486,8 +1500,7 @@ bool Component::isDefault() const
if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) {
QJSValue valueFromScript;
try {
- valueFromScript = d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("isDefault"));
+ valueFromScript = callScriptMethod(QLatin1String("isDefault"));
} catch (const Error &error) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()),
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
index e5f1b38da..bb1a7d1b6 100644
--- a/src/libs/installer/component.h
+++ b/src/libs/installer/component.h
@@ -118,10 +118,9 @@ public:
void removeComponent(Component *component);
QList<Component*> descendantComponents() const;
- void loadComponentScript();
+ void loadComponentScript(const bool postLoad = false);
+ void evaluateComponentScript(const QString &fileName, const bool postScriptContext = false);
- //move this to private
- void loadComponentScript(const QString &fileName);
void loadTranslations(const QDir &directory, const QStringList &qms);
void loadUserInterfaces(const QDir &directory, const QStringList &uis);
void loadLicenses(const QString &directory, const QHash<QString, QVariant> &hash);
@@ -236,6 +235,8 @@ private:
Operation *createOperation(const QString &operationName, const QStringList &parameters);
void markComponentUnstable(const Component::UnstableError error, const QString &errorMessage);
+ QJSValue callScriptMethod(const QString &methodName, const QJSValueList &arguments = QJSValueList()) const;
+
private:
QString validatorCallbackName;
ComponentPrivate *d;
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 4030d266e..42a4c5277 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,6 +55,9 @@ ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq)
, m_operationsCreatedSuccessfully(true)
, m_updateIsAvailable(false)
, m_treeNameMoveChildren(false)
+ , m_postLoadScript(false)
+ , m_scriptContext(QJSValue::UndefinedValue)
+ , m_postScriptContext(QJSValue::UndefinedValue)
{
}
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
index bdc67898d..fe9d84023 100644
--- a/src/libs/installer/component_p.h
+++ b/src/libs/installer/component_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -64,17 +64,20 @@ public:
bool m_operationsCreatedSuccessfully;
bool m_updateIsAvailable;
bool m_treeNameMoveChildren;
+ bool m_postLoadScript;
QString m_componentName;
QUrl m_repositoryUrl;
QString m_localTempPath;
QJSValue m_scriptContext;
+ QJSValue m_postScriptContext;
QHash<QString, QString> m_vars;
QList<Component*> m_childComponents;
QList<Component*> m_allChildComponents;
QStringList m_downloadableArchives;
QStringList m_stopProcessForUpdateRequests;
QHash<QString, QPointer<QWidget> > m_userInterfaces;
+ QHash<QString, QVariant> m_scriptHash;
// < display name, < file name, file content > >
QHash<QString, QVariantMap> m_licenses;
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 8e78cff84..d8e22be40 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -601,6 +601,18 @@ bool PackageManagerCore::clearLocalCache(QString *error)
}
/*!
+ \internal
+ */
+template <typename T>
+bool PackageManagerCore::loadComponentScripts(const T &components, const bool postScript)
+{
+ return d->loadComponentScripts(components, postScript);
+}
+
+template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
+
+/*!
\sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
*/
void PackageManagerCore::componentsToInstallNeedsRecalculation()
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index 2c307971a..d7466a1da 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -347,6 +347,8 @@ public:
bool resetLocalCache(bool init = false);
bool clearLocalCache(QString *error = nullptr);
+ template <typename T>
+ bool loadComponentScripts(const T &components, const bool postScript = false);
public Q_SLOTS:
bool runInstaller();
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 6eb589086..368f9aaae 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -433,7 +433,7 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
}
template <typename T>
-bool PackageManagerCorePrivate::loadComponentScripts(const T &components)
+bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
{
infoMessage(nullptr, tr("Loading component scripts..."));
@@ -442,7 +442,7 @@ bool PackageManagerCorePrivate::loadComponentScripts(const T &components)
if (statusCanceledOrFailed())
return false;
- component->loadComponentScript();
+ component->loadComponentScript(postScript);
++loadedComponents;
const int currentProgress = qRound(double(loadedComponents) / components.count() * 100);
@@ -452,8 +452,8 @@ bool PackageManagerCorePrivate::loadComponentScripts(const T &components)
return true;
}
-template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &);
-template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &);
+template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
void PackageManagerCorePrivate::cleanUpComponentEnvironment()
{
@@ -3073,6 +3073,14 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun()
{
QString htmlOutput;
bool componentsOk = m_core->calculateComponents(&htmlOutput);
+
+ try {
+ loadComponentScripts(installerCalculator()->orderedComponentsToInstall(), true);
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ return false;
+ }
+
if (statusCanceledOrFailed()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled.";
} else if (componentsOk && acceptLicenseAgreements()) {
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index a42033ff6..03047240c 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -108,7 +108,7 @@ public:
bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript);
template <typename T>
- bool loadComponentScripts(const T &components);
+ bool loadComponentScripts(const T &components, const bool postScript = false);
void cleanUpComponentEnvironment();
ScriptEngine *componentScriptEngine() const;
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index 2715155ff..f7c500811 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -2227,6 +2227,24 @@ void ComponentSelectionPage::showEvent(QShowEvent *event)
QWizardPage::showEvent(event);
}
+bool ComponentSelectionPage::validatePage()
+{
+ PackageManagerCore *core = packageManagerCore();
+ try {
+ core->loadComponentScripts(core->orderedComponentsToInstall(), true);
+ } catch (const Error &error) {
+ // As component script loading failed, there is error in the script and component is
+ // marked as unselected. Recalculate so that unselected component is removed from install.
+ // User is then able to select other components for install.
+ core->clearComponentsToInstallCalculated();
+ core->calculateComponentsToInstall();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
+ return true;
+}
+
/*!
Selects all components in the component tree.
*/
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index 5f4bd6157..cce36e4ae 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.h
@@ -334,6 +334,7 @@ protected:
void entering() override;
void leaving() override;
void showEvent(QShowEvent *event) override;
+ bool validatePage() override;
private Q_SLOTS:
void setModified(bool modified);