summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--doc/installerfw.qdoc9
-rw-r--r--src/libs/ifwtools/repositorygen.cpp79
-rw-r--r--src/libs/ifwtools/repositorygen.h3
-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
-rw-r--r--src/libs/kdtools/updatesinfo.cpp9
-rw-r--r--tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp4
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml10
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7zbin0 -> 209 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7zbin0 -> 1010 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/settings.qrc2
-rw-r--r--tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp15
-rw-r--r--tests/auto/installer/scriptengine/tst_scriptengine.cpp52
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 &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);
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
new file mode 100644
index 000000000..58ff52baa
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z
Binary files differ
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
new file mode 100644
index 000000000..be99410a8
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z
Binary files differ
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();