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 | |
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>
21 files changed, 247 insertions, 95 deletions
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index 3d251fbc7..8709e5f39 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -945,6 +945,15 @@ \row \li Script \li File name of a script being loaded. Optional. + Specifying the \c {postLoad="True"} attribute will cause the script + to be loaded only to the component that is selected for update or + install. With the attribute, the script is loaded right before the + component installation starts. This will speed up the installer + if there are large amounts of components with install scripts in the + repository. Make sure the script does not contain anything that needs + to be evaluated before the install tree is shown, like setting \c + <Default> to \c true. Both \c <Script postLoad="True"> and + \c <Script> tags can be used at the same time. For more information, see \l{Adding Operations}. \row \li UserInterfaces diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp index a5e015299..8c629edc4 100644 --- a/src/libs/ifwtools/repositorygen.cpp +++ b/src/libs/ifwtools/repositorygen.cpp @@ -367,39 +367,8 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met root.appendChild(update); - // copy script file - const QString script = package.firstChildElement(QLatin1String("Script")).text(); - if (!script.isEmpty()) { - QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script)); - if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".") - .arg(QDir::toNativeSeparators(scriptFile.fileName()))); - } - - const QString scriptContent = QLatin1String("(function() {") - + QString::fromUtf8(scriptFile.readAll()) - + QLatin1String(";" - " if (typeof Component == \"undefined\")" - " throw \"Missing Component constructor. Please check your script.\";" - "})();"); - - // if the user isn't aware of the downloadable archives value we will add it automatically later - foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive")) - || scriptContent.contains(QLatin1String("removeDownloadableArchive")); - - static QInstaller::ScriptEngine testScriptEngine; - const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName()); - if (value.isError()) { - throw QInstaller::Error(QString::fromLatin1("Exception while loading component " - "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), - value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : - value.toString() + QStringLiteral(" on line number: ") + - value.property(QStringLiteral("lineNumber")).toString())); - } - - const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script)); - copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript); - } + // copy script files + copyScriptFiles(childNodes, info, foundDownloadableArchives, targetDir); // write DownloadableArchives tag if that is missed by the user if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) { @@ -977,6 +946,50 @@ void QInstallerTools::splitMetadata(const QStringList &entryList, const QString } } +void QInstallerTools::copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir) +{ + for (int i = 0; i < childNodes.count(); ++i) { + const QDomNode node = childNodes.at(i); + const QString key = node.nodeName(); + + if (key != QLatin1String("Script")) + continue; + const QString script = node.toElement().text(); + if (script.isEmpty()) + continue; + + QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script)); + if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".") + .arg(QDir::toNativeSeparators(scriptFile.fileName()))); + } + + const QString scriptContent = QLatin1String("(function() {") + + QString::fromUtf8(scriptFile.readAll()) + + QLatin1String(";" + " if (typeof Component == \"undefined\")" + " throw \"Missing Component constructor. Please check your script.\";" + "})();"); + + // if the user isn't aware of the downloadable archives value we will add it automatically later + foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive")) + || scriptContent.contains(QLatin1String("removeDownloadableArchive")); + + static QInstaller::ScriptEngine testScriptEngine; + const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName()); + if (value.isError()) { + throw QInstaller::Error(QString::fromLatin1("Exception while loading component " + "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), + value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : + value.toString() + QStringLiteral(" on line number: ") + + value.property(QStringLiteral("lineNumber")).toString())); + } + + const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script)); + copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript); + } +} + void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QString &repoDir, PackageInfoVector *const infos, const QString &archiveSuffix, Compression compression) { diff --git a/src/libs/ifwtools/repositorygen.h b/src/libs/ifwtools/repositorygen.h index 054c023f4..7ad3dd073 100644 --- a/src/libs/ifwtools/repositorygen.h +++ b/src/libs/ifwtools/repositorygen.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 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. @@ -88,6 +88,7 @@ void IFWTOOLS_EXPORT compressMetaDirectories(const QString &repoDir, const QStri QStringList unifyMetadata(const QString &repoDir, const QString &existingRepoDir, QDomDocument doc); void splitMetadata(const QStringList &entryList, const QString &repoDir, QDomDocument doc, const QHash<QString, QString> &versionMapping); +void copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir); void IFWTOOLS_EXPORT copyMetaData(const QString &outDir, const QString &dataDir, const PackageInfoVector &packages, const QString &appName, const QString& appVersion, const QStringList &uniteMetadatas); 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); diff --git a/src/libs/kdtools/updatesinfo.cpp b/src/libs/kdtools/updatesinfo.cpp index 545931a55..43f75e8a7 100644 --- a/src/libs/kdtools/updatesinfo.cpp +++ b/src/libs/kdtools/updatesinfo.cpp @@ -115,6 +115,7 @@ bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE) UpdateInfo info; QMap<QString, QString> localizedDescriptions; + QHash<QString, QVariant> scriptHash; for (int i = 0; i < updateE.childNodes().count(); i++) { QDomElement childE = updateE.childNodes().at(i).toElement(); if (childE.isNull()) @@ -162,10 +163,18 @@ bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE) const QDomNodeList operationNodes = childE.childNodes(); QVariant operationListVariant = parseOperations(childE.childNodes()); info.data.insert(QLatin1String("Operations"), operationListVariant); + } else if (childE.tagName() == QLatin1String("Script")) { + const bool postLoad = QVariant(childE.attribute(QLatin1String("postLoad"))).toBool(); + if (postLoad) + scriptHash.insert(QLatin1String("postLoadScript"), childE.text()); + else + scriptHash.insert(QLatin1String("installScript"), childE.text()); } else { info.data[childE.tagName()] = childE.text(); } } + if (!scriptHash.isEmpty()) + info.data.insert(QLatin1String("Script"), scriptHash); QStringList candidates; foreach (const QString &lang, QLocale().uiLanguages()) diff --git a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp index 43bd15288..b52e0e9f6 100644 --- a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp +++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2018 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. @@ -125,7 +125,7 @@ private slots: const QString debugMessage = QString("Exception while loading the component script"); const QRegularExpression re(debugMessage); QTest::ignoreMessage(QtWarningMsg, re); - invalidScriptComponent->loadComponentScript(":///data/broken_script.qs"); + invalidScriptComponent->evaluateComponentScript(":///data/broken_script.qs"); model->reset(components); testModelState(model, m_checkedComponentsWithBrokenScript, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithBrokenScript); diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml index 81831ed5b..3b5e22cc0 100644 --- a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml +++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml @@ -170,4 +170,14 @@ <DownloadableArchives>content.7z</DownloadableArchives> <Virtual>true</Virtual> </PackageUpdate> + <PackageUpdate> + <Name>componentJ</Name> + <DisplayName>component J</DisplayName> + <Description>This component has post install script.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2014-08-25</ReleaseDate> + <SortingPriority>50</SortingPriority> + <DownloadableArchives>content.7z</DownloadableArchives> + <Script postLoad="True">postLoadscript.js</Script> + </PackageUpdate> </Updates> diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z Binary files differnew file mode 100644 index 000000000..58ff52baa --- /dev/null +++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z Binary files differnew file mode 100644 index 000000000..be99410a8 --- /dev/null +++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z diff --git a/tests/auto/installer/commandlineinstall/settings.qrc b/tests/auto/installer/commandlineinstall/settings.qrc index 824517e1e..992dbfd58 100644 --- a/tests/auto/installer/commandlineinstall/settings.qrc +++ b/tests/auto/installer/commandlineinstall/settings.qrc @@ -14,6 +14,8 @@ <file>data/installPackagesRepository/componentG/1.0.0content.7z</file> <file>data/installPackagesRepository/componentH/1.0.0content.7z</file> <file>data/installPackagesRepository/componentI/1.0.0content.7z</file> + <file>data/installPackagesRepository/componentJ/1.0.0content.7z</file> + <file>data/installPackagesRepository/componentJ/1.0.0meta.7z</file> <file>data/installPackagesRepository/componentG/1.0.0meta.7z</file> <file>data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z</file> <file>data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z</file> diff --git a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp index c0ac7f399..a986234c8 100644 --- a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp +++ b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp @@ -729,6 +729,21 @@ private slots: QVERIFY(file.remove()); } + void testPostScript() + { + QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit + (m_installDir, ":///data/installPackagesRepository")); + QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList() << QLatin1String("componentJ"))); + VerifyInstaller::verifyInstallerResources(m_installDir, "componentJ", "1.0.0content.txt"); //Selected + VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG + VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall + VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default + + //componentJ is extracted to "extractToAnotherPath" -folder in post install script + bool fileExists = QFileInfo::exists(m_installDir + QDir::separator() + "extractToAnotherPath" + QDir::separator() + "installcontentJ.txt"); + QVERIFY2(fileExists, QString("File \"%1\" does not exist.").arg("installcontentJ.txt").toLatin1()); + } + void init() { m_installDir = QInstaller::generateTemporaryFileName(); diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp index 105bcf5d7..6c55fe205 100644 --- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp +++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 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. @@ -388,13 +388,24 @@ private slots: QCOMPARE(result.isError(), false); } + void loadSimpleComponentScript_data() + { + QTest::addColumn<QString>("path"); + QTest::addColumn<bool>("postLoad"); + QTest::newRow("Pre component script") << ":///data/component1.qs" << false; + QTest::newRow("Post component script") << ":///data/component1.qs" << true; + } + void loadSimpleComponentScript() { - try { - // ignore retranslateUi which is called by loadComponentScript + QFETCH(QString, path); + QFETCH(bool, postLoad); + + try { + // ignore retranslateUi which is called by evaluateComponentScript setExpectedScriptOutput("Component constructor - OK"); setExpectedScriptOutput("retranslateUi - OK"); - m_component->loadComponentScript(":///data/component1.qs"); + m_component->evaluateComponentScript(path, postLoad); setExpectedScriptOutput("retranslateUi - OK"); m_component->languageChanged(); @@ -422,8 +433,19 @@ private slots: } } + void loadBrokenComponentScript_data() + { + QTest::addColumn<QString>("path"); + QTest::addColumn<bool>("postLoad"); + QTest::newRow("Pre component script") << ":///data/component2.qs" << false; + QTest::newRow("Post component script") << ":///data/component2.qs" << true; + } + void loadBrokenComponentScript() { + QFETCH(QString, path); + QFETCH(bool, postLoad); + Component *testComponent = new Component(&m_core); testComponent->setValue(scName, "broken.component"); @@ -433,21 +455,31 @@ private slots: try { // ignore Output from script setExpectedScriptOutput("script function: Component"); - testComponent->loadComponentScript(":///data/component2.qs"); + testComponent->evaluateComponentScript(path, postLoad); } catch (const Error &error) { const QString debugMessage( - QString("create Error-Exception: \"Exception while loading the component script \"%1\": " - "ReferenceError: broken is not defined\"").arg(QDir::toNativeSeparators(":///data/component2.qs"))); + QString("Exception while loading the component script \"%1\": " + "ReferenceError: broken is not defined on line number: 33").arg(QDir::toNativeSeparators(":///data/component2.qs"))); QVERIFY2(debugMessage.contains(error.message()), "(ReferenceError: broken is not defined)"); } } + void loadComponentUserInterfaces_data() + { + QTest::addColumn<QString>("path"); + QTest::addColumn<bool>("postLoad"); + QTest::newRow("Pre component script") << ":///data/userinterface.qs" << false; + QTest::newRow("Post component script") << ":///data/userinterface.qs" << true; + } + void loadComponentUserInterfaces() { - try { + QFETCH(QString, path); + QFETCH(bool, postLoad); + try { setExpectedScriptOutput("checked: false"); m_component->loadUserInterfaces(QDir(":///data"), QStringList() << QLatin1String("form.ui")); - m_component->loadComponentScript(":///data/userinterface.qs"); + m_component->evaluateComponentScript(path, postLoad); } catch (const Error &error) { QFAIL(qPrintable(error.message())); } @@ -591,7 +623,7 @@ private slots: try { m_core.setPackageManager(); Component *component = m_core.componentByName("component.test.addOperation"); - component->loadComponentScript(":///data/addOperation.qs"); + component->evaluateComponentScript(":///data/addOperation.qs"); setExpectedScriptOutput("Component::createOperations()"); component->createOperations(); |