summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKatja Marttila <katja.marttila@qt.io>2018-02-28 09:54:22 +0200
committerKatja Marttila <katja.marttila@qt.io>2018-04-05 06:54:21 +0000
commitd2ae9c16c3427c272d41e9a471f705ad8947a97a (patch)
tree4ed6b3cafd28b11f02ef4f4faa6164bcfbd85b63
parent6664ca85f09d6ae195ac30f83a60d53c2355da0f (diff)
Add attribute to mark parts of install tree unstable
This commit adds new AllowUnstableComponents configuration. Setting AllowUnstablecomponents to true in config.xml will * allow installing other components when there are errors in scripts * allow installing other components when there are missing dependencies * will mark the 'broken' components uninstallable in treeview Task-number: QTIFW-930 Change-Id: I8d28cf9c4b0401e0bb76795e87d581f39b64f128 Reviewed-by: Iikka Eklund <iikka.eklund@qt.io>
-rw-r--r--examples/unstablecomponent/README5
-rw-r--r--examples/unstablecomponent/config/config.xml10
-rw-r--r--examples/unstablecomponent/packages/componentB/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml9
-rw-r--r--examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js33
-rw-r--r--examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml10
-rw-r--r--examples/unstablecomponent/packages/componentE/meta/package.xml9
-rw-r--r--examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml9
-rw-r--r--examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentF/meta/package.xml8
-rw-r--r--examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml9
-rw-r--r--examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt2
-rw-r--r--examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js33
-rw-r--r--examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml9
-rw-r--r--examples/unstablecomponent/unstablecomponent.pro13
-rw-r--r--src/libs/installer/component.cpp71
-rw-r--r--src/libs/installer/component.h4
-rw-r--r--src/libs/installer/component_p.h1
-rw-r--r--src/libs/installer/componentmodel.cpp7
-rw-r--r--src/libs/installer/constants.h1
-rw-r--r--src/libs/installer/installercalculator.cpp11
-rw-r--r--src/libs/installer/settings.cpp15
-rw-r--r--src/libs/installer/settings.h3
-rw-r--r--tests/auto/installer/brokeninstaller/brokeninstaller.pro8
-rw-r--r--tests/auto/installer/brokeninstaller/components.qrc6
-rw-r--r--tests/auto/installer/brokeninstaller/data/broken_script.qs31
-rw-r--r--tests/auto/installer/brokeninstaller/data/updates.xml163
-rw-r--r--tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp228
32 files changed, 744 insertions, 12 deletions
diff --git a/examples/unstablecomponent/README b/examples/unstablecomponent/README
new file mode 100644
index 000000000..6c081844a
--- /dev/null
+++ b/examples/unstablecomponent/README
@@ -0,0 +1,5 @@
+Shows how to setup an installer that allows install even if there are broken components
+
+Generate installer with:
+
+binarycreator -c config/config.xml -p packages installer
diff --git a/examples/unstablecomponent/config/config.xml b/examples/unstablecomponent/config/config.xml
new file mode 100644
index 000000000..bd9ac49ea
--- /dev/null
+++ b/examples/unstablecomponent/config/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Installer>
+ <Name>Unstable components Example</Name>
+ <Version>1.0.0</Version>
+ <Title>Unstable components Example</Title>
+ <Publisher>Qt-Project</Publisher>
+ <StartMenuDir>Qt IFW Examples</StartMenuDir>
+ <TargetDir>@HomeDir@/IfwExamples/unstablecomponent</TargetDir>
+ <AllowUnstableComponents>true</AllowUnstableComponents>
+</Installer>
diff --git a/examples/unstablecomponent/packages/componentB/meta/package.xml b/examples/unstablecomponent/packages/componentB/meta/package.xml
new file mode 100644
index 000000000..acf9a3a28
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentB/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component B</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>90</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml b/examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml
new file mode 100644
index 000000000..42d7cbb30
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>ComponentDependsonComponentMissingDependency</DisplayName>
+ <Description>This component depends on component which is depending on component with missing dependency. User is not able to select this component for install.</Description>
+ <Dependencies>componentMissingDependency</Dependencies>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>70</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js
new file mode 100644
index 000000000..7ad2f4db3
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js
@@ -0,0 +1,33 @@
+/**************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+function Component()
+{
+ component.addDependency("componentInvalidScipt");
+}
+
diff --git a/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml
new file mode 100644
index 000000000..4df66da40
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component depends on ComponentWithInvalidScript)</DisplayName>
+ <Description>Depends on component with invalid script. User is not able to select this component for install.</Description>
+ <Default>true</Default>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <Script>installscript.js</Script>
+ <SortingPriority>30</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentE/meta/package.xml b/examples/unstablecomponent/packages/componentE/meta/package.xml
new file mode 100644
index 000000000..a19c4f7a9
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentE/meta/package.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component E (forced)</DisplayName>
+ <Description>This is a forced component that is always installed.</Description>
+ <ForcedInstallation>true</ForcedInstallation>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>60</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml
new file mode 100644
index 000000000..717eb723f
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Subsubcomponent 1</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml
new file mode 100644
index 000000000..29bc65ffa
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Subsubcomponent 2</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>50</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml
new file mode 100644
index 000000000..7111a9031
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Subcomponent 1</DisplayName>
+ <Description>This component contains 2 leaf components.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+ <Dependencies>componentA</Dependencies>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml
new file mode 100644
index 000000000..717eb723f
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Subsubcomponent 1</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml
new file mode 100644
index 000000000..29bc65ffa
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Subsubcomponent 2</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>50</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml
new file mode 100644
index 000000000..131f4799e
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Subcomponent 2</DisplayName>
+ <Description>This component contains 2 leaf components.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>50</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentF/meta/package.xml b/examples/unstablecomponent/packages/componentF/meta/package.xml
new file mode 100644
index 000000000..7f24c4f35
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentF/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component F</DisplayName>
+ <Description>This component contains 2 subcomponents.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>40</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml b/examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml
new file mode 100644
index 000000000..52cb99c1a
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>ComponentMissingDependency</DisplayName>
+ <Description>This component depends on component which does not exists. User is not able to select this component for install.</Description>
+ <Dependencies>componentDoesnotExists</Dependencies>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+</Package>
diff --git a/examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt b/examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt
new file mode 100644
index 000000000..f40001983
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt
@@ -0,0 +1,2 @@
+This file will be installed into the target directory....
+
diff --git a/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js
new file mode 100644
index 000000000..4ca6a8816
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js
@@ -0,0 +1,33 @@
+/**************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+function Component()
+{
+ component.invalidScriptCall()
+}
+
diff --git a/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml
new file mode 100644
index 000000000..7bf69ccc4
--- /dev/null
+++ b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>ComponentWithInvalidScript</DisplayName>
+ <Description>This component has an invalid script. User is not able to select this component for install.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2018-02-02</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+ <Script>installscript.js</Script>
+</Package>
diff --git a/examples/unstablecomponent/unstablecomponent.pro b/examples/unstablecomponent/unstablecomponent.pro
new file mode 100644
index 000000000..415df49d5
--- /dev/null
+++ b/examples/unstablecomponent/unstablecomponent.pro
@@ -0,0 +1,13 @@
+TEMPLATE = aux
+
+INSTALLER = installer
+
+INPUT = $$PWD/config/config.xml $$PWD/packages
+example.input = INPUT
+example.output = $$INSTALLER
+example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT}
+example.CONFIG += target_predeps no_link combine
+
+QMAKE_EXTRA_COMPILERS += example
+
+OTHER_FILES = README
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index 9be2357cd..65dfbbc8d 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -67,6 +67,7 @@ static const QLatin1String scCurrentState("CurrentState");
static const QLatin1String scForcedInstallation("ForcedInstallation");
static const QLatin1String scCheckable("Checkable");
static const QLatin1String scExpandedByDefault("ExpandedByDefault");
+static const QLatin1String scUnstable("Unstable");
/*!
\inmodule QtInstallerFramework
@@ -511,9 +512,29 @@ void Component::loadComponentScript(const QString &fileName)
{
// introduce the component object as javascript value and call the name to check that it
// was successful
- d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
+ try {
+ d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
+ QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
+ .arg(name()));
+ if (packageManagerCore()->settings().allowUnstableComponents()) {
+ // Check if component has dependency to a broken component. Dependencies to broken
+ // components are checked if error is thrown but if dependency to a broken
+ // component is added in script, the script might not be loaded yet
+ foreach (QString dependency, dependencies()) {
+ Component *dependencyComponent = packageManagerCore()->componentByName
+ (PackageManagerCore::checkableName(dependency));
+ if (dependencyComponent && dependencyComponent->isUnstable())
+ setUnstable();
+ }
+ }
+ } catch (const Error &error) {
+ if (packageManagerCore()->settings().allowUnstableComponents()) {
+ setUnstable();
+ qWarning() << error.message();
+ } else {
+ throw error;
+ }
+ }
emit loaded();
languageChanged();
@@ -1008,6 +1029,13 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
return operation;
}
+void Component::markComponentUnstable()
+{
+ setValue(scDefault, scFalse);
+ setCheckState(Qt::Unchecked);
+ setValue(scUnstable, scTrue);
+}
+
namespace {
inline bool convert(QQmlV4Function *func, QStringList *toArgs)
@@ -1339,6 +1367,31 @@ bool Component::isUninstalled() const
return scUninstalled == d->m_vars.value(scCurrentState);
}
+bool Component::isUnstable() const
+{
+ return scTrue == d->m_vars.value(scUnstable);
+}
+
+void Component::setUnstable()
+{
+ QList<Component*> dependencies = d->m_core->dependees(this);
+ // Mark this component unstable
+ markComponentUnstable();
+
+ // Marks all components unstable that depend on the unstable component
+ foreach (Component *dependency, dependencies) {
+ dependency->markComponentUnstable();
+ foreach (Component *descendant, dependency->descendantComponents()) {
+ descendant->markComponentUnstable();
+ }
+ }
+
+ // Marks all child components unstable
+ foreach (Component *descendant, this->descendantComponents()) {
+ descendant->markComponentUnstable();
+ }
+}
+
/*!
Returns whether the user wants to uninstall the component.
@@ -1424,13 +1477,21 @@ void Component::updateModelData(const QString &key, const QString &data)
const QString &updateInfo = d->m_vars.value(scUpdateText);
if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
- const QString tooltipText
+ QString tooltipText
= QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
+ if (isUnstable()) {
+ tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
+ "This component can not be installed.");
+ }
setData(tooltipText, Qt::ToolTipRole);
} else {
- const QString tooltipText
+ QString tooltipText
= d->m_vars.value(scDescription) + QLatin1String("<br><br>")
+ tr("Update Info: ") + updateInfo;
+ if (isUnstable()) {
+ tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
+ "This component can not be updated.");
+ }
setData(tooltipText, Qt::ToolTipRole);
}
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
index b11fd4cef..574117b6d 100644
--- a/src/libs/installer/component.h
+++ b/src/libs/installer/component.h
@@ -62,6 +62,7 @@ class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper
Q_PROPERTY(bool default READ isDefault)
Q_PROPERTY(bool installed READ isInstalled)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(bool unstable READ isUnstable)
public:
explicit Component(PackageManagerCore *core);
@@ -182,6 +183,8 @@ public:
Q_INVOKABLE bool componentChangeRequested();
+ bool isUnstable() const;
+ void setUnstable();
bool isVirtual() const;
bool isSelected() const;
@@ -212,6 +215,7 @@ private:
const QString &parameter8 = QString(), const QString &parameter9 = QString(),
const QString &parameter10 = QString());
Operation *createOperation(const QString &operationName, const QStringList &parameters);
+ void markComponentUnstable();
private:
QString validatorCallbackName;
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
index 60cbd8075..b9f50b557 100644
--- a/src/libs/installer/component_p.h
+++ b/src/libs/installer/component_p.h
@@ -63,6 +63,7 @@ public:
bool m_autoCreateOperations;
bool m_operationsCreatedSuccessfully;
bool m_updateIsAvailable;
+ bool m_unstable;
QString m_componentName;
QUrl m_repositoryUrl;
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 0f8a47c38..078afc50a 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -222,14 +222,15 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
return component->data(Qt::UserRole + index.column());
}
if (role == Qt::CheckStateRole) {
- if (!component->isCheckable())
- return QVariant();
- if (!component->autoDependencies().isEmpty())
+ if (!component->isCheckable() || !component->autoDependencies().isEmpty() || component->isUnstable())
return QVariant();
}
if (role == ComponentModelHelper::ExpandedByDefault) {
return component->isExpandedByDefault();
}
+ if (component->isUnstable() && role == Qt::ForegroundRole) {
+ return QVariant(QColor(Qt::lightGray));
+ }
return component->data(role);
}
return QVariant();
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 7f44acbe7..ddfa8c555 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -89,6 +89,7 @@ static const QLatin1String scUrlQueryString("UrlQueryString");
static const QLatin1String scProductUUID("ProductUUID");
static const QLatin1String scAllUsers("AllUsers");
static const QLatin1String scSupportsModify("SupportsModify");
+static const QLatin1String scAllowUnstableComponents("AllowUnstableComponents");
const char scRelocatable[] = "@RELOCATABLE_PATH@";
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index d44450eb2..4e0d7fdfa 100644
--- a/src/libs/installer/installercalculator.cpp
+++ b/src/libs/installer/installercalculator.cpp
@@ -30,6 +30,7 @@
#include "component.h"
#include "packagemanagercore.h"
+#include "settings.h"
#include <QDebug>
@@ -94,7 +95,8 @@ QString InstallerCalculator::componentsToInstallError() const
void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version)
{
- if (!component->isInstalled(version) || component->updateRequested()) {
+ if (!component->isUnstable() &&
+ (!component->isInstalled(version) || component->updateRequested())) {
m_orderedComponentsToInstall.append(component);
m_toInstallComponentIds.insert(component->name());
}
@@ -169,7 +171,12 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
component->name());
qWarning().noquote() << errorMessage;
m_componentsToInstallError.append(errorMessage);
- return false;
+ if (component->packageManagerCore()->settings().allowUnstableComponents()) {
+ component->setUnstable();
+ return true;
+ } else {
+ return false;
+ }
}
//Check if component requires higher version than what might be already installed
bool isUpdateRequired = false;
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
index 79d4bcddc..3d5d4b858 100644
--- a/src/libs/installer/settings.cpp
+++ b/src/libs/installer/settings.cpp
@@ -256,7 +256,7 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
<< scWizardDefaultWidth << scWizardDefaultHeight
<< scRepositorySettingsPageVisible << scTargetConfigurationFile
<< scRemoteRepositories << scTranslations << scUrlQueryString << QLatin1String(scControlScript)
- << scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify;
+ << scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify << scAllowUnstableComponents;
Settings s;
s.d->m_data.insert(scPrefix, prefix);
@@ -325,7 +325,8 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
s.d->m_data.insert(scCreateLocalRepository, false);
if (!s.d->m_data.contains(scInstallActionColumnVisible))
s.d->m_data.insert(scInstallActionColumnVisible, false);
-
+ if (!s.d->m_data.contains(scAllowUnstableComponents))
+ s.d->m_data.insert(scAllowUnstableComponents, false);
return s;
}
@@ -743,3 +744,13 @@ bool Settings::supportsModify() const
{
return d->m_data.value(scSupportsModify, true).toBool();
}
+
+bool Settings::allowUnstableComponents() const
+{
+ return d->m_data.value(scAllowUnstableComponents, true).toBool();
+}
+
+void Settings::setAllowUnstableComponents(bool allow)
+{
+ d->m_data.insert(scAllowUnstableComponents, allow);
+}
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index 3dc1c99c3..45c4fbcc3 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -154,6 +154,9 @@ public:
bool supportsModify() const;
+ bool allowUnstableComponents() const;
+ void setAllowUnstableComponents(bool allow);
+
private:
class Private;
QSharedDataPointer<Private> d;
diff --git a/tests/auto/installer/brokeninstaller/brokeninstaller.pro b/tests/auto/installer/brokeninstaller/brokeninstaller.pro
new file mode 100644
index 000000000..e48733ac6
--- /dev/null
+++ b/tests/auto/installer/brokeninstaller/brokeninstaller.pro
@@ -0,0 +1,8 @@
+include(../../qttest.pri)
+
+QT -= gui
+QT += network xml qml
+
+SOURCES += tst_brokeninstaller.cpp
+
+RESOURCES += components.qrc
diff --git a/tests/auto/installer/brokeninstaller/components.qrc b/tests/auto/installer/brokeninstaller/components.qrc
new file mode 100644
index 000000000..1431393a6
--- /dev/null
+++ b/tests/auto/installer/brokeninstaller/components.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/updates.xml</file>
+ <file>data/broken_script.qs</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/brokeninstaller/data/broken_script.qs b/tests/auto/installer/brokeninstaller/data/broken_script.qs
new file mode 100644
index 000000000..a1574f46d
--- /dev/null
+++ b/tests/auto/installer/brokeninstaller/data/broken_script.qs
@@ -0,0 +1,31 @@
+/**************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+function Component()
+{
+ installer.invalidScriptCall()
+}
diff --git a/tests/auto/installer/brokeninstaller/data/updates.xml b/tests/auto/installer/brokeninstaller/data/updates.xml
new file mode 100644
index 000000000..a20275120
--- /dev/null
+++ b/tests/auto/installer/brokeninstaller/data/updates.xml
@@ -0,0 +1,163 @@
+<Updates>
+ <ApplicationName>Your application</ApplicationName>
+ <ApplicationVersion>1.2.3</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>componentinvalidscript</Name>
+ <DisplayName>Invalid script component</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Default>true</Default>
+ <SortingPriority>1</SortingPriority>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentb</Name>
+ <DisplayName>The second root component</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Default>true</Default>
+ <Script>installscript.qs</Script>
+ <SortingPriority>1</SortingPriority>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentc</Name>
+ <DisplayName>The third root component</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Default>true</Default>
+ <Script>installscript.qs</Script>
+ <SortingPriority>1</SortingPriority>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentc.subnode</Name>
+ <DisplayName>A subcomponent for the third root, dependency to component with invalid script</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Default>true</Default>
+ <Script>installscript.qs</Script>
+ <SortingPriority>1</SortingPriority>
+ <Dependencies>componentinvalidscript</Dependencies>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentc.subnode.sub</Name>
+ <DisplayName>A subcomponent for the third roots subcomponent</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Script>installscript.qs</Script>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentmissingdependency</Name>
+ <DisplayName>A component that has missing sha</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Default>true</Default>
+ <Script>installscript.qs</Script>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentd</Name>
+ <DisplayName>Component that depends on component which is missing dependency</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Script>installscript.qs</Script>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentmissingcontent</Name>
+ <DisplayName>A component which is missing 7z</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Default>true</Default>
+ <Script>installscript.qs</Script>
+ <SortingPriority>0</SortingPriority>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentmissingcontent.sub</Name>
+ <DisplayName>A subcomponent of component which is missing 7z</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Checkable>true</Checkable>
+ <Default>true</Default>
+ <Script>installscript.qs</Script>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentE</Name>
+ <DisplayName>A component which is dependent on subcomponent of missing 7z component</DisplayName>
+ <Description>Install this example.</Description>
+ <Version>0.1.0-1</Version>
+ <ReleaseDate>2010-09-21</ReleaseDate>
+ <Script>installscript.qs</Script>
+ <Checkable>true</Checkable>
+ <Default>true</Default>
+ <Dependencies>componentmissingcontent.sub</Dependencies>
+ <UpdateFile UncompressedSize="61"
+ CompressedSize="61"/>
+ <Licenses>
+ <License name="Beer Public License Agreement"
+ file="license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
new file mode 100644
index 000000000..95f4ac77d
--- /dev/null
+++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
@@ -0,0 +1,228 @@
+/**************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "component.h"
+#include "componentmodel.h"
+#include "updatesinfo_p.h"
+#include "packagemanagercore.h"
+#include "settings.h"
+
+#include <QTest>
+#include <QtCore/QLocale>
+#include <QRegularExpression>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+#define EXPECTED_COUNTS_COMPONENTS_VISIBLE 10
+#define EXPECTED_COUNT_ROOT_COMPONENTS 7
+#define EXPECTED_COUNT_INVALID_SCRIPT 7
+#define EXPECTED_COUNT_MISSING_DEPENDENCY 8
+
+static const char componentInvalidScript[] = "componentinvalidscript";
+static const char componentB[] = "componentb";
+
+static const char componentC[] = "componentc";
+static const char componentCSubnodeDependsOnInvalidScriptComponent[] = "componentc.subnode";
+static const char componentCSubnodeDependsOnInvalidScriptComponentSub[] = "componentc.subnode.sub";
+
+static const char componentMissingDependency[] = "componentmissingdependency";
+static const char componentDependsOnMissingDependency[] = "componentd";
+
+static const char componentMissingContent[] = "componentmissingcontent";
+static const char componentMissingContentSub[] = "componentmissingcontent.sub";
+static const char componentDependsOnMissingContentSub[] = "componentE";
+
+static const QMap<QString, QString> rootComponentDisplayNames = {
+ {"", QLatin1String("The root component")},
+ {"ru_ru", QString::fromUtf8("Корневая компонента")},
+ {"de_de", QString::fromUtf8("Wurzel Komponente")}
+};
+
+using namespace QInstaller;
+
+class tst_BrokenInstaller : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Option {
+ NoFlags = 0x00,
+ VirtualsVisible = 0x01,
+ NoForcedInstallation = 0x02
+ };
+ Q_DECLARE_FLAGS(Options, Option);
+
+private slots:
+ void initTestCase()
+ {
+
+ m_checkedComponentsWithBrokenScript << componentB << componentMissingDependency << componentDependsOnMissingDependency << componentMissingContent
+ << componentMissingContentSub << componentDependsOnMissingContentSub;
+ m_uncheckedComponentsWithBrokenScript << componentInvalidScript << componentCSubnodeDependsOnInvalidScriptComponent
+ << componentCSubnodeDependsOnInvalidScriptComponentSub << componentC;
+
+ m_checkedComponentsWithMissingDependency << componentB << componentMissingContent << componentMissingContentSub << componentDependsOnMissingContentSub
+ << componentInvalidScript << componentCSubnodeDependsOnInvalidScriptComponent
+ << componentCSubnodeDependsOnInvalidScriptComponentSub << componentC;
+ m_uncheckedComponentsWithMissingDependency << componentMissingDependency << componentDependsOnMissingDependency;
+
+ }
+
+ void testNameToIndexAndIndexToName()
+ {
+ PackageManagerCore core;
+ QList<Component*> rootComponents = loadComponents(core);
+
+ ComponentModel *model = new ComponentModel(1, &core);
+ model->setRootComponents(rootComponents);
+ // all names should be resolvable
+ QStringList all;
+ all << m_checkedComponentsWithBrokenScript << m_uncheckedComponentsWithBrokenScript << m_partiallyCheckedComponentsWithBrokenScript;
+ foreach (const QString &name, all) {
+ QVERIFY(model->indexFromComponentName(name).isValid());
+ QVERIFY(model->componentFromIndex(model->indexFromComponentName(name)) != 0);
+ QCOMPARE(model->componentFromIndex(model->indexFromComponentName(name))->name(), name);
+ }
+ }
+
+ void testInvalidScript()
+ {
+ PackageManagerCore core;
+ QList<Component*> components = loadComponents(core);
+
+ core.settings().setAllowUnstableComponents(true);
+ ComponentModel *model = new ComponentModel(1, &core);
+ Component *invalidScriptComponent = core.componentByName("componentinvalidscript");
+
+ // Using regexp to ignore the warning message as the message contains
+ // object address which we don't know
+ const QString debugMessage = QString("Exception while loading the component script");
+ const QRegularExpression re(debugMessage);
+ QTest::ignoreMessage(QtWarningMsg, re);
+ invalidScriptComponent->loadComponentScript(":///data/broken_script.qs");
+
+ model->setRootComponents(components);
+ testModelState(model, m_checkedComponentsWithBrokenScript, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithBrokenScript);
+ }
+
+ void testMissingDependency()
+ {
+ PackageManagerCore core;
+ QList<Component*> components = loadComponents(core);
+
+ core.settings().setAllowUnstableComponents(true);
+ ComponentModel *model = new ComponentModel(1, &core);
+
+ Component *invalidComponent = core.componentByName("componentmissingdependency");
+ invalidComponent->addDependency("missingcomponent");
+
+ Component *componentDependingOnMissingDependency = core.componentByName("componentd");
+ componentDependingOnMissingDependency->addDependency("componentmissingdependency");
+
+ core.componentsToInstallNeedsRecalculation();
+ model->setRootComponents(components);
+
+ testModelState(model, m_checkedComponentsWithMissingDependency, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithMissingDependency);
+ }
+
+private:
+
+ QList<Component*> loadComponents(PackageManagerCore &core)
+ {
+ UpdatesInfo updatesInfo;
+ updatesInfo.setFileName(":///data/updates.xml");
+ const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo();
+
+ QList <Component*> components;
+ UpdateInfo info;
+ for (int i = 0; i < updateInfos.count(); i++) {
+ info = updateInfos.at(i);
+ Component *component = new Component(&core);
+
+ // we need at least these to be able to test the model
+ component->setValue("Name", info.data.value("Name").toString());
+ component->setValue("Virtual", info.data.value("Virtual").toString());
+ component->setValue("DisplayName", info.data.value("DisplayName").toString());
+ component->setValue("Checkable", info.data.value("Checkable").toString());
+ component->setValue("Dependencies", info.data.value("Dependencies").toString());
+ component->setCheckState(Qt::Checked);
+ QString forced = info.data.value("ForcedInstallation", scFalse).toString().toLower();
+ if (core.noForceInstallation())
+ forced = scFalse;
+ component->setValue("ForcedInstallation", forced);
+ if (forced == scTrue) {
+ component->setEnabled(false);
+ component->setCheckable(false);
+ }
+ //If name does not contain dot it is root component
+ qDebug()<<"component name "<<component->name();
+ if (!component->name().contains('.')) {
+ core.appendRootComponent(component);
+ } else { //Contains dot, is previous components child component
+ components.at(i-1)->appendComponent(component);
+ }
+ components.append(component);
+ }
+ return components;
+ }
+
+ void testModelState(ComponentModel *model, const QStringList &checked, const QStringList &partially,
+ const QStringList &unchecked) const
+ {
+ QCOMPARE(model->checked().count(), checked.count());
+ QCOMPARE(model->partially().count(), partially.count());
+ QCOMPARE(model->unchecked().count(), unchecked.count());
+
+ // these components should have checked state
+ foreach (Component *const component, model->checked())
+ QVERIFY(checked.contains(component->name()));
+
+ // these components should not have partially checked state
+ foreach (Component *const component, model->partially())
+ QVERIFY(partially.contains(component->name()));
+
+ // these components should not have checked state
+ foreach (Component *const component, model->unchecked())
+ QVERIFY(unchecked.contains(component->name()));
+ }
+
+private:
+ QStringList m_checkedComponentsWithBrokenScript;
+ QStringList m_uncheckedComponentsWithBrokenScript;
+ QStringList m_partiallyCheckedComponentsWithBrokenScript;
+
+ QStringList m_checkedComponentsWithMissingDependency;
+ QStringList m_uncheckedComponentsWithMissingDependency;
+ QStringList m_partiallyCheckedComponentsWithMissingDependency;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(tst_BrokenInstaller::Options)
+
+QTEST_MAIN(tst_BrokenInstaller)
+
+#include "tst_brokeninstaller.moc"