diff options
author | Katja Marttila <katja.marttila@qt.io> | 2022-10-24 14:20:31 +0300 |
---|---|---|
committer | Katja Marttila <katja.marttila@qt.io> | 2022-11-04 10:45:40 +0200 |
commit | 32f16fc0cfb64a2623570ff0447b1aa9e3b2385a (patch) | |
tree | 9f37eead434a42369539f78a34579cdee4784b00 /src/libs/installer | |
parent | cb0b27ef66ceb2de84f46c3de1307765366f3182 (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.cpp | 91 | ||||
-rw-r--r-- | src/libs/installer/component.h | 7 | ||||
-rw-r--r-- | src/libs/installer/component_p.cpp | 5 | ||||
-rw-r--r-- | src/libs/installer/component_p.h | 5 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.cpp | 12 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.h | 2 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 16 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.h | 2 | ||||
-rw-r--r-- | src/libs/installer/packagemanagergui.cpp | 18 | ||||
-rw-r--r-- | src/libs/installer/packagemanagergui.h | 1 |
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 ¶meters); 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); |