summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/config/config.xml6
-rw-r--r--dist/packages/org.qtproject.ifw.binaries/meta/package.xml4
-rw-r--r--dist/packages/org.qtproject.ifw/meta/package.xml4
-rw-r--r--doc/installerfw.qdoc21
-rw-r--r--doc/scripting-api/qdesktopservices.qdoc8
-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--installerfw.pri4
-rw-r--r--src/libs/installer/component.cpp92
-rw-r--r--src/libs/installer/component.h5
-rw-r--r--src/libs/installer/component_p.cpp16
-rw-r--r--src/libs/installer/component_p.h7
-rw-r--r--src/libs/installer/componentmodel.cpp10
-rw-r--r--src/libs/installer/constants.h2
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp2
-rw-r--r--src/libs/installer/globals.cpp2
-rw-r--r--src/libs/installer/installercalculator.cpp17
-rw-r--r--src/libs/installer/packagemanagercore.cpp67
-rw-r--r--src/libs/installer/packagemanagercore.h4
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp12
-rw-r--r--src/libs/installer/packagemanagergui.cpp14
-rw-r--r--src/libs/installer/scriptengine.cpp24
-rw-r--r--src/libs/installer/scriptengine_p.h10
-rw-r--r--src/libs/installer/settings.cpp15
-rw-r--r--src/libs/installer/settings.h3
-rw-r--r--src/libs/installer/uninstallercalculator.cpp2
-rw-r--r--src/libs/kdtools/localpackagehub.cpp9
-rw-r--r--src/libs/kdtools/localpackagehub.h4
-rw-r--r--src/libs/kdtools/updatefinder.cpp56
-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
-rw-r--r--tests/auto/installer/compareversion/compareversion.pro6
-rw-r--r--tests/auto/installer/compareversion/tst_compareversion.cpp93
-rw-r--r--tests/auto/installer/componentidentifier/componentidentifier.pro6
-rw-r--r--tests/auto/installer/componentidentifier/tst_componentidentifier.cpp252
-rw-r--r--tests/auto/installer/installer.pro2
-rw-r--r--tests/auto/installer/scriptengine/tst_scriptengine.cpp28
-rw-r--r--tests/auto/installer/solver/tst_solver.cpp10
-rw-r--r--tools/binarycreator/binarycreator.cpp44
-rw-r--r--tools/common/repositorygen.cpp674
-rw-r--r--tools/common/repositorygen.h6
-rw-r--r--tools/repogen/repogen.cpp30
62 files changed, 1876 insertions, 338 deletions
diff --git a/dist/config/config.xml b/dist/config/config.xml
index 515aa4b18..a0e049792 100644
--- a/dist/config/config.xml
+++ b/dist/config/config.xml
@@ -1,13 +1,13 @@
<?xml version="1.0"?>
<Installer>
<Name>Qt Installer Framework</Name>
- <Title>Qt Installer Framework 3.0.4</Title>
- <Version>3.0.4</Version>
+ <Title>Qt Installer Framework 3.1.81</Title>
+ <Version>3.1.81</Version>
<Publisher>Qt Project</Publisher>
<ProductUrl>http://qt-project.org</ProductUrl>
<Watermark>watermark.png</Watermark>
<MaintenanceToolName>Uninstaller</MaintenanceToolName>
<!-- Tweaked for windows in installscript.qs -->
- <TargetDir>@HomeDir@/Qt/QtIFW-3.0.4</TargetDir>
+ <TargetDir>@HomeDir@/Qt/QtIFW-3.1.81</TargetDir>
</Installer>
diff --git a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml
index d571b887d..0d6a8ac22 100644
--- a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml
+++ b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml
@@ -2,7 +2,7 @@
<Package>
<DisplayName>Qt Installer Framework Binaries</DisplayName>
<Description>Installs the binaries, examples and help files.</Description>
- <Version>3.0.4</Version>
- <ReleaseDate>2018-03-20</ReleaseDate>
+ <Version>3.1.81</Version>
+ <ReleaseDate>2017-04-06</ReleaseDate>
<Default>True</Default>
</Package>
diff --git a/dist/packages/org.qtproject.ifw/meta/package.xml b/dist/packages/org.qtproject.ifw/meta/package.xml
index 371b0c973..4f5628391 100644
--- a/dist/packages/org.qtproject.ifw/meta/package.xml
+++ b/dist/packages/org.qtproject.ifw/meta/package.xml
@@ -2,8 +2,8 @@
<Package>
<DisplayName>Qt Installer Framework</DisplayName>
<Description>Installs the Qt Installer Framework.</Description>
- <Version>3.0.2</Version>
- <ReleaseDate>2017-11-21</ReleaseDate>
+ <Version>3.1.81</Version>
+ <ReleaseDate>2017-04-06</ReleaseDate>
<Licenses>
<License name="The Qt Company GPL Exception 1.0" file="LICENSE.GPL3-EXCEPT" />
<License name="Third Party Code Licenses" file="3RDPARTY" />
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc
index 23d0c9e47..669fc66f4 100644
--- a/doc/installerfw.qdoc
+++ b/doc/installerfw.qdoc
@@ -691,6 +691,9 @@
\li Checkable
\li Set to \c false if you want to hide the checkbox for an item. This is useful
when only a few subcomponents should be selected instead of all. Optional.
+ \row
+ \li ExpandedByDefault
+ \li Set to \c true if you want this item to be expanded by default. Optional.
\endtable
@@ -822,6 +825,11 @@
{package directory}.
Defaults to the current working directory.
\row
+ \li --repository directory
+ \li Use \c directory as the repository directory
+ with packages to repack.
+ This entry can be given multiple times.
+ \row
\li -n or --online-only
\li Compile without any component in the installer binary.
\row
@@ -853,6 +861,10 @@
\li Ignore component or package directories that do not have valid
metadata information (package.xml) to make testing faster.
\row
+ \li --ignore-invalid-repositories
+ \li Ignore repository directories that do not have valid
+ metadata information (Updates.xml) instead of aborting.
+ \row
\li -v or --verbose
\li Display debug output.
@@ -906,6 +918,9 @@
specify the location in the installer configuration file when creating an
installer for it.
+ You can use an existing repository to repack packages to another
+ repository or offline installer.
+
\section2 Summary of repogen Parameters
\table
@@ -917,6 +932,12 @@
\li Use \c directory as the \l{Package Directory Structure}
{package directory}. This is mandatory.
\row
+ \li --repository directory
+ \li Use \c directory as the repository directory
+ with packages to repack (not to confuse with the mandatory target
+ repository directory).
+ This entry can be given multiple times.
+ \row
\li repository directory
\li Target directory for the repository. During an initial installation, the directory
must not yet exist. When updating, the directory may exist and its contents may be
diff --git a/doc/scripting-api/qdesktopservices.qdoc b/doc/scripting-api/qdesktopservices.qdoc
index 037a0b6dd..7c2b31ea3 100644
--- a/doc/scripting-api/qdesktopservices.qdoc
+++ b/doc/scripting-api/qdesktopservices.qdoc
@@ -99,3 +99,11 @@
Returns the specified \a location.
*/
+
+/*!
+ \qmlmethod array QDesktopServices::findFiles(string path, string pattern)
+
+ Returns file names matching \a pattern. Searches the files recursively from \a path.
+ \a Pattern understands * and ? wildcards.
+*/
+
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/installerfw.pri b/installerfw.pri
index cb37042d6..2810e5144 100644
--- a/installerfw.pri
+++ b/installerfw.pri
@@ -3,8 +3,8 @@
}
IFW_PRI_INCLUDED = 1
-IFW_VERSION_STR = 3.0.4
-IFW_VERSION = 0x030004
+IFW_VERSION_STR = 3.1.81
+IFW_VERSION = 0x030181
IFW_REPOSITORY_FORMAT_VERSION = 1.0.0
IFW_NEWLINE = $$escape_expand(\\n\\t)
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index da0230ade..65dfbbc8d 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -66,6 +66,8 @@ static const QLatin1String scUninstalled("Uninstalled");
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
@@ -262,6 +264,7 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package)
setValue(scVirtual, package.virtualComp ? scTrue : scFalse);
setValue(scCurrentState, scInstalled);
setValue(scCheckable, package.checkable ? scTrue : scFalse);
+ setValue(scExpandedByDefault, package.expandedByDefault ? scTrue : scFalse);
}
/*!
@@ -295,6 +298,7 @@ void Component::loadDataFromPackage(const Package &package)
setValue(scReplaces, package.data(scReplaces).toString());
setValue(scReleaseDate, package.data(scReleaseDate).toString());
setValue(scCheckable, package.data(scCheckable).toString());
+ setValue(scExpandedByDefault, package.data(scExpandedByDefault).toString());
QString forced = package.data(scForcedInstallation, scFalse).toString().toLower();
if (PackageManagerCore::noForceInstallation())
@@ -388,6 +392,8 @@ void Component::setValue(const QString &key, const QString &value)
d->m_componentName = normalizedValue;
if (key == scCheckable)
this->setCheckable(normalizedValue.toLower() == scTrue);
+ if (key == scExpandedByDefault)
+ this->setExpandedByDefault(normalizedValue.toLower() == scTrue);
d->m_vars[key] = normalizedValue;
emit valueChanged(key, normalizedValue);
@@ -506,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();
@@ -1003,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)
@@ -1170,6 +1203,22 @@ QStringList Component::dependencies() const
return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
}
+/*!
+ Adds the component specified by \a newDependOn to the automatic depend-on list.
+
+ \sa {component::addAutoDependOn}{component.addAutoDependOn}
+ \sa dependencies
+*/
+
+void Component::addAutoDependOn(const QString &newDependOn)
+{
+ QString oldDependOn = d->m_vars.value(scAutoDependOn);
+ if (oldDependOn.isEmpty())
+ setValue(scAutoDependOn, newDependOn);
+ else
+ setValue(scAutoDependOn, oldDependOn + QLatin1String(", ") + newDependOn);
+}
+
QStringList Component::autoDependencies() const
{
return d->m_vars.value(scAutoDependOn).split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
@@ -1318,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.
@@ -1403,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 01622548f..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);
@@ -154,6 +155,7 @@ public:
Q_INVOKABLE void addDependency(const QString &newDependency);
QStringList dependencies() const;
+ Q_INVOKABLE void addAutoDependOn(const QString &newDependOn);
QStringList autoDependencies() const;
void languageChanged();
@@ -181,6 +183,8 @@ public:
Q_INVOKABLE bool componentChangeRequested();
+ bool isUnstable() const;
+ void setUnstable();
bool isVirtual() const;
bool isSelected() const;
@@ -211,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.cpp b/src/libs/installer/component_p.cpp
index 4bf2a4754..5f44f83a4 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -198,6 +198,22 @@ void ComponentModelHelper::setSelectable(bool selectable)
changeFlags(selectable, Qt::ItemIsSelectable);
}
+/*!
+ Returns whether the component is expanded by default. The default value is \c false.
+*/
+bool ComponentModelHelper::isExpandedByDefault() const
+{
+ return data(ComponentModelHelper::ExpandedByDefault).value<bool>();
+}
+
+/*!
+ Sets whether the component is expanded by default. The default value is \c false.
+*/
+void ComponentModelHelper::setExpandedByDefault(bool expandedByDefault)
+{
+ setData(QVariant::fromValue<bool>(expandedByDefault), ComponentModelHelper::ExpandedByDefault);
+}
+
ComponentModelHelper::InstallAction ComponentModelHelper::installAction() const
{
return data(ComponentModelHelper::Action).value<ComponentModelHelper::InstallAction>();
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
index f6e9ac2bb..05b169d4b 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;
@@ -91,7 +92,8 @@ public:
LocalDisplayVersion,
RemoteDisplayVersion,
ReleaseDate,
- UncompressedSize
+ UncompressedSize,
+ ExpandedByDefault
};
enum InstallAction {
@@ -130,6 +132,9 @@ public:
bool isSelectable() const;
void setSelectable(bool selectable);
+ bool isExpandedByDefault() const;
+ void setExpandedByDefault(bool expandedByDefault);
+
InstallAction installAction() const;
void setInstallAction(InstallAction action);
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 57fc05181..8201248f8 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -222,11 +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::darkGray));
+ }
return component->data(role);
}
return QVariant();
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index f1eac9926..ddfa8c555 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -60,6 +60,7 @@ static const QLatin1String scInstalledVersion("InstalledVersion");
static const QLatin1String scUncompressedSize("UncompressedSize");
static const QLatin1String scUncompressedSizeSum("UncompressedSizeSum");
static const QLatin1String scRequiresAdminRights("RequiresAdminRights");
+static const QLatin1String scSHA1("SHA1");
// constants used throughout the components class
static const QLatin1String scVirtual("Virtual");
@@ -88,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/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
index 8fd8a40a9..80c094ae0 100644
--- a/src/libs/installer/downloadarchivesjob.cpp
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -269,7 +269,7 @@ KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &s
{
KDUpdater::FileDownloader *downloader = 0;
const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
- const Component *const component = m_core->componentByName(QFileInfo(fi.path()).fileName());
+ const Component *const component = m_core->componentByName(PackageManagerCore::checkableName(QFileInfo(fi.path()).fileName()));
if (component) {
QString fullQueryString;
if (!queryString.isEmpty())
diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp
index 5f3009905..c059b6630 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -50,7 +50,7 @@ QStringList loggingCategories()
return categories;
}
-Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, staticCommaRegExp, (QLatin1String("\\b(,|, )\\b")));
+Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, staticCommaRegExp, (QLatin1String("(, |,)")));
QRegExp commaRegExp()
{
return *staticCommaRegExp();
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index 064dbafa5..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,17 +171,24 @@ 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;
- if (dependencyComponentName.contains(QChar::fromLatin1('-')) &&
+ QString requiredName;
+ QString requiredVersion;
+ PackageManagerCore::parseNameAndVersion(dependencyComponentName, &requiredName, &requiredVersion);
+ if (!requiredVersion.isEmpty() &&
!dependencyComponent->value(scInstalledVersion).isEmpty()) {
QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ?
compEx.cap(2) : dependencyComponent->value(scInstalledVersion);
- QString requiredVersion = dependencyComponentName.section(QLatin1Char('-'), 1);
requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion;
if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) {
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index c905caaf1..4e8931d9a 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -727,7 +727,7 @@ void PackageManagerCore::rollBackInstallation()
const QString componentName = operation->value(QLatin1String("component")).toString();
if (!componentName.isEmpty()) {
- Component *component = componentByName(componentName);
+ Component *component = componentByName(checkableName(componentName));
if (!component)
component = d->componentsToReplace().value(componentName).second;
if (component) {
@@ -1559,12 +1559,9 @@ Component *PackageManagerCore::componentByName(const QString &name, const QList<
return 0;
QString fixedVersion;
- QString fixedName = name;
- if (name.contains(QChar::fromLatin1('-'))) {
- // the last part is considered to be the version, then
- fixedVersion = name.section(QLatin1Char('-'), 1);
- fixedName = name.section(QLatin1Char('-'), 0, 0);
- }
+ QString fixedName;
+
+ parseNameAndVersion(name, &fixedName, &fixedVersion);
foreach (Component *component, components) {
if (componentMatches(component, fixedName, fixedVersion))
@@ -1759,14 +1756,13 @@ QList<Component*> PackageManagerCore::dependees(const Component *_component) con
if (availableComponents.isEmpty())
return QList<Component *>();
- const QLatin1Char dash('-');
QList<Component *> dependees;
+ QString name;
+ QString version;
foreach (Component *component, availableComponents) {
const QStringList &dependencies = component->dependencies();
foreach (const QString &dependency, dependencies) {
- // the last part is considered to be the version then
- const QString name = dependency.contains(dash) ? dependency.section(dash, 0, 0) : dependency;
- const QString version = dependency.contains(dash) ? dependency.section(dash, 1) : QString();
+ parseNameAndVersion(dependency, &name, &version);
if (componentMatches(_component, name, version))
dependees.append(component);
}
@@ -2953,3 +2949,52 @@ void PackageManagerCore::addFilesForDelayedDeletion(const QStringList &files)
{
d->m_filesForDelayedDeletion.append(files);
}
+
+QString PackageManagerCore::checkableName(const QString &name)
+{
+ // to ensure backward compatibility, fix component name with dash (-) symbol
+ if (!name.contains(QLatin1Char(':')))
+ if (name.contains(QLatin1Char('-')))
+ return name + QLatin1Char(':');
+
+ return name;
+}
+
+void PackageManagerCore::parseNameAndVersion(const QString &requirement, QString *name, QString *version)
+{
+ if (requirement.isEmpty()) {
+ if (name)
+ name->clear();
+ if (version)
+ version->clear();
+ return;
+ }
+
+ int pos = requirement.indexOf(QLatin1Char(':'));
+ // to ensure backward compatibility, check dash (-) symbol too
+ if (pos == -1)
+ pos = requirement.indexOf(QLatin1Char('-'));
+ if (pos != -1) {
+ if (name)
+ *name = requirement.left(pos);
+ if (version)
+ *version = requirement.mid(pos + 1);
+ } else {
+ if (name)
+ *name = requirement;
+ if (version)
+ version->clear();
+ }
+}
+
+QStringList PackageManagerCore::parseNames(const QStringList &requirements)
+{
+ QString name;
+ QString version;
+ QStringList names;
+ foreach (const QString &requirement, requirements) {
+ parseNameAndVersion(requirement, &name, &version);
+ names.append(name);
+ }
+ return names;
+}
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index 2a235bd44..10aad54d1 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -265,6 +265,10 @@ public:
QStringList filesForDelayedDeletion() const;
void addFilesForDelayedDeletion(const QStringList &files);
+ static QString checkableName(const QString &name);
+ static void parseNameAndVersion(const QString &requirement, QString *name, QString *version);
+ static QStringList parseNames(const QStringList &requirements);
+
public Q_SLOTS:
bool runInstaller();
bool runUninstaller();
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 1cd7811c7..2577f09fc 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -533,7 +533,7 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const
QList<Component*> installedComponents;
foreach (const QString &name, pmcp->localInstalledPackages().keys()) {
- if (Component *component = m_core->componentByName(name)) {
+ if (Component *component = m_core->componentByName(PackageManagerCore::checkableName(name))) {
if (!component->uninstallationRequested())
installedComponents.append(component);
}
@@ -1655,7 +1655,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
const QString &name = operation->value(QLatin1String("component")).toString();
Component *component = componentsByName.value(name, 0);
if (!component)
- component = m_core->componentByName(name);
+ component = m_core->componentByName(PackageManagerCore::checkableName(name));
if (component)
componentsByName.insert(name, component);
@@ -1935,7 +1935,8 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
component->isVirtual(),
component->value(scUncompressedSize).toULongLong(),
component->value(scInheritVersion),
- component->isCheckable());
+ component->isCheckable(),
+ component->isExpandedByDefault());
m_localPackageHub->writeToDisk();
component->setInstalled();
@@ -2093,7 +2094,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
else if (button == QMessageBox::Ignore)
ignoreError = true;
}
- Component *component = m_core->componentByName(componentName);
+ Component *component = m_core->componentByName(PackageManagerCore::checkableName(componentName));
if (!component)
component = componentsToReplace().value(componentName).second;
if (component) {
@@ -2393,11 +2394,10 @@ OperationList PackageManagerCorePrivate::sortOperationsBasedOnComponentDependenc
componentOperationHash[componentName].append(operation);
}
- const QRegExp dash(QLatin1String("-.*"));
Graph<QString> componentGraph; // create the complete component graph
foreach (const Component* node, m_core->components(PackageManagerCore::ComponentType::All)) {
componentGraph.addNode(node->name());
- componentGraph.addEdges(node->name(), node->dependencies().replaceInStrings(dash, QString()));
+ componentGraph.addEdges(node->name(), m_core->parseNames(node->dependencies()));
}
const QStringList resolvedComponents = componentGraph.sort();
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index 5ee3d14fc..287bf7d38 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -1275,19 +1275,19 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
QWidget *widget = new QWidget(this);
QVBoxLayout *boxLayout = new QVBoxLayout(widget);
- m_packageManager = new QRadioButton(tr("Add or remove components"), this);
+ m_packageManager = new QRadioButton(tr("&Add or remove components"), this);
m_packageManager->setObjectName(QLatin1String("PackageManagerRadioButton"));
boxLayout->addWidget(m_packageManager);
m_packageManager->setChecked(core->isPackageManager());
connect(m_packageManager, &QAbstractButton::toggled, this, &IntroductionPage::setPackageManager);
- m_updateComponents = new QRadioButton(tr("Update components"), this);
+ m_updateComponents = new QRadioButton(tr("&Update components"), this);
m_updateComponents->setObjectName(QLatin1String("UpdaterRadioButton"));
boxLayout->addWidget(m_updateComponents);
m_updateComponents->setChecked(core->isUpdater());
connect(m_updateComponents, &QAbstractButton::toggled, this, &IntroductionPage::setUpdater);
- m_removeAllComponents = new QRadioButton(tr("Remove all components"), this);
+ m_removeAllComponents = new QRadioButton(tr("&Remove all components"), this);
m_removeAllComponents->setObjectName(QLatin1String("UninstallerRadioButton"));
boxLayout->addWidget(m_removeAllComponents);
m_removeAllComponents->setChecked(core->isUninstaller());
@@ -1621,7 +1621,7 @@ void IntroductionPage::entering()
showWidgets(false);
setMessage(QString());
setErrorMessage(QString());
- setButtonText(QWizard::CancelButton, tr("Quit"));
+ setButtonText(QWizard::CancelButton, tr("&Quit"));
m_progressBar->setValue(0);
m_progressBar->setRange(0, 0);
@@ -1981,6 +1981,12 @@ public:
m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel;
m_treeView->setModel(m_currentModel);
m_treeView->setExpanded(m_currentModel->index(0, 0), true);
+ foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::All)) {
+ if (component->isExpandedByDefault()) {
+ const QModelIndex index = m_currentModel->indexFromComponentName(component->name());
+ m_treeView->setExpanded(index, true);
+ }
+ }
const bool installActionColumnVisible = m_core->settings().installActionColumnVisible();
if (!installActionColumnVisible)
diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index 28f91a394..4a3c163d2 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.cpp
@@ -72,6 +72,28 @@ QJSValue InstallerProxy::componentByName(const QString &componentName)
return QJSValue();
}
+QJSValue QDesktopServicesProxy::findFiles(const QString &path, const QString &pattern)
+{
+ QStringList result;
+ findRecursion(path, pattern, &result);
+
+ QJSValue scriptComponentsObject = m_engine->newArray(result.count());
+ for (int i = 0; i < result.count(); ++i) {
+ scriptComponentsObject.setProperty(i, result.at(i));
+ }
+ return scriptComponentsObject;
+}
+
+void QDesktopServicesProxy::findRecursion(const QString &path, const QString &pattern, QStringList *result)
+{
+ QDir currentDir(path);
+ const QString prefix = path + QLatin1Char('/');
+ foreach (const QString &match, currentDir.entryList(QStringList(pattern), QDir::Files | QDir::NoSymLinks))
+ result->append(prefix + match);
+ foreach (const QString &dir, currentDir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
+ findRecursion(prefix + dir, pattern, result);
+}
+
GuiProxy::GuiProxy(ScriptEngine *engine, QObject *parent) :
QObject(parent),
m_engine(engine),
@@ -526,7 +548,7 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericConfigLocation, QStandardPaths)
- QJSValue object = m_engine.newQObject(new QDesktopServicesProxy);
+ QJSValue object = m_engine.newQObject(new QDesktopServicesProxy(this));
object.setPrototype(desktopServices); // attach the properties
return object;
}
diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h
index 7af5cbab8..adbe2dc0c 100644
--- a/src/libs/installer/scriptengine_p.h
+++ b/src/libs/installer/scriptengine_p.h
@@ -93,7 +93,8 @@ class QDesktopServicesProxy : public QObject
Q_DISABLE_COPY(QDesktopServicesProxy)
public:
- QDesktopServicesProxy() {}
+ QDesktopServicesProxy(ScriptEngine *engine)
+ : m_engine(engine){}
public slots :
bool openUrl(const QString &url) const {
@@ -108,6 +109,13 @@ public slots :
QString storageLocation(qint32 location) const {
return QStandardPaths::writableLocation(QStandardPaths::StandardLocation(location));
}
+ QJSValue findFiles(const QString &path, const QString &pattern);
+
+private:
+ void findRecursion(const QString &path, const QString &pattern, QStringList *result);
+
+private:
+ ScriptEngine *m_engine;
};
#if QT_VERSION < 0x050400
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/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp
index 5bdd56e34..20ad3c049 100644
--- a/src/libs/installer/uninstallercalculator.cpp
+++ b/src/libs/installer/uninstallercalculator.cpp
@@ -75,7 +75,7 @@ void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*>
foreach (Component *component, m_installedComponents) {
// If a components is installed and not yet scheduled for un-installation, check for auto depend.
if (component->isInstalled() && !m_componentsToUninstall.contains(component)) {
- QStringList autoDependencies = component->autoDependencies();
+ QStringList autoDependencies = PackageManagerCore::parseNames(component->autoDependencies());
if (autoDependencies.isEmpty())
continue;
diff --git a/src/libs/kdtools/localpackagehub.cpp b/src/libs/kdtools/localpackagehub.cpp
index 6ac3da803..11ff36a88 100644
--- a/src/libs/kdtools/localpackagehub.cpp
+++ b/src/libs/kdtools/localpackagehub.cpp
@@ -322,7 +322,8 @@ void LocalPackageHub::addPackage(const QString &name,
bool virtualComp,
quint64 uncompressedSize,
const QString &inheritVersionFrom,
- bool checkable)
+ bool checkable,
+ bool expandedByDefault)
{
// TODO: This somewhat unexpected, remove?
if (d->m_packageInfoMap.contains(name)) {
@@ -343,6 +344,7 @@ void LocalPackageHub::addPackage(const QString &name,
info.virtualComp = virtualComp;
info.uncompressedSize = uncompressedSize;
info.checkable = checkable;
+ info.expandedByDefault = expandedByDefault;
d->m_packageInfoMap.insert(name, info);
}
d->modified = true;
@@ -416,6 +418,8 @@ void LocalPackageHub::writeToDisk()
addTextChildHelper(&package, QLatin1String("Virtual"), QLatin1String("true"));
if (info.checkable)
addTextChildHelper(&package, QLatin1String("Checkable"), QLatin1String("true"));
+ if (info.expandedByDefault)
+ addTextChildHelper(&package, QLatin1String("ExpandedByDefault"), QLatin1String("true"));
root.appendChild(package);
}
@@ -444,6 +448,7 @@ void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packag
info.forcedInstallation = false;
info.virtualComp = false;
info.checkable = false;
+ info.expandedByDefault = false;
for (int i = 0; i < childNodes.count(); i++) {
QDomNode childNode = childNodes.item(i);
QDomElement childNodeE = childNode.toElement();
@@ -478,6 +483,8 @@ void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packag
info.installDate = QDate::fromString(childNodeE.text(), Qt::ISODate);
else if (childNodeE.tagName() == QLatin1String("Checkable"))
info.checkable = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
+ else if (childNodeE.tagName() == QLatin1String("ExpandedByDefault"))
+ info.expandedByDefault = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
}
m_packageInfoMap.insert(info.name, info);
}
diff --git a/src/libs/kdtools/localpackagehub.h b/src/libs/kdtools/localpackagehub.h
index f5c7b7623..7a067e73e 100644
--- a/src/libs/kdtools/localpackagehub.h
+++ b/src/libs/kdtools/localpackagehub.h
@@ -53,6 +53,7 @@ struct KDTOOLS_EXPORT LocalPackage
bool virtualComp;
quint64 uncompressedSize;
bool checkable;
+ bool expandedByDefault;
};
class KDTOOLS_EXPORT LocalPackageHub
@@ -104,7 +105,8 @@ public:
bool virtualComp,
quint64 uncompressedSize,
const QString &inheritVersionFrom,
- bool checkable);
+ bool checkable,
+ bool expandedByDefault);
bool removePackage(const QString &pkgName);
void refresh();
diff --git a/src/libs/kdtools/updatefinder.cpp b/src/libs/kdtools/updatefinder.cpp
index ec1be8a4e..b64f922c3 100644
--- a/src/libs/kdtools/updatefinder.cpp
+++ b/src/libs/kdtools/updatefinder.cpp
@@ -590,34 +590,62 @@ int KDUpdater::compareVersion(const QString &v1, const QString &v2)
if (v1 == v2)
return 0;
- // Split version numbers across "."
- const QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-")));
- const QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-")));
+ // Split version components across ".", "-" or "_"
+ QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-|_")));
+ QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-|_")));
// Check each component of the version
int index = 0;
while (true) {
- if (index == v1_comps.count() && index < v2_comps.count())
- return -1;
- if (index < v1_comps.count() && index == v2_comps.count())
- return +1;
+ bool v1_ok = false;
+ bool v2_ok = false;
+
+ if (index == v1_comps.count() && index < v2_comps.count()) {
+ v2_comps.at(index).toInt(&v2_ok);
+ return v2_ok ? -1 : +1;
+ }
+ if (index < v1_comps.count() && index == v2_comps.count()) {
+ v1_comps.at(index).toInt(&v1_ok);
+ return v1_ok ? +1 : -1;
+ }
if (index >= v1_comps.count() || index >= v2_comps.count())
break;
- bool v1_ok, v2_ok;
- int v1_comp = v1_comps[index].toInt(&v1_ok);
- int v2_comp = v2_comps[index].toInt(&v2_ok);
+ int v1_comp = v1_comps.at(index).toInt(&v1_ok);
+ int v2_comp = v2_comps.at(index).toInt(&v2_ok);
if (!v1_ok) {
- if (v1_comps[index] == QLatin1String("x"))
+ if (v1_comps.at(index) == QLatin1String("x"))
return 0;
}
if (!v2_ok) {
- if (v2_comps[index] == QLatin1String("x"))
+ if (v2_comps.at(index) == QLatin1String("x"))
return 0;
}
- if (!v1_ok && !v2_ok)
- return v1_comps[index].compare(v2_comps[index]);
+ if (!v1_ok && !v2_ok) {
+ // try remove equal start
+ int i = 0;
+ while (i < v1_comps.at(index).size()
+ && i < v2_comps.at(index).size()
+ && v1_comps.at(index).at(i) == v2_comps.at(index).at(i)) {
+ ++i;
+ }
+ if (i > 0) {
+ v1_comps[index] = v1_comps.at(index).mid(i);
+ v2_comps[index] = v2_comps.at(index).mid(i);
+ // compare again
+ continue;
+ }
+ }
+ if (!v1_ok || !v2_ok) {
+ int res = v1_comps.at(index).compare(v2_comps.at(index));
+ if (res == 0) {
+ // v1_comps.at(index) == v2_comps(index)
+ ++index;
+ continue;
+ }
+ return res > 0 ? +1 : -1;
+ }
if (v1_comp < v2_comp)
return -1;
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"
diff --git a/tests/auto/installer/compareversion/compareversion.pro b/tests/auto/installer/compareversion/compareversion.pro
new file mode 100644
index 000000000..4f941df4a
--- /dev/null
+++ b/tests/auto/installer/compareversion/compareversion.pro
@@ -0,0 +1,6 @@
+include(../../qttest.pri)
+
+QT -= gui
+
+SOURCES += tst_compareversion.cpp
+
diff --git a/tests/auto/installer/compareversion/tst_compareversion.cpp b/tests/auto/installer/compareversion/tst_compareversion.cpp
new file mode 100644
index 000000000..24ebe9ba6
--- /dev/null
+++ b/tests/auto/installer/compareversion/tst_compareversion.cpp
@@ -0,0 +1,93 @@
+/**************************************************************************
+**
+** Copyright (C) 2017 Konstantin Podsvirov <konstantin@podsvirov.pro>
+** 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 "updater.h"
+
+#include <QTest>
+
+class tst_CompareVersion : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void compareVersion();
+ void compareVersionX();
+ void compareVersionAll();
+ void compareVersionExtra();
+};
+
+void tst_CompareVersion::compareVersion()
+{
+ QCOMPARE(KDUpdater::compareVersion("2.0", "2.1"), -1);
+ QCOMPARE(KDUpdater::compareVersion("2.1", "2.0"), +1);
+ QCOMPARE(KDUpdater::compareVersion("2.0", "2.0"), 0);
+ QCOMPARE(KDUpdater::compareVersion("2.1", "2.1"), 0);
+
+ QCOMPARE(KDUpdater::compareVersion("2.0.12.4", "2.1.10.4"), -1);
+}
+
+void tst_CompareVersion::compareVersionX()
+{
+ QCOMPARE(KDUpdater::compareVersion("2.0", "2.x"), 0);
+ QCOMPARE(KDUpdater::compareVersion("2.x", "2.0"), 0);
+
+ QCOMPARE(KDUpdater::compareVersion("2.0.12.x", "2.0.x"), 0);
+ QCOMPARE(KDUpdater::compareVersion("2.1.12.x", "2.0.x"), +1);
+ QCOMPARE(KDUpdater::compareVersion("2.1.12.x", "2.x"), 0);
+ QCOMPARE(KDUpdater::compareVersion("2.x", "2.1.12.x"), 0);
+}
+
+void tst_CompareVersion::compareVersionAll()
+{
+ QCOMPARE(KDUpdater::compareVersion("version-1", "version-2"), -1);
+
+ QCOMPARE(KDUpdater::compareVersion("v2.0", "v2.x"), 0);
+ QCOMPARE(KDUpdater::compareVersion("v2.x", "v2.0"), 0);
+
+ QCOMPARE(KDUpdater::compareVersion("v2.0-alpha", "v2.0-alpha"), 0);
+
+ QCOMPARE(KDUpdater::compareVersion("v2.0-alpha", "v2.0-beta"), -1);
+ QCOMPARE(KDUpdater::compareVersion("v2.0-beta", "v2.0-alpha"), +1);
+
+ QCOMPARE(KDUpdater::compareVersion("v2.0-rc1", "v2.0-beta"), +1);
+ QCOMPARE(KDUpdater::compareVersion("v2.0-rc1", "v2.0-rc2"), -1);
+ QCOMPARE(KDUpdater::compareVersion("v2.0-rc2", "v2.0-rc11"), -1);
+ QCOMPARE(KDUpdater::compareVersion("v2.0-rc22", "v2.0-rc2"), +1);
+
+ QCOMPARE(KDUpdater::compareVersion("v2.0", "v2.0-rc3"), +1);
+}
+
+void tst_CompareVersion::compareVersionExtra()
+{
+ QCOMPARE(KDUpdater::compareVersion("OpenSSL_1_0_2k", "OpenSSL_1_0_2l"), -1);
+ QCOMPARE(KDUpdater::compareVersion("OpenSSL_1_1_0f", "OpenSSL_1_0_2k"), +1);
+}
+
+QTEST_MAIN(tst_CompareVersion)
+
+#include "tst_compareversion.moc"
diff --git a/tests/auto/installer/componentidentifier/componentidentifier.pro b/tests/auto/installer/componentidentifier/componentidentifier.pro
new file mode 100644
index 000000000..dade1f025
--- /dev/null
+++ b/tests/auto/installer/componentidentifier/componentidentifier.pro
@@ -0,0 +1,6 @@
+include(../../qttest.pri)
+
+QT -= gui
+
+SOURCES += tst_componentidentifier.cpp
+
diff --git a/tests/auto/installer/componentidentifier/tst_componentidentifier.cpp b/tests/auto/installer/componentidentifier/tst_componentidentifier.cpp
new file mode 100644
index 000000000..d7aa6cac3
--- /dev/null
+++ b/tests/auto/installer/componentidentifier/tst_componentidentifier.cpp
@@ -0,0 +1,252 @@
+/**************************************************************************
+**
+** Copyright (C) 2017 Konstantin Podsvirov <konstantin@podsvirov.pro>
+** 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 <packagemanagercore.h>
+
+#include <QTest>
+
+using namespace QInstaller;
+
+class NamedComponent : public Component
+{
+public:
+ NamedComponent(PackageManagerCore *core, const QString &name)
+ : Component(core)
+ {
+ setValue(scName, name);
+ setValue(scVersion, QLatin1String("1.0.0"));
+ }
+
+ NamedComponent(PackageManagerCore *core, const QString &name, const QString &version)
+ : Component(core)
+ {
+ setValue(scName, name);
+ setValue(scVersion, version);
+ }
+
+};
+
+class tst_ComponentIdentifier : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testPackageManagerCoreSetterGetter_data();
+ void testPackageManagerCoreSetterGetter();
+
+ void testComponentDependencies();
+};
+
+void tst_ComponentIdentifier::testPackageManagerCoreSetterGetter_data()
+{
+ const char *tag = 0;
+
+ QTest::addColumn<PackageManagerCore *>("core");
+ QTest::addColumn<QStringList>("requirements");
+ QTest::addColumn<QList<Component *> >("expectedResult");
+
+ PackageManagerCore *core = 0;
+ QStringList req; // requirements;
+ QList<Component *> res; // expectedResult;
+
+ tag = "Alphabetic name";
+ core = 0; req.clear(); res.clear();
+ {
+ core = new PackageManagerCore();
+ core->setPackageManager();
+
+ NamedComponent *componentRoot = new NamedComponent(core, QLatin1String("root"));
+ core->appendRootComponent(componentRoot);
+
+ req << QLatin1String("root");
+ res << componentRoot;
+ }
+ QTest::newRow(tag) << core << req << res;
+
+ tag = "Alphabetic name, dash (-) and numbered version";
+ core = 0; req.clear(); res.clear();
+ {
+ core = new PackageManagerCore();
+ core->setPackageManager();
+
+ NamedComponent *componentRoot = new NamedComponent(core,
+ QLatin1String("root"),
+ QLatin1String("1.0.1"));
+ core->appendRootComponent(componentRoot);
+
+ req << QLatin1String("root-1.0.1");
+ res << componentRoot;
+
+ req << QLatin1String("root->1");
+ res << componentRoot;
+
+ req << QLatin1String("root-<2");
+ res << componentRoot;
+
+ req << QLatin1String("root-");
+ res << componentRoot;
+ }
+ QTest::newRow(tag) << core << req << res;
+
+ tag = "Alphabetic name, colon (:) and numbered version";
+ core = 0; req.clear(); res.clear();
+ {
+ core = new PackageManagerCore();
+ core->setPackageManager();
+
+ NamedComponent *componentRoot = new NamedComponent(core,
+ QLatin1String("root"),
+ QLatin1String("1.0.1"));
+ core->appendRootComponent(componentRoot);
+
+ req << QLatin1String("root:1.0.1");
+ res << componentRoot;
+
+ req << QLatin1String("root:>1");
+ res << componentRoot;
+
+ req << QLatin1String("root:<2");
+ res << componentRoot;
+
+ req << QLatin1String("root:");
+ res << componentRoot;
+ }
+ QTest::newRow(tag) << core << req << res;
+
+ tag = "Kebab-case name, dash (-) and numbered version";
+ core = 0; req.clear(); res.clear();
+ {
+ core = new PackageManagerCore();
+ core->setPackageManager();
+
+ NamedComponent *componentSdk = new NamedComponent(core,
+ QLatin1String("org.qt-project.sdk.qt"),
+ QLatin1String("4.5"));
+ NamedComponent *componentQt = new NamedComponent(core,
+ QLatin1String("org.qt"),
+ QLatin1String("project.sdk.qt->=4.5"));
+ componentQt->appendComponent(componentSdk);
+ core->appendRootComponent(componentQt);
+
+ // this example has been present for many years in
+ // the PackageManagerCore::componentByName() method documentation,
+ // but it does not work as expected
+ // in this case, name is "org.qt" and version is "project.sdk.qt->=4.5"
+ req << QLatin1String("org.qt-project.sdk.qt->=4.5");
+ res << componentQt; // and you expected componentSdk?
+ }
+ QTest::newRow(tag) << core << req << res;
+
+ tag = "Kebab-case name, colon (:) and numbered version";
+ core = 0; req.clear(); res.clear();
+ {
+ core = new PackageManagerCore();
+ core->setPackageManager();
+
+ NamedComponent *componentSdk = new NamedComponent(core,
+ QLatin1String("org.qt-project.sdk.qt"),
+ QLatin1String("4.5"));
+ core->appendRootComponent(componentSdk);
+
+
+ req << QLatin1String("org.qt-project.sdk.qt:>=4.5");
+ res << componentSdk; // work as expected
+
+ req << QLatin1String("org.qt-project.sdk.qt");
+ res << 0;
+
+ // we should fix names with dash (-) while support dash (-) as separator
+ req << PackageManagerCore::checkableName(QLatin1String("org.qt-project.sdk.qt"));
+ res << componentSdk;
+ }
+ QTest::newRow(tag) << core << req << res;
+}
+
+void tst_ComponentIdentifier::testPackageManagerCoreSetterGetter()
+{
+ QFETCH(PackageManagerCore *, core);
+ QFETCH(QStringList, requirements);
+ QFETCH(QList<Component *>, expectedResult);
+
+ QCOMPARE(requirements.count(), expectedResult.count());
+ for (int i = 0; i < requirements.count(); ++i) {
+ QCOMPARE(core->componentByName(requirements.at(i)), expectedResult.at(i));
+ }
+
+ delete core;
+}
+
+void tst_ComponentIdentifier::testComponentDependencies()
+{
+ PackageManagerCore *core = new PackageManagerCore();
+ core->setPackageManager();
+
+ Component *componentA = new NamedComponent(core, "A");
+ Component *componentB = new NamedComponent(core, "B");
+ Component *componentC = new NamedComponent(core, "component-C");
+ Component *componentD = new NamedComponent(core, "D-backward-compatibility");
+ Component *componentE = new NamedComponent(core, "E");
+
+ componentA->addDependency("B-1.0.0");
+ componentA->addDependency("component-C:1.0.0");
+ componentA->addDependency("D-backward-compatibility:");
+
+ componentE->addAutoDependOn("B-1.0.0");
+ componentE->addAutoDependOn("component-C:1.0.0");
+ componentE->addAutoDependOn("D-backward-compatibility:");
+
+ core->appendRootComponent(componentA);
+ core->appendRootComponent(componentB);
+ core->appendRootComponent(componentC);
+ core->appendRootComponent(componentD);
+ core->appendRootComponent(componentE);
+
+ QList<Component*> expectedResult = QList<Component*>()
+ << componentB << componentC << componentD;
+
+ QList<Component*> dependencies;
+ foreach (const QString &identifier, componentA->dependencies()) {
+ dependencies.append(core->componentByName(identifier));
+ }
+
+ QCOMPARE(expectedResult, dependencies);
+
+ QList<Component*> autoDependOn;
+ foreach (const QString &identifier, componentE->autoDependencies()) {
+ autoDependOn.append(core->componentByName(identifier));
+ }
+
+ QCOMPARE(expectedResult, autoDependOn);
+
+ delete core;
+}
+
+QTEST_MAIN(tst_ComponentIdentifier)
+
+#include "tst_componentidentifier.moc"
diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro
index ba2dd0244..db94a23cb 100644
--- a/tests/auto/installer/installer.pro
+++ b/tests/auto/installer/installer.pro
@@ -3,6 +3,8 @@ TEMPLATE = subdirs
SUBDIRS += \
settings \
repository \
+ compareversion\
+ componentidentifier \
componentmodel \
fakestopprocessforupdateoperation \
messageboxhandler \
diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
index 2049e6323..7118d067d 100644
--- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp
+++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
@@ -198,6 +198,7 @@ private slots:
m_core.appendRootComponent(component);
m_scriptEngine = m_core.componentScriptEngine();
+ m_applicatonDirPath = qApp->applicationDirPath();
}
void testDefaultScriptEngineValues()
@@ -234,6 +235,8 @@ private slots:
.hasProperty(QLatin1String("displayName")), true);
QCOMPARE(global.property(QLatin1String("QDesktopServices"))
.hasProperty(QLatin1String("storageLocation")), true);
+ QCOMPARE(global.property(QLatin1String("QDesktopServices"))
+ .hasProperty(QLatin1String("findFiles")), true);
QCOMPARE(global.hasProperty(QLatin1String("buttons")), true);
QCOMPARE(global.hasProperty(QLatin1String("QInstaller")), true);
@@ -342,6 +345,30 @@ private slots:
}
}
+ void testFindFiles()
+ {
+ const QString expectedOutput = QString::fromLatin1("Found file %1/tst_scriptengine.moc").arg(m_applicatonDirPath);
+ QByteArray array = expectedOutput.toLatin1();
+ const char *c_str2 = array.data();
+
+ setExpectedScriptOutput(c_str2);
+ const QString script = QString::fromLatin1("var directory = \"C:/Qt/test\";"
+ "\n"
+ "var pattern = \"*.moc\";"
+ "\n"
+ "var fileArray = QDesktopServices.findFiles('%1', pattern)"
+ "\n"
+ "for (i = 0; i < fileArray.length; i++) {"
+ "print(\"Found file \"+fileArray[i]);"
+ "}").arg(m_applicatonDirPath);
+ const QJSValue result = m_scriptEngine->evaluate(script);
+ qDebug()<<result.isArray();
+ qDebug()<<result.isObject();
+ qDebug()<<result.isString();
+ qDebug()<<result.isVariant();
+ QCOMPARE(result.isError(), false);
+ }
+
void loadSimpleComponentScript()
{
try {
@@ -585,6 +612,7 @@ private:
PackageManagerCore m_core;
Component *m_component;
ScriptEngine *m_scriptEngine;
+ QString m_applicatonDirPath;
};
diff --git a/tests/auto/installer/solver/tst_solver.cpp b/tests/auto/installer/solver/tst_solver.cpp
index 5829621f2..7ae502a5e 100644
--- a/tests/auto/installer/solver/tst_solver.cpp
+++ b/tests/auto/installer/solver/tst_solver.cpp
@@ -156,19 +156,23 @@ private slots:
componentA->appendComponent(componentAB);
NamedComponent *componentB = new NamedComponent(core, QLatin1String("B"));
NamedComponent *componentB_NewVersion = new NamedComponent(core, QLatin1String("B_version"), QLatin1String("2.0.0"));
+ NamedComponent *componentB_Auto = new NamedComponent(core, QLatin1String("B_auto"));
componentB->addDependency(QLatin1String("A.B"));
componentAB->addDependency(QLatin1String("B_version->=2.0.0"));
+ componentB_Auto->addAutoDependOn(QLatin1String("B_version"));
core->appendRootComponent(componentA);
core->appendRootComponent(componentB);
core->appendRootComponent(componentB_NewVersion);
+ core->appendRootComponent(componentB_Auto);
QTest::newRow("Installer resolved") << core
<< (QList<Component *>() << componentB)
- << (QList<Component *>() << componentB_NewVersion << componentAB << componentB)
+ << (QList<Component *>() << componentB_NewVersion << componentAB << componentB << componentB_Auto)
<< (QList<int>()
<< InstallerCalculator::Dependent
<< InstallerCalculator::Dependent
- << InstallerCalculator::Resolved);
+ << InstallerCalculator::Resolved
+ << InstallerCalculator::Automatic);
}
void resolveInstaller()
@@ -299,7 +303,7 @@ private slots:
NamedComponent *componentA = new NamedComponent(core, QLatin1String("A"));
NamedComponent *componentB = new NamedComponent(core, QLatin1String("B"));
- componentB->setValue(QLatin1String("AutoDependOn"), QLatin1String("A"));
+ componentB->addAutoDependOn(QLatin1String("A"));
componentB->setValue(QLatin1String("Default"), QLatin1String("true"));
core->appendRootComponent(componentA);
core->appendRootComponent(componentB);
diff --git a/tools/binarycreator/binarycreator.cpp b/tools/binarycreator/binarycreator.cpp
index 1d2faeace..6492b59a1 100644
--- a/tools/binarycreator/binarycreator.cpp
+++ b/tools/binarycreator/binarycreator.cpp
@@ -770,6 +770,7 @@ int main(int argc, char **argv)
QString target;
QString configFile;
QStringList packagesDirectories;
+ QStringList repositoryDirectories;
bool onlineOnly = false;
bool offlineOnly = false;
QStringList resources;
@@ -793,6 +794,16 @@ int main(int argc, char **argv)
"specified location."));
}
packagesDirectories.append(*it);
+ } else if (*it == QLatin1String("--repository")) {
+ ++it;
+ if (it == args.end()) {
+ return printErrorAndUsageAndExit(QString::fromLatin1("Error: Repository parameter missing argument."));
+ }
+ if (QFileInfo(*it).exists()) {
+ repositoryDirectories.append(*it);
+ } else {
+ return printErrorAndUsageAndExit(QString::fromLatin1("Error: Only local filesystem repositories now supported."));
+ }
} else if (*it == QLatin1String("-e") || *it == QLatin1String("--exclude")) {
++it;
if (!filteredPackages.isEmpty())
@@ -905,8 +916,8 @@ int main(int argc, char **argv)
if (configFile.isEmpty())
return printErrorAndUsageAndExit(QString::fromLatin1("Error: No configuration file selected."));
- if (packagesDirectories.isEmpty())
- return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package directory parameter missing."));
+ if (packagesDirectories.isEmpty() && repositoryDirectories.isEmpty())
+ return printErrorAndUsageAndExit(QString::fromLatin1("Error: Both Package directory and Repository parameters missing."));
qDebug() << "Parsed arguments, ok.";
@@ -924,14 +935,29 @@ int main(int argc, char **argv)
// Note: the order here is important
- // 1; create the list of available packages
- QInstallerTools::PackageInfoVector packages =
- QInstallerTools::createListOfPackages(packagesDirectories, &filteredPackages, ftype);
+ QInstallerTools::PackageInfoVector packages;
+
+ // 1; update the list of available compressed packages
+ if (!repositoryDirectories.isEmpty()) {
+ // 1.1; search packages
+ QInstallerTools::PackageInfoVector precompressedPackages = QInstallerTools::createListOfRepositoryPackages(repositoryDirectories,
+ &filteredPackages, ftype);
+ // 1.2; add to common vector
+ packages.append(precompressedPackages);
+ }
- // 2; copy the packages data and setup the packages vector with the files we copied,
- // must happen before copying meta data because files will be compressed if
- // needed and meta data generation relies on this
- QInstallerTools::copyComponentData(packagesDirectories, tmpRepoDir, &packages);
+ // 2; update the list of available prepared packages
+ if (!packagesDirectories.isEmpty()) {
+ // 2.1; search packages
+ QInstallerTools::PackageInfoVector preparedPackages = QInstallerTools::createListOfPackages(packagesDirectories,
+ &filteredPackages, ftype);
+ // 2.2; copy the packages data and setup the packages vector with the files we copied,
+ // must happen before copying meta data because files will be compressed if
+ // needed and meta data generation relies on this
+ QInstallerTools::copyComponentData(packagesDirectories, tmpRepoDir, &preparedPackages);
+ // 2.3; add to common vector
+ packages.append(preparedPackages);
+ }
// 3; copy the meta data of the available packages, generate Updates.xml
QInstallerTools::copyMetaData(tmpMetaDir, tmpRepoDir, packages, settings
diff --git a/tools/common/repositorygen.cpp b/tools/common/repositorygen.cpp
index e4eb33590..a1144a005 100644
--- a/tools/common/repositorygen.cpp
+++ b/tools/common/repositorygen.cpp
@@ -27,11 +27,13 @@
**************************************************************************/
#include "repositorygen.h"
+#include <constants.h>
#include <fileio.h>
#include <fileutils.h>
#include <errors.h>
#include <globals.h>
#include <lib7z_create.h>
+#include <lib7z_extract.h>
#include <lib7z_facade.h>
#include <lib7z_list.h>
#include <settings.h>
@@ -48,12 +50,15 @@
#include <iostream>
+using namespace QInstaller;
using namespace QInstallerTools;
void QInstallerTools::printRepositoryGenOptions()
{
std::cout << " -p|--packages dir The directory containing the available packages." << std::endl;
std::cout << " This entry can be given multiple times." << std::endl;
+ std::cout << " --repository dir The directory containing the available repository." << std::endl;
+ std::cout << " This entry can be given multiple times." << std::endl;
std::cout << " -e|--exclude p1,...,pn Exclude the given packages." << std::endl;
std::cout << " -i|--include p1,...,pn Include the given packages and their dependencies" << std::endl;
@@ -61,6 +66,7 @@ void QInstallerTools::printRepositoryGenOptions()
std::cout << " --ignore-translations Do not use any translation" << std::endl;
std::cout << " --ignore-invalid-packages Ignore all invalid packages instead of aborting." << std::endl;
+ std::cout << " --ignore-invalid-repositories Ignore all invalid repositories instead of aborting." << std::endl;
}
QString QInstallerTools::makePathAbsolute(const QString &path)
@@ -154,221 +160,237 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
}
foreach (const PackageInfo &info, packages) {
- if (!QDir(targetDir).mkpath(info.name))
- throw QInstaller::Error(QString::fromLatin1("Cannot create directory \"%1\".").arg(info.name));
-
- const QString packageXmlPath = QString::fromLatin1("%1/meta/package.xml").arg(info.directory);
- qDebug() << "Copy meta data for package" << info.name << "using" << packageXmlPath;
-
- QFile file(packageXmlPath);
- QInstaller::openForRead(&file);
-
- QString errMsg;
- int line = 0;
- int column = 0;
- QDomDocument packageXml;
- if (!packageXml.setContent(&file, &errMsg, &line, &column)) {
- throw QInstaller::Error(QString::fromLatin1("Cannot parse \"%1\": line: %2, column: %3: %4 (%5)")
- .arg(QDir::toNativeSeparators(packageXmlPath)).arg(line).arg(column).arg(errMsg, info.name));
- }
+ if (info.metaFile.isEmpty() && info.metaNode.isEmpty()) {
+ if (!QDir(targetDir).mkpath(info.name))
+ throw QInstaller::Error(QString::fromLatin1("Cannot create directory \"%1\".").arg(info.name));
+
+ const QString packageXmlPath = QString::fromLatin1("%1/meta/package.xml").arg(info.directory);
+ qDebug() << "Copy meta data for package" << info.name << "using" << packageXmlPath;
+
+ QFile file(packageXmlPath);
+ QInstaller::openForRead(&file);
+
+ QString errMsg;
+ int line = 0;
+ int column = 0;
+ QDomDocument packageXml;
+ if (!packageXml.setContent(&file, &errMsg, &line, &column)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot parse \"%1\": line: %2, column: %3: %4 (%5)")
+ .arg(QDir::toNativeSeparators(packageXmlPath)).arg(line).arg(column).arg(errMsg, info.name));
+ }
- QDomElement update = doc.createElement(QLatin1String("PackageUpdate"));
- QDomNode nameElement = update.appendChild(doc.createElement(QLatin1String("Name")));
- nameElement.appendChild(doc.createTextNode(info.name));
-
- // list of current unused or later transformed tags
- QStringList blackList;
- blackList << QLatin1String("UserInterfaces") << QLatin1String("Translations") <<
- QLatin1String("Licenses") << QLatin1String("Name");
-
- bool foundDefault = false;
- bool foundVirtual = false;
- bool foundDisplayName = false;
- bool foundDownloadableArchives = false;
- bool foundCheckable = false;
- const QDomNode package = packageXml.firstChildElement(QLatin1String("Package"));
- const QDomNodeList childNodes = package.childNodes();
- for (int i = 0; i < childNodes.count(); ++i) {
- const QDomNode node = childNodes.at(i);
- const QString key = node.nodeName();
-
- if (key == QLatin1String("Default"))
- foundDefault = true;
- if (key == QLatin1String("Virtual"))
- foundVirtual = true;
- if (key == QLatin1String("DisplayName"))
- foundDisplayName = true;
- if (key == QLatin1String("DownloadableArchives"))
- foundDownloadableArchives = true;
- if (key == QLatin1String("Checkable"))
- foundCheckable = true;
- if (node.isComment() || blackList.contains(key))
- continue; // just skip comments and some tags...
-
- QDomElement element = doc.createElement(key);
- for (int j = 0; j < node.attributes().size(); ++j) {
- element.setAttribute(node.attributes().item(j).toAttr().name(),
- node.attributes().item(j).toAttr().value());
+ QDomElement update = doc.createElement(QLatin1String("PackageUpdate"));
+ QDomNode nameElement = update.appendChild(doc.createElement(QLatin1String("Name")));
+ nameElement.appendChild(doc.createTextNode(info.name));
+
+ // list of current unused or later transformed tags
+ QStringList blackList;
+ blackList << QLatin1String("UserInterfaces") << QLatin1String("Translations") <<
+ QLatin1String("Licenses") << QLatin1String("Name");
+
+ bool foundDefault = false;
+ bool foundVirtual = false;
+ bool foundDisplayName = false;
+ bool foundDownloadableArchives = false;
+ bool foundCheckable = false;
+ const QDomNode package = packageXml.firstChildElement(QLatin1String("Package"));
+ const QDomNodeList childNodes = package.childNodes();
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomNode node = childNodes.at(i);
+ const QString key = node.nodeName();
+
+ if (key == QLatin1String("Default"))
+ foundDefault = true;
+ if (key == QLatin1String("Virtual"))
+ foundVirtual = true;
+ if (key == QLatin1String("DisplayName"))
+ foundDisplayName = true;
+ if (key == QLatin1String("DownloadableArchives"))
+ foundDownloadableArchives = true;
+ if (key == QLatin1String("Checkable"))
+ foundCheckable = true;
+ if (node.isComment() || blackList.contains(key))
+ continue; // just skip comments and some tags...
+
+ QDomElement element = doc.createElement(key);
+ for (int j = 0; j < node.attributes().size(); ++j) {
+ element.setAttribute(node.attributes().item(j).toAttr().name(),
+ node.attributes().item(j).toAttr().value());
+ }
+ update.appendChild(element).appendChild(doc.createTextNode(node.toElement().text()));
}
- update.appendChild(element).appendChild(doc.createTextNode(node.toElement().text()));
- }
- if (foundDefault && foundVirtual) {
- throw QInstaller::Error(QString::fromLatin1("Error: <Default> and <Virtual> elements are "
- "mutually exclusive in file \"%1\".").arg(QDir::toNativeSeparators(packageXmlPath)));
- }
+ if (foundDefault && foundVirtual) {
+ throw QInstaller::Error(QString::fromLatin1("Error: <Default> and <Virtual> elements are "
+ "mutually exclusive in file \"%1\".").arg(QDir::toNativeSeparators(packageXmlPath)));
+ }
- if (foundDefault && foundCheckable) {
- throw QInstaller::Error(QString::fromLatin1("Error: <Default> and <Checkable>"
- "elements are mutually exclusive in file \"%1\".")
- .arg(QDir::toNativeSeparators(packageXmlPath)));
- }
+ if (foundDefault && foundCheckable) {
+ throw QInstaller::Error(QString::fromLatin1("Error: <Default> and <Checkable>"
+ "elements are mutually exclusive in file \"%1\".")
+ .arg(QDir::toNativeSeparators(packageXmlPath)));
+ }
- if (!foundDisplayName) {
- qWarning() << "No DisplayName tag found at" << info.name << ", using component Name instead.";
- QDomElement displayNameElement = doc.createElement(QLatin1String("DisplayName"));
- update.appendChild(displayNameElement).appendChild(doc.createTextNode(info.name));
- }
+ if (!foundDisplayName) {
+ qWarning() << "No DisplayName tag found at" << info.name << ", using component Name instead.";
+ QDomElement displayNameElement = doc.createElement(QLatin1String("DisplayName"));
+ update.appendChild(displayNameElement).appendChild(doc.createTextNode(info.name));
+ }
- // get the size of the data
- quint64 componentSize = 0;
- quint64 compressedComponentSize = 0;
-
- const QDir::Filters filters = QDir::Files | QDir::NoDotAndDotDot;
- const QDir dataDir = QString::fromLatin1("%1/%2/data").arg(metaDataDir, info.name);
- const QFileInfoList entries = dataDir.exists() ? dataDir.entryInfoList(filters | QDir::Dirs)
- : QDir(QString::fromLatin1("%1/%2").arg(metaDataDir, info.name)).entryInfoList(filters);
- qDebug() << "calculate size of directory" << dataDir.absolutePath();
- foreach (const QFileInfo &fi, entries) {
- try {
- if (fi.isDir()) {
- QDirIterator recursDirIt(fi.filePath(), QDirIterator::Subdirectories);
- while (recursDirIt.hasNext()) {
- recursDirIt.next();
- const quint64 size = QInstaller::fileSize(recursDirIt.fileInfo());
+ // get the size of the data
+ quint64 componentSize = 0;
+ quint64 compressedComponentSize = 0;
+
+ const QDir::Filters filters = QDir::Files | QDir::NoDotAndDotDot;
+ const QDir dataDir = QString::fromLatin1("%1/%2/data").arg(metaDataDir, info.name);
+ const QFileInfoList entries = dataDir.exists() ? dataDir.entryInfoList(filters | QDir::Dirs)
+ : QDir(QString::fromLatin1("%1/%2").arg(metaDataDir, info.name)).entryInfoList(filters);
+ qDebug() << "calculate size of directory" << dataDir.absolutePath();
+ foreach (const QFileInfo &fi, entries) {
+ try {
+ if (fi.isDir()) {
+ QDirIterator recursDirIt(fi.filePath(), QDirIterator::Subdirectories);
+ while (recursDirIt.hasNext()) {
+ recursDirIt.next();
+ const quint64 size = QInstaller::fileSize(recursDirIt.fileInfo());
+ componentSize += size;
+ compressedComponentSize += size;
+ }
+ } else if (Lib7z::isSupportedArchive(fi.filePath())) {
+ // if it's an archive already, list its files and sum the uncompressed sizes
+ QFile archive(fi.filePath());
+ compressedComponentSize += archive.size();
+ QInstaller::openForRead(&archive);
+
+ QVector<Lib7z::File>::const_iterator fileIt;
+ const QVector<Lib7z::File> files = Lib7z::listArchive(&archive);
+ for (fileIt = files.begin(); fileIt != files.end(); ++fileIt)
+ componentSize += fileIt->uncompressedSize;
+ } else {
+ // otherwise just add its size
+ const quint64 size = QInstaller::fileSize(fi);
componentSize += size;
compressedComponentSize += size;
}
- } else if (Lib7z::isSupportedArchive(fi.filePath())) {
- // if it's an archive already, list its files and sum the uncompressed sizes
- QFile archive(fi.filePath());
- compressedComponentSize += archive.size();
- QInstaller::openForRead(&archive);
-
- QVector<Lib7z::File>::const_iterator fileIt;
- const QVector<Lib7z::File> files = Lib7z::listArchive(&archive);
- for (fileIt = files.begin(); fileIt != files.end(); ++fileIt)
- componentSize += fileIt->uncompressedSize;
- } else {
- // otherwise just add its size
- const quint64 size = QInstaller::fileSize(fi);
- componentSize += size;
- compressedComponentSize += size;
+ } catch (const QInstaller::Error &error) {
+ qDebug().noquote() << error.message();
+ } catch(...) {
+ // ignore, that's just about the sizes - and size doesn't matter, you know?
}
- } catch (const QInstaller::Error &error) {
- qDebug().noquote() << error.message();
- } catch(...) {
- // ignore, that's just about the sizes - and size doesn't matter, you know?
}
- }
- QDomElement fileElement = doc.createElement(QLatin1String("UpdateFile"));
- fileElement.setAttribute(QLatin1String("UncompressedSize"), componentSize);
- fileElement.setAttribute(QLatin1String("CompressedSize"), compressedComponentSize);
- // adding the OS attribute to be compatible with old sdks
- fileElement.setAttribute(QLatin1String("OS"), QLatin1String("Any"));
- update.appendChild(fileElement);
-
- 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())));
- }
+ QDomElement fileElement = doc.createElement(QLatin1String("UpdateFile"));
+ fileElement.setAttribute(QLatin1String("UncompressedSize"), componentSize);
+ fileElement.setAttribute(QLatin1String("CompressedSize"), compressedComponentSize);
+ // adding the OS attribute to be compatible with old sdks
+ fileElement.setAttribute(QLatin1String("OS"), QLatin1String("Any"));
+ update.appendChild(fileElement);
+
+ 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 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);
- }
+ const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script));
+ copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript);
+ }
- // write DownloadableArchives tag if that is missed by the user
- if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) {
- QStringList realContentFiles;
- foreach (const QString &filePath, info.copiedFiles) {
- if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) {
- const QString fileName = QFileInfo(filePath).fileName();
- // remove unnecessary version string from filename and add it to the list
- realContentFiles.append(fileName.mid(info.version.count()));
+ // write DownloadableArchives tag if that is missed by the user
+ if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) {
+ QStringList realContentFiles;
+ foreach (const QString &filePath, info.copiedFiles) {
+ if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) {
+ const QString fileName = QFileInfo(filePath).fileName();
+ // remove unnecessary version string from filename and add it to the list
+ realContentFiles.append(fileName.mid(info.version.count()));
+ }
}
- }
- update.appendChild(doc.createElement(QLatin1String("DownloadableArchives"))).appendChild(doc
- .createTextNode(realContentFiles.join(QChar::fromLatin1(','))));
- }
+ update.appendChild(doc.createElement(QLatin1String("DownloadableArchives"))).appendChild(doc
+ .createTextNode(realContentFiles.join(QChar::fromLatin1(','))));
+ }
- // copy user interfaces
- const QStringList uiFiles = copyFilesFromNode(QLatin1String("UserInterfaces"),
- QLatin1String("UserInterface"), QString(), QLatin1String("user interface"), package, info,
- targetDir);
- if (!uiFiles.isEmpty()) {
- update.appendChild(doc.createElement(QLatin1String("UserInterfaces"))).appendChild(doc
- .createTextNode(uiFiles.join(QChar::fromLatin1(','))));
- }
+ // copy user interfaces
+ const QStringList uiFiles = copyFilesFromNode(QLatin1String("UserInterfaces"),
+ QLatin1String("UserInterface"), QString(), QLatin1String("user interface"), package, info,
+ targetDir);
+ if (!uiFiles.isEmpty()) {
+ update.appendChild(doc.createElement(QLatin1String("UserInterfaces"))).appendChild(doc
+ .createTextNode(uiFiles.join(QChar::fromLatin1(','))));
+ }
- // copy translations
- QStringList trFiles;
- if (!qApp->arguments().contains(QString::fromLatin1("--ignore-translations"))) {
- trFiles = copyFilesFromNode(QLatin1String("Translations"), QLatin1String("Translation"),
- QString(), QLatin1String("translation"), package, info, targetDir);
- if (!trFiles.isEmpty()) {
- update.appendChild(doc.createElement(QLatin1String("Translations"))).appendChild(doc
- .createTextNode(trFiles.join(QChar::fromLatin1(','))));
+ // copy translations
+ QStringList trFiles;
+ if (!qApp->arguments().contains(QString::fromLatin1("--ignore-translations"))) {
+ trFiles = copyFilesFromNode(QLatin1String("Translations"), QLatin1String("Translation"),
+ QString(), QLatin1String("translation"), package, info, targetDir);
+ if (!trFiles.isEmpty()) {
+ update.appendChild(doc.createElement(QLatin1String("Translations"))).appendChild(doc
+ .createTextNode(trFiles.join(QChar::fromLatin1(','))));
+ }
}
- }
- // copy license files
- const QStringList licenses = copyFilesFromNode(QLatin1String("Licenses"), QLatin1String("License"),
- QLatin1String("file"), QLatin1String("license"), package, info, targetDir);
- if (!licenses.isEmpty()) {
- foreach (const QString &trFile, trFiles) {
- // Copy translated license file based on the assumption that it will have the same base name
- // as the original license plus the file name of an existing translation file without suffix.
- foreach (const QString &license, licenses) {
- const QFileInfo untranslated(license);
- const QString translatedLicense = QString::fromLatin1("%2_%3.%4").arg(untranslated
- .baseName(), QFileInfo(trFile).baseName(), untranslated.completeSuffix());
- // ignore copy failure, that's just about the translations
- QFile::copy(QString::fromLatin1("%1/meta/%2").arg(info.directory).arg(translatedLicense),
- QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, translatedLicense));
+ // copy license files
+ const QStringList licenses = copyFilesFromNode(QLatin1String("Licenses"), QLatin1String("License"),
+ QLatin1String("file"), QLatin1String("license"), package, info, targetDir);
+ if (!licenses.isEmpty()) {
+ foreach (const QString &trFile, trFiles) {
+ // Copy translated license file based on the assumption that it will have the same base name
+ // as the original license plus the file name of an existing translation file without suffix.
+ foreach (const QString &license, licenses) {
+ const QFileInfo untranslated(license);
+ const QString translatedLicense = QString::fromLatin1("%2_%3.%4").arg(untranslated
+ .baseName(), QFileInfo(trFile).baseName(), untranslated.completeSuffix());
+ // ignore copy failure, that's just about the translations
+ QFile::copy(QString::fromLatin1("%1/meta/%2").arg(info.directory).arg(translatedLicense),
+ QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, translatedLicense));
+ }
}
+ update.appendChild(package.firstChildElement(QLatin1String("Licenses")).cloneNode());
+ }
+ } else {
+ // Extract metadata from archive
+ QFile metaFile(info.metaFile);
+ QInstaller::openForRead(&metaFile);
+ Lib7z::extractArchive(&metaFile, targetDir);
+
+ // Restore "PackageUpdate" node;
+ QDomDocument update;
+ if (!update.setContent(info.metaNode)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot restore \"PackageUpdate\" description for node %1").arg(info.name));
}
- update.appendChild(package.firstChildElement(QLatin1String("Licenses")).cloneNode());
+
+ root.appendChild(update.documentElement());
}
}
+
doc.appendChild(root);
QFile targetUpdatesXml(targetDir + QLatin1String("/Updates.xml"));
@@ -403,11 +425,10 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
qDebug() << "Found subdirectory" << it->fileName();
// because the filter is QDir::Dirs - filename means the name of the subdirectory
if (it->fileName().contains(QLatin1Char('-'))) {
- if (ignoreInvalidPackages)
- continue;
- throw QInstaller::Error(QString::fromLatin1("Component \"%1\" must not contain '-'. This is not "
- "allowed, because dashes are used as the separator between the component name and the "
- "version number internally.").arg(QDir::toNativeSeparators(it->fileName())));
+ qDebug("When using the component \"%s\" as a dependency, "
+ "to ensure backward compatibility, you must add a colon symbol at the end, "
+ "even if you do not specify a version.",
+ qUtf8Printable(it->fileName()));
}
QFile file(QString::fromLatin1("%1/meta/package.xml").arg(it->filePath()));
@@ -460,7 +481,10 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
PackageInfo info;
info.name = it->fileName();
info.version = packageElement.firstChildElement(QLatin1String("Version")).text();
- if (!QRegExp(QLatin1String("[0-9]+((\\.|-)[0-9]+)*")).exactMatch(info.version)) {
+ // Version cannot start with comparison characters, be an empty string
+ // or have whitespaces at the beginning or at the end
+ if (!QRegExp(QLatin1String("(?![<=>\\s]+)(.+)")).exactMatch(info.version) ||
+ (info.version != info.version.trimmed())) {
if (ignoreInvalidPackages)
continue;
throw QInstaller::Error(QString::fromLatin1("Component version for \"%1\" is invalid! <Version>%2</Version>")
@@ -485,6 +509,114 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
return dict;
}
+PackageInfoVector QInstallerTools::createListOfRepositoryPackages(const QStringList &repositoryDirectories,
+ QStringList *packagesToFilter, FilterType filterType)
+{
+ qDebug() << "Collecting information about available repository packages...";
+
+ bool ignoreInvalidRepositories = qApp->arguments().contains(QString::fromLatin1("--ignore-invalid-repositories"));
+
+ PackageInfoVector dict;
+ QFileInfoList entries;
+ foreach (const QString &repositoryDirectory, repositoryDirectories)
+ entries.append(QFileInfo(repositoryDirectory));
+ for (QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it) {
+
+ qDebug() << "Process repository" << it->fileName();
+
+ QFile file(QString::fromLatin1("%1/Updates.xml").arg(it->filePath()));
+
+ QFileInfo fileInfo(file);
+ if (!fileInfo.exists()) {
+ if (ignoreInvalidRepositories) {
+ qDebug() << "- skip invalid repository";
+ continue;
+ }
+ throw QInstaller::Error(QString::fromLatin1("Repository \"%1\" does not contain a update "
+ "description (Updates.xml is missing).").arg(QDir::toNativeSeparators(it->fileName())));
+ }
+ if (!file.open(QIODevice::ReadOnly)) {
+ qDebug() << "Cannot open Updates.xml for reading:" << file.errorString();
+ continue;
+ }
+
+ QString error;
+ QDomDocument doc;
+ if (!doc.setContent(&file, &error)) {
+ qDebug().nospace() << "Cannot fetch a valid version of Updates.xml from repository "
+ << it->fileName() << ": " << error;
+ continue;
+ }
+ file.close();
+
+ const QDomElement root = doc.documentElement();
+ if (root.tagName() != QLatin1String("Updates")) {
+ throw QInstaller::Error(QCoreApplication::translate("QInstaller",
+ "Invalid content in \"%1\".").arg(QDir::toNativeSeparators(file.fileName())));
+ }
+
+ const QDomNodeList children = root.childNodes();
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if ((!el.isNull()) && (el.tagName() == QLatin1String("PackageUpdate"))) {
+ QInstallerTools::PackageInfo info;
+
+ QDomElement c1 = el.firstChildElement(QInstaller::scName);
+ if (!c1.isNull())
+ info.name = c1.text();
+ else
+ continue;
+ if (filterType == Exclude) {
+ // Check for current package in exclude list, if found, skip it
+ if (packagesToFilter->contains(info.name)) {
+ continue;
+ }
+ } else {
+ // Check for current package in include list, if not found, skip it
+ if (!packagesToFilter->contains(info.name))
+ continue;
+ }
+ c1 = el.firstChildElement(QInstaller::scVersion);
+ if (!c1.isNull())
+ info.version = c1.text();
+ else
+ continue;
+
+ info.directory = QString::fromLatin1("%1/%2").arg(it->filePath(), info.name);
+ info.metaFile = QString::fromLatin1("%1/%3%2").arg(info.directory,
+ QString::fromLatin1("meta.7z"), info.version);
+
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ if (c2.at(j).toElement().tagName() == QInstaller::scDependencies)
+ info.dependencies = c2.at(j).toElement().text()
+ .split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
+ else if (c2.at(j).toElement().tagName() == QInstaller::scDownloadableArchives) {
+ QStringList names = c2.at(j).toElement().text()
+ .split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
+ foreach (const QString &name, names) {
+ info.copiedFiles.append(QString::fromLatin1("%1/%3%2").arg(info.directory,
+ name, info.version));
+ info.copiedFiles.append(QString::fromLatin1("%1/%3%2.sha1").arg(info.directory,
+ name, info.version));
+ }
+ }
+ }
+ QString metaString;
+ {
+ QTextStream metaStream(&metaString);
+ el.save(metaStream, 0);
+ }
+ info.metaNode = metaString;
+ dict.push_back(info);
+ qDebug() << "- it provides the package" << info.name << " - " << info.version;
+ }
+ }
+ }
+
+ return dict;
+}
+
QHash<QString, QString> QInstallerTools::buildPathToVersionMapping(const PackageInfoVector &info)
{
QHash<QString, QString> map;
@@ -496,13 +628,28 @@ QHash<QString, QString> QInstallerTools::buildPathToVersionMapping(const Package
static void writeSHA1ToNodeWithName(QDomDocument &doc, QDomNodeList &list, const QByteArray &sha1sum,
const QString &nodename)
{
- qDebug() << "searching sha1sum node for" << nodename;
+ qDebug() << "Searching sha1sum node for" << nodename;
+ QString sha1Value = QString::fromLatin1(sha1sum.toHex().constData());
for (int i = 0; i < list.size(); ++i) {
QDomNode curNode = list.at(i);
- QDomNode nameTag = curNode.firstChildElement(QLatin1String("Name"));
+ QDomNode nameTag = curNode.firstChildElement(scName);
if (!nameTag.isNull() && nameTag.toElement().text() == nodename) {
- QDomNode sha1Node = doc.createElement(QLatin1String("SHA1"));
- sha1Node.appendChild(doc.createTextNode(QString::fromLatin1(sha1sum.toHex().constData())));
+ QDomNode sha1Node = curNode.firstChildElement(scSHA1);
+ if (!sha1Node.isNull() && sha1Node.hasChildNodes()) {
+ QDomNode sha1NodeChild = sha1Node.firstChild();
+ QString sha1OldValue = sha1NodeChild.nodeValue();
+ if (sha1Value == sha1OldValue) {
+ qDebug() << "- keeping the existing sha1sum" << sha1OldValue;
+ continue;
+ } else {
+ qDebug() << "- clearing the old sha1sum" << sha1OldValue;
+ sha1Node.removeChild(sha1NodeChild);
+ }
+ } else {
+ sha1Node = doc.createElement(scSHA1);
+ }
+ qDebug() << "- writing the sha1sum" << sha1Value;
+ sha1Node.appendChild(doc.createTextNode(sha1Value));
curNode.appendChild(sha1Node);
}
}
@@ -570,70 +717,83 @@ void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QS
.arg(name));
}
- QStringList compressedFiles;
- QStringList filesToCompress;
- foreach (const QString &packageDir, packageDirs) {
- const QDir dataDir(QString::fromLatin1("%1/%2/data").arg(packageDir, name));
- foreach (const QString &entry, dataDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files)) {
- QFileInfo fileInfo(dataDir.absoluteFilePath(entry));
- if (fileInfo.isFile() && !fileInfo.isSymLink()) {
- const QString absoluteEntryFilePath = dataDir.absoluteFilePath(entry);
- if (Lib7z::isSupportedArchive(absoluteEntryFilePath)) {
- QFile tmp(absoluteEntryFilePath);
- QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, entry, info.version);
- qDebug() << "Copying archive from" << tmp.fileName() << "to" << target;
- if (!tmp.copy(target)) {
- throw QInstaller::Error(QString::fromLatin1("Cannot copy file \"%1\" to \"%2\": %3")
- .arg(QDir::toNativeSeparators(tmp.fileName()), QDir::toNativeSeparators(target), tmp.errorString()));
+ if (info.copiedFiles.isEmpty()) {
+ QStringList compressedFiles;
+ QStringList filesToCompress;
+ foreach (const QString &packageDir, packageDirs) {
+ const QDir dataDir(QString::fromLatin1("%1/%2/data").arg(packageDir, name));
+ foreach (const QString &entry, dataDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files)) {
+ QFileInfo fileInfo(dataDir.absoluteFilePath(entry));
+ if (fileInfo.isFile() && !fileInfo.isSymLink()) {
+ const QString absoluteEntryFilePath = dataDir.absoluteFilePath(entry);
+ if (Lib7z::isSupportedArchive(absoluteEntryFilePath)) {
+ QFile tmp(absoluteEntryFilePath);
+ QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, entry, info.version);
+ qDebug() << "Copying archive from" << tmp.fileName() << "to" << target;
+ if (!tmp.copy(target)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot copy file \"%1\" to \"%2\": %3")
+ .arg(QDir::toNativeSeparators(tmp.fileName()), QDir::toNativeSeparators(target), tmp.errorString()));
+ }
+ compressedFiles.append(target);
+ } else {
+ filesToCompress.append(absoluteEntryFilePath);
}
+ } else if (fileInfo.isDir()) {
+ qDebug() << "Compressing data directory" << entry;
+ QString target = QString::fromLatin1("%1/%3%2.7z").arg(namedRepoDir, entry, info.version);
+ Lib7z::createArchive(target, QStringList() << dataDir.absoluteFilePath(entry),
+ Lib7z::QTmpFile::No);
compressedFiles.append(target);
- } else {
- filesToCompress.append(absoluteEntryFilePath);
+ } else if (fileInfo.isSymLink()) {
+ filesToCompress.append(dataDir.absoluteFilePath(entry));
}
- } else if (fileInfo.isDir()) {
- qDebug() << "Compressing data directory" << entry;
- QString target = QString::fromLatin1("%1/%3%2.7z").arg(namedRepoDir, entry, info.version);
- Lib7z::createArchive(target, QStringList() << dataDir.absoluteFilePath(entry),
- Lib7z::QTmpFile::No);
- compressedFiles.append(target);
- } else if (fileInfo.isSymLink()) {
- filesToCompress.append(dataDir.absoluteFilePath(entry));
}
}
- }
- if (!filesToCompress.isEmpty()) {
- qDebug() << "Compressing files found in data directory:" << filesToCompress;
- QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, QLatin1String("content.7z"),
- info.version);
- Lib7z::createArchive(target, filesToCompress, Lib7z::QTmpFile::No);
- compressedFiles.append(target);
- }
+ if (!filesToCompress.isEmpty()) {
+ qDebug() << "Compressing files found in data directory:" << filesToCompress;
+ QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, QLatin1String("content.7z"),
+ info.version);
+ Lib7z::createArchive(target, filesToCompress, Lib7z::QTmpFile::No);
+ compressedFiles.append(target);
+ }
- foreach (const QString &target, compressedFiles) {
- (*infos)[i].copiedFiles.append(target);
-
- QFile archiveFile(target);
- QFile archiveHashFile(archiveFile.fileName() + QLatin1String(".sha1"));
-
- qDebug() << "Hash is stored in" << archiveHashFile.fileName();
- qDebug() << "Creating hash of archive" << archiveFile.fileName();
-
- try {
- QInstaller::openForRead(&archiveFile);
- const QByteArray hashOfArchiveData = QInstaller::calculateHash(&archiveFile,
- QCryptographicHash::Sha1).toHex();
- archiveFile.close();
-
- QInstaller::openForWrite(&archiveHashFile);
- archiveHashFile.write(hashOfArchiveData);
- qDebug() << "Generated sha1 hash:" << hashOfArchiveData;
- (*infos)[i].copiedFiles.append(archiveHashFile.fileName());
- archiveHashFile.close();
- } catch (const QInstaller::Error &/*e*/) {
- archiveFile.close();
- archiveHashFile.close();
- throw;
+ foreach (const QString &target, compressedFiles) {
+ (*infos)[i].copiedFiles.append(target);
+
+ QFile archiveFile(target);
+ QFile archiveHashFile(archiveFile.fileName() + QLatin1String(".sha1"));
+
+ qDebug() << "Hash is stored in" << archiveHashFile.fileName();
+ qDebug() << "Creating hash of archive" << archiveFile.fileName();
+
+ try {
+ QInstaller::openForRead(&archiveFile);
+ const QByteArray hashOfArchiveData = QInstaller::calculateHash(&archiveFile,
+ QCryptographicHash::Sha1).toHex();
+ archiveFile.close();
+
+ QInstaller::openForWrite(&archiveHashFile);
+ archiveHashFile.write(hashOfArchiveData);
+ qDebug() << "Generated sha1 hash:" << hashOfArchiveData;
+ (*infos)[i].copiedFiles.append(archiveHashFile.fileName());
+ archiveHashFile.close();
+ } catch (const QInstaller::Error &/*e*/) {
+ archiveFile.close();
+ archiveHashFile.close();
+ throw;
+ }
+ }
+ } else {
+ foreach (const QString &file, (*infos)[i].copiedFiles) {
+ QFileInfo fromInfo(file);
+ QFile from(file);
+ QString target = QString::fromLatin1("%1/%2").arg(namedRepoDir, fromInfo.fileName());
+ qDebug() << "Copying file from" << from.fileName() << "to" << target;
+ if (!from.copy(target)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot copy file \"%1\" to \"%2\": %3")
+ .arg(QDir::toNativeSeparators(from.fileName()), QDir::toNativeSeparators(target), from.errorString()));
+ }
}
}
}
diff --git a/tools/common/repositorygen.h b/tools/common/repositorygen.h
index 769f48729..ead9e15be 100644
--- a/tools/common/repositorygen.h
+++ b/tools/common/repositorygen.h
@@ -44,6 +44,8 @@ struct PackageInfo
QString directory;
QStringList dependencies;
QStringList copiedFiles;
+ QString metaFile;
+ QString metaNode;
};
typedef QVector<PackageInfo> PackageInfoVector;
@@ -58,6 +60,10 @@ void copyWithException(const QString &source, const QString &target, const QStri
PackageInfoVector createListOfPackages(const QStringList &packagesDirectories, QStringList *packagesToFilter,
FilterType ftype);
+
+PackageInfoVector createListOfRepositoryPackages(const QStringList &repositoryDirectories, QStringList *packagesToFilter,
+ FilterType filterType);
+
QHash<QString, QString> buildPathToVersionMapping(const PackageInfoVector &info);
void compressMetaDirectories(const QString &repoDir, const QString &baseDir,
diff --git a/tools/repogen/repogen.cpp b/tools/repogen/repogen.cpp
index 67184c49e..82ea752ca 100644
--- a/tools/repogen/repogen.cpp
+++ b/tools/repogen/repogen.cpp
@@ -95,6 +95,7 @@ int main(int argc, char** argv)
QStringList filteredPackages;
bool updateExistingRepository = false;
QStringList packagesDirectories;
+ QStringList repositoryDirectories;
QInstallerTools::FilterType filterType = QInstallerTools::Exclude;
bool remove = false;
bool updateExistingRepositoryWithNewComponents = false;
@@ -156,6 +157,19 @@ int main(int argc, char** argv)
packagesDirectories.append(args.first());
args.removeFirst();
+ } else if (args.first() == QLatin1String("--repository")) {
+ args.removeFirst();
+ if (args.isEmpty()) {
+ return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller",
+ "Error: Repository parameter missing argument"));
+ }
+
+ if (!QFileInfo(args.first()).exists()) {
+ return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller",
+ "Error: Only local filesystem repositories now supported"));
+ }
+ repositoryDirectories.append(args.first());
+ args.removeFirst();
} else if (args.first() == QLatin1String("--ignore-translations")
|| args.first() == QLatin1String("--ignore-invalid-packages")) {
args.removeFirst();
@@ -168,7 +182,7 @@ int main(int argc, char** argv)
}
}
- if (packagesDirectories.isEmpty() || (args.count() != 1)) {
+ if ((packagesDirectories.isEmpty() && repositoryDirectories.isEmpty()) || (args.count() != 1)) {
printUsage();
return 1;
}
@@ -190,8 +204,15 @@ int main(int argc, char** argv)
"Repository target directory \"%1\" already exists.").arg(QDir::toNativeSeparators(repositoryDir)));
}
- QInstallerTools::PackageInfoVector packages = QInstallerTools::createListOfPackages(packagesDirectories,
+ QInstallerTools::PackageInfoVector packages;
+
+ QInstallerTools::PackageInfoVector precompressedPackages = QInstallerTools::createListOfRepositoryPackages(repositoryDirectories,
+ &filteredPackages, filterType);
+ packages.append(precompressedPackages);
+
+ QInstallerTools::PackageInfoVector preparedPackages = QInstallerTools::createListOfPackages(packagesDirectories,
&filteredPackages, filterType);
+ packages.append(preparedPackages);
if (updateExistingRepositoryWithNewComponents) {
QDomDocument doc;
@@ -251,7 +272,10 @@ int main(int argc, char** argv)
QTemporaryDir tmp;
tmp.setAutoRemove(false);
tmpMetaDir = tmp.path();
- QInstallerTools::copyComponentData(packagesDirectories, repositoryDir, &packages);
+ QStringList directories;
+ directories.append(packagesDirectories);
+ directories.append(repositoryDirectories);
+ QInstallerTools::copyComponentData(directories, repositoryDir, &packages);
QInstallerTools::copyMetaData(tmpMetaDir, repositoryDir, packages, QLatin1String("{AnyApplication}"),
QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)));
QInstallerTools::compressMetaDirectories(tmpMetaDir, tmpMetaDir, pathToVersionMapping);