summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--Changelog41
-rwxr-xr-xcoin/create_ifw_installer.py2
-rw-r--r--coin/instructions/common_environment.yaml5
-rw-r--r--coin/instructions/make_instructions.yaml80
-rw-r--r--coin/instructions/qmake_module_build.yaml10
-rw-r--r--coin/qt-installer-package-config.json8
-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-using.qdoc15
-rw-r--r--doc/installerfw.qdoc15
-rw-r--r--doc/scripting-api/packagemanagercore.qdoc8
-rw-r--r--installerfw.pri6
-rw-r--r--src/libs/ifwtools/binarycreator.cpp10
-rw-r--r--src/libs/ifwtools/rcc/rcc.cpp22
-rw-r--r--src/libs/ifwtools/repositorygen.cpp122
-rw-r--r--src/libs/ifwtools/repositorygen.h16
-rw-r--r--src/libs/installer/adminauthorization_x11.cpp234
-rw-r--r--src/libs/installer/binarycontent.h9
-rw-r--r--src/libs/installer/commandlineparser.cpp9
-rw-r--r--src/libs/installer/component.cpp7
-rw-r--r--src/libs/installer/component_p.cpp1
-rw-r--r--src/libs/installer/componentmodel.cpp6
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp53
-rw-r--r--src/libs/installer/componentselectionpage_p.h6
-rw-r--r--src/libs/installer/constants.h5
-rw-r--r--src/libs/installer/installiconsoperation.cpp10
-rw-r--r--src/libs/installer/keepaliveobject.cpp7
-rw-r--r--src/libs/installer/loggingutils.cpp2
-rw-r--r--src/libs/installer/packagemanagercore.cpp103
-rw-r--r--src/libs/installer/packagemanagercore.h10
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp29
-rw-r--r--src/libs/installer/packagemanagercore_p.h8
-rw-r--r--src/libs/installer/packagemanagergui.cpp2
-rw-r--r--src/libs/installer/performinstallationform.cpp1
-rw-r--r--src/libs/installer/qsettingswrapper.cpp4
-rw-r--r--src/libs/installer/utils.cpp8
-rw-r--r--src/libs/kdtools/filedownloader.cpp2
-rw-r--r--src/libs/kdtools/localpackagehub.cpp8
-rw-r--r--src/libs/kdtools/localpackagehub.h4
-rw-r--r--src/libs/kdtools/selfrestarter.cpp14
-rw-r--r--src/libs/kdtools/updatefinder.cpp7
-rw-r--r--src/libs/kdtools/updateoperation.cpp2
-rw-r--r--src/libs/kdtools/updatesinfo.cpp8
-rw-r--r--src/sdk/commandlineinterface.cpp26
-rw-r--r--src/sdk/commandlineinterface.h3
-rw-r--r--src/sdk/tabcontroller.cpp19
-rw-r--r--src/sdk/translations/ifw_ar.ts2740
-rw-r--r--src/sdk/translations/ifw_de.ts83
-rw-r--r--tests/auto/installer/binaryformat/tst_binaryformat.cpp4
-rw-r--r--tests/auto/installer/clientserver/tst_clientserver.cpp2
-rw-r--r--tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7zbin182 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z.sha11
-rw-r--r--tests/auto/installer/cliinterface/data/repository/A/1.0.2-1meta.7zbin106 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/AB/1.0.2-1meta.7zbin114 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7zbin182 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z.sha11
-rw-r--r--tests/auto/installer/cliinterface/data/repository/B/1.0.0-1meta.7zbin106 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7zbin182 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z.sha11
-rw-r--r--tests/auto/installer/cliinterface/data/repository/C/1.0.0-1meta.7zbin106 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/repository/Updates.xml23
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7zbin184 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z.sha11
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0meta.7zbin106 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/AB/1.0.2-1meta.7zbin114 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7zbin184 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z.sha11
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0meta.7zbin106 -> 0 bytes
-rw-r--r--tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml10
-rw-r--r--tests/auto/installer/cliinterface/tst_cliinterface.cpp34
-rw-r--r--tests/auto/installer/contentshaupdate/contentshaupdate.pro9
-rw-r--r--tests/auto/installer/contentshaupdate/data/repository/Updates.xml48
-rw-r--r--tests/auto/installer/contentshaupdate/data/repository/componentA/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repository/componentB/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repository/componentC/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repository/componentD/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdate/Updates.xml48
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentA/0.1.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentB/0.1.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentC/2.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentD/2.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/settings.qrc14
-rw-r--r--tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp108
-rw-r--r--tests/auto/installer/factory/tst_factory.cpp8
-rw-r--r--tests/auto/installer/installer.pro3
-rw-r--r--tests/auto/installer/metadatajob/data/config.xml8
-rw-r--r--tests/auto/installer/metadatajob/settings.qrc1
-rw-r--r--tests/auto/installer/metadatajob/tst_metadatajob.cpp5
-rw-r--r--tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp6
-rw-r--r--tests/auto/installer/registerfiletypeoperation/tst_registerfiletypeoperation.cpp3
-rw-r--r--tests/auto/tools/repotest/settings.qrc24
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7zbin0 -> 32 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z.sha11
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0meta.7zbin0 -> 209 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7zbin0 -> 32 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z.sha11
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0meta.7zbin0 -> 106 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml28
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7zbin0 -> 32 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z.sha11
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0meta.7zbin0 -> 221 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7zbin0 -> 32 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z.sha11
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0meta.7zbin0 -> 106 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_2/Updates.xml28
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7zbin0 -> 32 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z.sha11
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0meta.7zbin0 -> 221 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7zbin0 -> 32 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z.sha11
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0meta.7zbin0 -> 106 bytes
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_3/Updates.xml28
-rw-r--r--tests/auto/tools/repotest/tst_repotest.cpp261
-rw-r--r--tools/devtool/binarydump.cpp6
-rw-r--r--tools/devtool/main.cpp26
-rw-r--r--tools/repocompare/repositorymanager.cpp1
-rw-r--r--tools/repogen/repogen.cpp92
118 files changed, 4001 insertions, 638 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 8798c3d5a..36f547a67 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,2 +1,2 @@
-VERSION=4.1.0
+VERSION=4.2.0
CONFIG=prepare_docs qt_docs_targets $$CONFIG
diff --git a/Changelog b/Changelog
index 34f637320..85c43e235 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,44 @@
+4.1.0
+- Emit signal when components are recalculated (QTIFW-2172)
+- Add Arabic translation (QTIFW-1425)
+- Update side widget with the binary marker changes in introduction page (QTIFW-2150)
+- Fix App Error when trying to launch a running installer (QTIFW-2149)
+- Fix value overwrite from command line (QTIFW-2021)
+- Fix DesktopDir value for AllUsers
+- CLI: Add warning when cli-only options are used without command keyword (QTIFW-2046)
+- Translations: Update existing TS files (QTIFW-2126)
+- Add possibility to move component in component tree (QTIFW-594)
+- Try rerunning execute operation (QTIFW-2125)
+- CLI: Exit when expecting user input and the output device is not a TTY (QTIFW-1949)
+- Add support for setting minimum wizard dimensions in config.xml (QTIFW-1903)
+- Save Execute operation with predefined variable (QTIFW-2124)
+- Make LicenseAgreementPage license list & details browser user-resizable (QTIFW-1987)
+- Fix wizard page addition before ComponentSelectionPage (QTIFW-1168)
+- Remove double calculation of uninstall components (QTIFW-1021)
+- Replace usage of derived LazyPlainTextEdit class with QTextEdit (QTIFW-1994)
+- Fix InstallIcons and CreateDesktopEntry operations usage as root (QTIFW-2093)
+- Add support for generating offline installer from online (QTIFW-1945)
+- Provide tooltip texts for buttons (QTIFW-2073)
+- Allow disabling undo in CreateLocalRepository operation during uninstall (QTIFW-2094)
+- Remove 'Reject Licenses' radio button and change 'Accept Licenses' to checkbox (QTIFW-2057)
+- Add highdpi support for watermark, banner, background and pagelistpixmaps (QTIFW-2084)
+- Fix translating from component script (QTIFW-392)
+- Allow disabling undo in RegisterFileType operation during uninstall (QTIFW-2089)
+- Allow disabling undo in SimpleMoveFile operation during uninstall (2090)
+- Allow disabling undo in CopyDirectory during uninstall (QTIFW-2088)
+- Allow disabling undo of GlobalSettings during uninstall (QTIFW-1973)
+- Introduce ForcedUpdate element for component (QTIFW-1565)
+- Add high dpi image support for Logo and ProductImages (QTIFW-2061, QTIFW-2060)
+- Copy highdpi images as resource to installer (QTIFW-2080)
+- Tools: fix binarycreator usage with unified meta-only repositories (QTIFW-2051)
+- Add option to define operations in component.xml (QTIFW-507)
+- Print package information in xml format (QTIFW-1950)
+- Arrange licenses and filter duplicates
+- CLI: Print extra archive information with higher verbosity level (QTIFW-1998)
+- Allow to set ForcedInstallation from component script (QTIFW-675)
+- Automatically uninstall unneeded virtual components (QTBUG-76210)
+- CLI: Add possibility to filter installed packages with regexp (QTIFW-1953)
+
4.0.1
- Enable HighDPI support in X11
- Fix AppendFile undo (QTIFW-2020)
diff --git a/coin/create_ifw_installer.py b/coin/create_ifw_installer.py
index aacb6adeb..6da4f1d16 100755
--- a/coin/create_ifw_installer.py
+++ b/coin/create_ifw_installer.py
@@ -76,7 +76,7 @@ def create_installer_package(src_dir: str, bld_dir: str, target_dir: str, target
check_call(["strip", os.path.join(package_dir, 'bin/repogen')])
# Copy remaining payload to package dir
- shutil.copytree(os.path.join(bld_dir, 'doc'), os.path.join(package_dir, 'doc'))
+ shutil.copytree(os.path.join(bld_dir, 'doc/html'), os.path.join(package_dir, 'doc/html'))
shutil.copytree(os.path.join(src_dir, 'examples'), os.path.join(package_dir, 'examples'))
shutil.copy(os.path.join(src_dir, 'README'), package_dir)
diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml
index 4bef13a71..c3fd59f15 100644
--- a/coin/instructions/common_environment.yaml
+++ b/coin/instructions/common_environment.yaml
@@ -62,3 +62,8 @@ instructions:
condition: property
property: host.os
equals_value: Linux
+ - type: Group
+ instructions:
+ - type: EnvironmentVariable
+ variableName: PACKAGING_KEYS_CONFIG_URL
+ variableValue: "http://ci-files01-hki.intra.qt.io/input/semisecure/packaging/packaging_secure.ini"
diff --git a/coin/instructions/make_instructions.yaml b/coin/instructions/make_instructions.yaml
index f9f1dd406..003e9d746 100644
--- a/coin/instructions/make_instructions.yaml
+++ b/coin/instructions/make_instructions.yaml
@@ -81,8 +81,77 @@ instructions:
property: target.compiler
in_values: [MSVC2013, MSVC2015, MSVC2017, MSVC2019]
+ - type: Group
+ instructions:
+ - type: ChangeDirectory
+ directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}"
+ - type: ExecuteCommand
+ command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}} --target-name QtInstallerFramework-linux-x64-4.1.0.run"
+ maxTimeInSeconds: 36000
+ maxTimeBetweenOutput: 3600
+ userMessageOnFailure: "Failed to create ifw installer."
+ - type: Rename
+ sourcePath: "{{.SourceDir}}/QtInstallerFramework-linux-x64-4.1.0.run"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.1.0.run"
+ userMessageOnFailure: "Failed to copy installer."
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+
+ - type: Group
+ instructions:
+ - type: ChangeDirectory
+ directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}"
+ - type: ExecuteCommand
+ command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}} --target-name QtInstallerFramework-macOS-x86_64-4.1.0.app"
+ maxTimeInSeconds: 36000
+ maxTimeBetweenOutput: 3600
+ userMessageOnFailure: "Failed to create ifw installer."
+ - type: EnvironmentVariable
+ variableName: QT_CODESIGN_IDENTITY_KEY
+ variableValue: "A5GTH44LYL"
+ - type: ExecuteCommand
+ command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/sign_installer.py mac --file={{.SourceDir}}/QtInstallerFramework-macOS-x86_64-4.1.0.app"
+ maxTimeInSeconds: 36000
+ maxTimeBetweenOutput: 3600
+ userMessageOnFailure: "Failed to sign the ifw installer"
+ - type: ExecuteCommand
+ command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/notarize.py --dmg={{.SourceDir}}/QtInstallerFramework-macOS-x86_64-4.1.0.dmg"
+ maxTimeInSeconds: 36000
+ maxTimeBetweenOutput: 3600
+ userMessageOnFailure: "Failed to notarize the ifw installer"
+ - type: Rename
+ sourcePath: "{{.SourceDir}}/QtInstallerFramework-macOS-x86_64-4.1.0.dmg"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x86_64-4.1.0.dmg"
+ userMessageOnFailure: "Failed to copy installer."
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+
+ - type: Group
+ instructions:
+ - type: ChangeDirectory
+ directory: "{{.SourceDir}}"
+ - type: ExecuteCommand
+ command: "{{.Env.PYTHON3_PATH}}\\python {{.SourceDir}}\\coin\\create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir C:\\{{.SourceDir}} --target-name QtInstallerFramework-windows-x86-4.1.0"
+ maxTimeInSeconds: 1200
+ maxTimeBetweenOutput: 1200
+ userMessageOnFailure: "Failed to create ifw installer."
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Windows
+
- type: SignPackage
- directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}"
+ directory: "{{.SourceDir}}"
maxTimeInSeconds: 1200
maxTimeBetweenOutput: 1200
enable_if:
@@ -90,6 +159,15 @@ instructions:
property: host.os
equals_value: Windows
+ - type: Rename
+ sourcePath: "{{.SourceDir}}/QtInstallerFramework-windows-x86-4.1.0.exe"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x86-4.1.0.exe"
+ userMessageOnFailure: "Failed to copy installer."
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+
- type: UploadArtifact
archiveDirectory: "{{.InstallRoot}}/{{.AgentWorkingDir}}"
transferType: UploadModuleBuildArtifact
diff --git a/coin/instructions/qmake_module_build.yaml b/coin/instructions/qmake_module_build.yaml
index 120a7745f..37ff0f396 100644
--- a/coin/instructions/qmake_module_build.yaml
+++ b/coin/instructions/qmake_module_build.yaml
@@ -3,6 +3,16 @@ instructions:
- type: MakeDirectory
directory: ".git"
+ - type: Group
+ instructions:
+ - type: InstallSourceArchive
+ maxTimeInSeconds: 600
+ maxTimeBetweenOutput: 600
+ project: qtsdk/qtsdk
+ ref: master
+ directory: qtsdk/qtsdk
+ userMessageOnFailure: "Could not install qtsdk/qtsdk source archive. Please investigate why."
+
- type: SetBuildDirectory
directory: "{{.SourceDir}}"
disable_if:
diff --git a/coin/qt-installer-package-config.json b/coin/qt-installer-package-config.json
new file mode 100644
index 000000000..563110519
--- /dev/null
+++ b/coin/qt-installer-package-config.json
@@ -0,0 +1,8 @@
+{
+ "version": "1",
+ "module-split": {
+ "__extract__": [
+ "**/*QtInstallerFramework-*"
+ ]
+ }
+}
diff --git a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml
index f94336822..4d9a3c7a7 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>4.1.0</Version>
- <ReleaseDate>2020-12-09</ReleaseDate>
+ <Version>4.2.0</Version>
+ <ReleaseDate>2021-03-03</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 b68e6cbf2..5c56a177e 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>4.1.0</Version>
- <ReleaseDate>2020-12-09</ReleaseDate>
+ <Version>4.2.0</Version>
+ <ReleaseDate>2021-03-03</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-using.qdoc b/doc/installerfw-using.qdoc
index 8786cd005..5c5bbbbc4 100644
--- a/doc/installerfw-using.qdoc
+++ b/doc/installerfw-using.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -448,10 +448,12 @@
The \c search command can be used to search components from available repositories, or
from integrated binary content in case of an offline installer. It can be used with
no arguments to list all available components or with a regular expression to get a list
- of only components matching the pattern:
+ of only components matching the pattern. The \c --filter-packages option can be
+ used to specify additional filters for the search operation. For a list of usable
+ information elements with the option, refer to \l{Summary of Package Information File Elements}.
\code
- installer.exe search "expression"
+ installer.exe --filter-packages "DisplayName=MyComponent, Version=1.0" search "expression"
\endcode
\section1 Performing Full Uninstallation
@@ -484,7 +486,7 @@
beforehand with appropriate options:
\code
- installer.exe --root "C:\MyInstallation" --accept-licenses --default-answer install componentA
+ installer.exe --root "C:\MyInstallation" --accept-licenses --default-answer --confirm-command install componentA
\endcode
In this example, \c --accept-licenses is used to automatically accept all license agreements
@@ -494,4 +496,9 @@
queries, for example to confirm overwriting of an existing directory, you could use
\c {--auto-answer OverwriteTargetDirectory=Yes}. Automatic answers are shown on the
console output and installation log.
+
+ By default, the installer and maintenance tool will print a summary of components to be
+ affected by the command and then ask for permission to continue performing the action,
+ to prevent accidental changes. For unattended usage, this can be skipped by using the
+ \c --confirm-command option.
*/
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc
index e9a6463bc..6f77d8850 100644
--- a/doc/installerfw.qdoc
+++ b/doc/installerfw.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -247,6 +247,11 @@
\li Create a local repository inside the installation directory. This option has no
effect on online installers.
\row
+ \li --fp, --filter-packages <element=regex,...>
+ \li [CLI] Comma separated list of additional key-value pair filters used to query packages with the
+ search command. The keys can be any of the possible package information elements, like
+ \c DisplayName and \c Description.
+ \row
\li --am, --accept-messages
\li [CLI] Accepts all message queries without user input.
\row
@@ -316,6 +321,9 @@
\row
\li se, search <regexp>
\li Search available packages. If no search pattern is given, show all available packages.
+
+ \note The \c --filter-packages option can be used to specify additional filters for
+ the search operation. See \l{Summary of Options}.
\row
\li co, create-offline <pkg ...>
\li Create offline installer from selected packages.
@@ -1215,6 +1223,11 @@
\row
\li -v or --verbose
\li Display debug output.
+ \row
+ \li -s or --sha-update p1,...,pn
+ \li Comma-separated list of packages to be updated based on the component sha
+ checksum instead of the version number. This parameter adds a new \c <ContentSha1>
+ node to the \c Updates.xml.
\endtable
\note We recommend that you use the \c {--update-new-packages} parameter
to update an existing repository, especially if you have a content delivery
diff --git a/doc/scripting-api/packagemanagercore.qdoc b/doc/scripting-api/packagemanagercore.qdoc
index bacf50f27..f4d71ee97 100644
--- a/doc/scripting-api/packagemanagercore.qdoc
+++ b/doc/scripting-api/packagemanagercore.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -861,6 +861,12 @@
*/
/*!
+ \qmlmethod boolean installer::isPackageViewer()
+
+ Returns \c true if the current installer is executed as package viewer.
+*/
+
+/*!
\qmlmethod boolean installer::isUserSetBinaryMarker()
Returns \c true if the magic binary marker has been set by user.
diff --git a/installerfw.pri b/installerfw.pri
index e38bb5108..377d07d2c 100644
--- a/installerfw.pri
+++ b/installerfw.pri
@@ -3,9 +3,9 @@
}
IFW_PRI_INCLUDED = 1
-IFW_VERSION_STR = 4.1.0
-IFW_VERSION = 0x040100
-IFW_VERSION_WIN32 = 4,1,0,0
+IFW_VERSION_STR = 4.2.0
+IFW_VERSION = 0x040200
+IFW_VERSION_WIN32 = 4,2,0,0
IFW_VERSION_STR_WIN32 = $$IFW_VERSION_STR\0
diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp
index deea01ed7..a813c7f50 100644
--- a/src/libs/ifwtools/binarycreator.cpp
+++ b/src/libs/ifwtools/binarycreator.cpp
@@ -410,9 +410,9 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt
collection.setName(info.name.toUtf8());
qDebug() << "Creating resource archive for" << info.name;
- foreach (const QString &file, info.copiedFiles) {
- const QSharedPointer<Resource> resource(new Resource(file));
- qDebug().nospace() << "Appending " << file << " (" << humanReadableSize(resource->size()) << ")";
+ foreach (const QString &copiedFile, info.copiedFiles) {
+ const QSharedPointer<Resource> resource(new Resource(copiedFile));
+ qDebug().nospace() << "Appending " << copiedFile << " (" << humanReadableSize(resource->size()) << ")";
collection.appendResource(resource);
}
input.manager.insertCollection(collection);
@@ -610,8 +610,8 @@ void QInstallerTools::copyConfigData(const QString &configFile, const QString &t
if (tagName == QLatin1String("ProductImages")) {
const QDomNodeList childNodes = domElement.childNodes();
- for (int i = 0; i < childNodes.count(); ++i) {
- const QDomElement childElement = childNodes.at(i).toElement();
+ for (int index = 0; index < childNodes.count(); ++index) {
+ const QDomElement childElement = childNodes.at(index).toElement();
const QString childName = childElement.tagName();
if (childName != QLatin1String("Image"))
continue;
diff --git a/src/libs/ifwtools/rcc/rcc.cpp b/src/libs/ifwtools/rcc/rcc.cpp
index 10b7cbc4f..12f399937 100644
--- a/src/libs/ifwtools/rcc/rcc.cpp
+++ b/src/libs/ifwtools/rcc/rcc.cpp
@@ -120,18 +120,18 @@ public:
RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
QLocale::Language language, QLocale::Country country, uint flags,
int compressLevel, int compressThreshold)
+ : m_flags(flags)
+ , m_name(name)
+ , m_language(language)
+ , m_country(country)
+ , m_fileInfo(fileInfo)
+ , m_parent(nullptr)
+ , m_compressLevel(compressLevel)
+ , m_compressThreshold(compressThreshold)
+ , m_nameOffset(0)
+ , m_dataOffset(0)
+ , m_childOffset(0)
{
- m_name = name;
- m_fileInfo = fileInfo;
- m_language = language;
- m_country = country;
- m_flags = flags;
- m_parent = nullptr;
- m_nameOffset = 0;
- m_dataOffset = 0;
- m_childOffset = 0;
- m_compressLevel = compressLevel;
- m_compressThreshold = compressThreshold;
}
RCCFileInfo::~RCCFileInfo()
diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp
index fbcf7b9f3..d8339b063 100644
--- a/src/libs/ifwtools/repositorygen.cpp
+++ b/src/libs/ifwtools/repositorygen.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -52,6 +52,9 @@
#include <iostream>
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
using namespace QInstaller;
using namespace QInstallerTools;
@@ -69,6 +72,8 @@ 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;
+ std::cout << " -s|--sha-update p1,...,pn List of packages which are updated using" <<std::endl;
+ std::cout << " content sha1 instead of version number." << std::endl;
}
QString QInstallerTools::makePathAbsolute(const QString &path)
@@ -289,6 +294,11 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
fileElement.setAttribute(QLatin1String("OS"), QLatin1String("Any"));
update.appendChild(fileElement);
+ if (info.createContentSha1Node) {
+ QDomNode contentSha1Element = update.appendChild(doc.createElement(QLatin1String("ContentSha1")));
+ contentSha1Element.appendChild(doc.createTextNode(info.contentSha1));
+ }
+
root.appendChild(update);
// copy script file
@@ -414,7 +424,7 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
}
PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packagesDirectories,
- QStringList *packagesToFilter, FilterType filterType)
+ QStringList *packagesToFilter, FilterType filterType, QStringList packagesUpdatedWithSha)
{
qDebug() << "Collecting information about available packages...";
@@ -508,6 +518,13 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
info.dependencies = packageElement.firstChildElement(QLatin1String("Dependencies")).text()
.split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
info.directory = it->filePath();
+ if (packagesUpdatedWithSha.contains(info.name)) {
+ info.createContentSha1Node = true;
+ packagesUpdatedWithSha.removeOne(info.name);
+ } else {
+ info.createContentSha1Node = false;
+ }
+
dict.push_back(info);
qDebug() << "- it provides the package" << info.name << " - " << info.version;
@@ -521,6 +538,11 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
if (dict.isEmpty())
qDebug() << "No available packages found at the specified location.";
+ if (!packagesUpdatedWithSha.isEmpty()) {
+ throw QInstaller::Error(QString::fromLatin1("The following packages could not be found in "
+ "package directory: %1").arg(packagesUpdatedWithSha.join(QLatin1String(", "))));
+ }
+
return dict;
}
@@ -650,8 +672,33 @@ PackageInfoVector QInstallerTools::createListOfRepositoryPackages(const QStringL
el.save(metaStream, 0);
}
info.metaNode = metaString;
- dict.push_back(info);
- qDebug() << "- it provides the package" << info.name << " - " << info.version;
+
+ bool pushToDict = true;
+ bool replacement = false;
+ // Check whether this package already exists in vector:
+ for (int i = 0; i < dict.size(); ++i) {
+ const QInstallerTools::PackageInfo oldInfo = dict.at(i);
+ if (oldInfo.name != info.name)
+ continue;
+
+ if (KDUpdater::compareVersion(info.version, oldInfo.version) > 0) {
+ // A package with newer version, it will replace the existing one.
+ dict.remove(i);
+ replacement = true;
+ } else {
+ // A package with older or same version, do not add it again.
+ pushToDict = false;
+ }
+ break;
+ }
+
+ if (pushToDict) {
+ replacement ? qDebug() << "- it provides a new version of the package" << info.name << " - " << info.version << "- replaced"
+ : qDebug() << "- it provides the package" << info.name << " - " << info.version;
+ dict.push_back(info);
+ } else {
+ qDebug() << "- it provides an old version of the package" << info.name << " - " << info.version << "- ignored";
+ }
}
}
}
@@ -755,14 +802,14 @@ QStringList QInstallerTools::unifyMetadata(const QString &repoDir, const QString
QFile archiveFile(existingRepoDir);
QInstaller::openForRead(&archiveFile);
Lib7z::extractArchive(&archiveFile, existingRepoTemp);
- QDir dir(existingRepoTemp);
- QStringList existingRepoEntries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ QDir dir2(existingRepoTemp);
+ QStringList existingRepoEntries = dir2.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString existingRepoEntry, existingRepoEntries) {
if (entryList.contains(existingRepoEntry)) {
continue;
} else {
- dir.cd(existingRepoEntry);
- const QString absPath = dir.absolutePath();
+ dir2.cd(existingRepoEntry);
+ const QString absPath = dir2.absolutePath();
absPaths.append(absPath);
}
}
@@ -902,6 +949,8 @@ void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QS
archiveHashFile.write(hashOfArchiveData);
qDebug() << "Generated sha1 hash:" << hashOfArchiveData;
(*infos)[i].copiedFiles.append(archiveHashFile.fileName());
+ if ((*infos)[i].createContentSha1Node)
+ (*infos)[i].contentSha1 = QLatin1String(hashOfArchiveData);
archiveHashFile.close();
} catch (const QInstaller::Error &/*e*/) {
archiveFile.close();
@@ -987,3 +1036,60 @@ QString QInstallerTools::existingUniteMeta7z(const QString &repositoryDir)
}
return uniteMeta7z;
}
+
+PackageInfoVector QInstallerTools::collectPackages(RepositoryInfo info, QStringList *filteredPackages, FilterType filterType, bool updateNewComponents, QStringList packagesUpdatedWithSha)
+{
+ PackageInfoVector packages;
+ PackageInfoVector precompressedPackages = QInstallerTools::createListOfRepositoryPackages(info.repositoryPackages,
+ filteredPackages, filterType);
+ packages.append(precompressedPackages);
+
+ PackageInfoVector preparedPackages = QInstallerTools::createListOfPackages(info.packages,
+ filteredPackages, filterType, packagesUpdatedWithSha);
+ packages.append(preparedPackages);
+ if (updateNewComponents) {
+ filterNewComponents(info.repositoryDir, packages);
+ }
+ foreach (const QInstallerTools::PackageInfo &package, packages) {
+ const QFileInfo fi(info.repositoryDir, package.name);
+ if (fi.exists())
+ removeDirectory(fi.absoluteFilePath());
+ }
+ return packages;
+}
+
+void QInstallerTools::createRepository(RepositoryInfo info, PackageInfoVector *packages,
+ const QString &tmpMetaDir, bool createComponentMetadata, bool createUnifiedMetadata)
+{
+ QHash<QString, QString> pathToVersionMapping = QInstallerTools::buildPathToVersionMapping(*packages);
+
+ QStringList directories;
+ directories.append(info.packages);
+ directories.append(info.repositoryPackages);
+ QStringList unite7zFiles;
+ foreach (const QString &repositoryDirectory, info.repositoryPackages) {
+ QDirIterator it(repositoryDirectory, QStringList(QLatin1String("*_meta.7z"))
+ , QDir::Files | QDir::CaseSensitive);
+ while (it.hasNext()) {
+ it.next();
+ unite7zFiles.append(it.fileInfo().absoluteFilePath());
+ }
+ }
+ QInstallerTools::copyComponentData(directories, info.repositoryDir, packages);
+ QInstallerTools::copyMetaData(tmpMetaDir, info.repositoryDir, *packages, QLatin1String("{AnyApplication}"),
+ QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)), unite7zFiles);
+
+ QString existing7z = QInstallerTools::existingUniteMeta7z(info.repositoryDir);
+ if (!existing7z.isEmpty())
+ existing7z = info.repositoryDir + QDir::separator() + existing7z;
+ QInstallerTools::compressMetaDirectories(tmpMetaDir, existing7z, pathToVersionMapping,
+ createComponentMetadata, createUnifiedMetadata);
+
+ QDirIterator it(info.repositoryDir, QStringList(QLatin1String("Updates*.xml"))
+ << QLatin1String("*_meta.7z"), QDir::Files | QDir::CaseSensitive);
+ while (it.hasNext()) {
+ it.next();
+ QFile::remove(it.fileInfo().absoluteFilePath());
+ }
+ QInstaller::moveDirectoryContents(tmpMetaDir, info.repositoryDir);
+}
diff --git a/src/libs/ifwtools/repositorygen.h b/src/libs/ifwtools/repositorygen.h
index 49d0a51dd..0da81db67 100644
--- a/src/libs/ifwtools/repositorygen.h
+++ b/src/libs/ifwtools/repositorygen.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,6 +48,8 @@ struct IFWTOOLS_EXPORT PackageInfo
QStringList copiedFiles;
QString metaFile;
QString metaNode;
+ QString contentSha1;
+ bool createContentSha1Node;
};
typedef QVector<PackageInfo> PackageInfoVector;
@@ -56,12 +58,19 @@ enum IFWTOOLS_EXPORT FilterType {
Exclude
};
+struct IFWTOOLS_EXPORT RepositoryInfo
+{
+ QStringList packages;
+ QStringList repositoryPackages;
+ QString repositoryDir;
+};
+
void IFWTOOLS_EXPORT printRepositoryGenOptions();
QString IFWTOOLS_EXPORT makePathAbsolute(const QString &path);
void IFWTOOLS_EXPORT copyWithException(const QString &source, const QString &target, const QString &kind = QString());
PackageInfoVector IFWTOOLS_EXPORT createListOfPackages(const QStringList &packagesDirectories, QStringList *packagesToFilter,
- FilterType ftype);
+ FilterType ftype, QStringList packagesUpdatedWithSha = QStringList());
PackageInfoVector IFWTOOLS_EXPORT createListOfRepositoryPackages(const QStringList &repositoryDirectories, QStringList *packagesToFilter,
FilterType filterType);
@@ -82,7 +91,8 @@ void IFWTOOLS_EXPORT copyComponentData(const QStringList &packageDir, const QStr
void IFWTOOLS_EXPORT filterNewComponents(const QString &repositoryDir, QInstallerTools::PackageInfoVector &packages);
QString IFWTOOLS_EXPORT existingUniteMeta7z(const QString &repositoryDir);
-
+PackageInfoVector IFWTOOLS_EXPORT collectPackages(RepositoryInfo info, QStringList *filteredPackages, FilterType filterType, bool updateNewComponents, QStringList packagesUpdatedWithSha);
+void IFWTOOLS_EXPORT createRepository(RepositoryInfo info, PackageInfoVector *packages, const QString &tmpMetaDir, bool createComponentMetadata, bool createUnifiedMetadata);
} // namespace QInstallerTools
#endif // REPOSITORYGEN_H
diff --git a/src/libs/installer/adminauthorization_x11.cpp b/src/libs/installer/adminauthorization_x11.cpp
index 14691c9de..f4951d523 100644
--- a/src/libs/installer/adminauthorization_x11.cpp
+++ b/src/libs/installer/adminauthorization_x11.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,41 +28,18 @@
#include "adminauthorization.h"
-#include <QtCore/QFile>
-#include <QtCore/QRegExp>
-#include <QDebug>
+#include "globals.h"
+#include <QDebug>
#include <QApplication>
-#include <QInputDialog>
#include <QMessageBox>
+#include <QProcess>
-#include <cstdlib>
-#include <sys/resource.h>
#include <unistd.h>
-#include <fcntl.h>
-
-#ifdef Q_OS_LINUX
-#include <linux/limits.h>
-#include <pty.h>
-#else
-#ifdef Q_OS_FREEBSD
-#include <libutil.h>
-#include <signal.h>
-#else
-#include <util.h>
-#endif
-#endif
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
#include <iostream>
-#include "globals.h"
-
-#define SU_COMMAND "/usr/bin/sudo"
-//#define SU_COMMAND "/bin/echo"
+#define PKEXEC_COMMAND "/usr/bin/pkexec"
namespace QInstaller {
@@ -72,24 +49,6 @@ namespace QInstaller {
\internal
*/
-static QString getPassword(QWidget *parent)
-{
- if (qobject_cast<QApplication*> (qApp) != 0) {
- bool ok = false;
- const QString result = QInputDialog::getText(parent, QObject::tr("Authorization required"),
- QObject::tr("Enter your password to authorize for sudo:"),
- QLineEdit::Password, QString(), &ok);
- return ok ? result : QString();
- } else {
- std::cout << QObject::tr("Authorization required").toStdString() << std::endl;
- std::cout << QObject::tr("Enter your password to authorize for sudo:").toStdString()
- << std::endl;
- std::string password;
- std::cin >> password;
- return QString::fromStdString(password);
- }
-}
-
static void printError(QWidget *parent, const QString &value)
{
if (qobject_cast<QApplication*> (qApp) != 0) {
@@ -105,184 +64,21 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const
const QString fallback = program + QLatin1String(" ") + arguments.join(QLatin1String(" "));
qCDebug(QInstaller::lcServer) << "Fallback:" << fallback;
- // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it
- int masterFD = -1;
- int slaveFD = -1;
- char ptsn[ PATH_MAX ];
+ QProcess process;
+ process.setProcessChannelMode(QProcess::MergedChannels);
+ process.start(QLatin1String(PKEXEC_COMMAND), QStringList() << program << arguments, QIODevice::ReadOnly);
- if (::openpty(&masterFD, &slaveFD, ptsn, 0, 0))
- return false;
-
- masterFD = ::posix_openpt(O_RDWR | O_NOCTTY);
- if (masterFD < 0)
- return false;
-
- const QByteArray ttyName = ::ptsname(masterFD);
-
- if (::grantpt(masterFD)) {
- ::close(masterFD);
- return false;
- }
-
- ::revoke(ttyName);
- ::unlockpt(masterFD);
-
- slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY);
- if (slaveFD < 0) {
- ::close(masterFD);
+ if (!process.waitForStarted() || !process.waitForFinished(-1)) {
+ printError(parent, process.errorString());
+ if (process.state() > QProcess::NotRunning)
+ process.kill();
return false;
}
-
- ::fcntl(masterFD, F_SETFD, FD_CLOEXEC);
- ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC);
- int pipedData[2];
- if (pipe(pipedData) != 0)
- return false;
-
- int flags = ::fcntl(pipedData[0], F_GETFL);
- if (flags != -1)
- ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK);
-
- flags = ::fcntl(masterFD, F_GETFL);
- if (flags != -1)
- ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK);
-
- pid_t child = fork();
-
- if (child < -1) {
- ::close(masterFD);
- ::close(slaveFD);
- ::close(pipedData[0]);
- ::close(pipedData[1]);
- return false;
- }
-
- // parent process
- else if (child > 0) {
- ::close(slaveFD);
- //close writing end of pipe
- ::close(pipedData[1]);
-
- QRegExp re(QLatin1String("[Pp]assword.*:"));
- QByteArray data;
- QByteArray errData;
- int bytes = 0;
- int errBytes = 0;
- char buf[1024];
- char errBuf[1024];
- int status;
- bool statusValid = false;
- while (bytes >= 0) {
- const pid_t waitResult = ::waitpid(child, &status, WNOHANG);
- if (waitResult == -1) {
- break;
- }
- if (waitResult == child) {
- statusValid = true;
- break;
- }
- bytes = ::read(masterFD, buf, 1023);
- if (bytes == -1 && errno == EAGAIN)
- bytes = 0;
- else if (bytes > 0)
- data.append(buf, bytes);
- errBytes = ::read(pipedData[0], errBuf, 1023);
- if (errBytes > 0)
- {
- errData.append(errBuf, errBytes);
- errBytes=0;
- }
- if (bytes > 0) {
- const QString line = QString::fromLatin1(data);
- if (re.indexIn(line) != -1) {
- const QString password = getPassword(parent);
- if (password.isEmpty()) {
- QByteArray pwd = password.toLatin1();
- for (int i = 0; i < 3; ++i) {
- ::write(masterFD, pwd.data(), pwd.length());
- ::write(masterFD, "\n", 1);
- }
- return false;
- }
- QByteArray pwd = password.toLatin1();
- ::write(masterFD, pwd.data(), pwd.length());
- ::write(masterFD, "\n", 1);
- ::read(masterFD, buf, pwd.length() + 1);
- }
- }
- if (bytes == 0)
- ::usleep(100000);
- }
-
- while (true) {
- errBytes = ::read(pipedData[0], errBuf, 1023);
- if (errBytes == -1 && errno == EAGAIN) {
- ::usleep(100000);
- continue;
- }
-
- if (errBytes <= 0)
- break;
-
- errData.append(errBuf, errBytes);
- }
-
- const bool success = statusValid && WIFEXITED(status) && WEXITSTATUS(status) == 0;
-
- if (!success && !errData.isEmpty()) {
- printError(parent, QString::fromLocal8Bit(errData.constData()));
- }
-
- ::close(pipedData[0]);
- return success;
- }
-
- // child process
- else {
- ::close(pipedData[0]);
- // Reset signal handlers
- for (int sig = 1; sig < NSIG; ++sig)
- signal(sig, SIG_DFL);
- signal(SIGHUP, SIG_IGN);
-
- ::setsid();
-
- ::ioctl(slaveFD, TIOCSCTTY, 1);
- int pgrp = ::getpid();
- ::tcsetpgrp(slaveFD, pgrp);
-
- ::dup2(slaveFD, 0);
- ::dup2(slaveFD, 1);
- ::dup2(pipedData[1], 2);
-
- // close all file descriptors
- struct rlimit rlp;
- getrlimit(RLIMIT_NOFILE, &rlp);
- for (int i = 3; i < static_cast<int>(rlp.rlim_cur); ++i)
- ::close(i);
-
- char **argp = (char **) ::malloc((arguments.count() + 4) * sizeof(char *));
- QList<QByteArray> args;
- args.push_back(SU_COMMAND);
- args.push_back("-b");
- args.push_back(program.toLocal8Bit());
- for (QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it)
- args.push_back(it->toLocal8Bit());
-
- int i = 0;
- for (QList<QByteArray>::iterator it = args.begin(); it != args.end(); ++it, ++i)
- argp[i] = it->data();
- argp[i] = 0;
-
- ::unsetenv("LANG");
- ::unsetenv("LC_ALL");
-
- int exitStatus = 0;
- if (::execv(SU_COMMAND, argp) == -1)
- exitStatus = -errno;
- _exit(exitStatus);
+ if (process.exitCode() != EXIT_SUCCESS) {
+ printError(parent, QLatin1String(process.readAll()));
return false;
}
+ return true;
}
// has no guarantee to work
diff --git a/src/libs/installer/binarycontent.h b/src/libs/installer/binarycontent.h
index a21cd69e7..7d50c0e28 100644
--- a/src/libs/installer/binarycontent.h
+++ b/src/libs/installer/binarycontent.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,6 +48,13 @@ public:
static const qint64 MagicUpdaterMarker = 0x12023235UL;
static const qint64 MagicPackageManagerMarker = 0x12023236UL;
+ // additional distinguishers only used at runtime, not written to the binary itself
+ enum MagicMarkerSupplement {
+ Default = 0x0,
+ OfflineGenerator = 0x1,
+ PackageViewer = 0x2
+ };
+
// the cookie put at the end of the file
static const quint64 MagicCookie = 0xc2630a1c99d668f8LL; // binary
static const quint64 MagicCookieDat = 0xc2630a1c99d668f9LL; // data
diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp
index 59d42eefc..4bfc4ad3a 100644
--- a/src/libs/installer/commandlineparser.cpp
+++ b/src/libs/installer/commandlineparser.cpp
@@ -59,6 +59,9 @@ CommandLineParser::CommandLineParser()
.arg(CommandLineOptions::scListShort, CommandLineOptions::scListLong)
+ indent + QString::fromLatin1("%1, %2 - search available packages - <regexp>\n")
.arg(CommandLineOptions::scSearchShort, CommandLineOptions::scSearchLong)
+ + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
+ .arg(CommandLineOptions::scFilterPackagesLong)
+ + indent + indent + QLatin1String("additional filters for the search operation\n")
+ indent + QString::fromLatin1("%1, %2 - create offline installer from selected packages - <pkg ...>\n")
.arg(CommandLineOptions::scCreateOfflineShort, CommandLineOptions::scCreateOfflineLong)
+ indent + QString::fromLatin1("%1, %2 - uninstall all packages and remove entire program directory")
@@ -161,6 +164,12 @@ CommandLineParser::CommandLineParser()
<< CommandLineOptions::scCreateLocalRepositoryShort << CommandLineOptions::scCreateLocalRepositoryLong,
QLatin1String("Create a local repository inside the installation directory. This option "
"has no effect on online installers.")));
+ addOptionWithContext(QCommandLineOption(QStringList()
+ << CommandLineOptions::scFilterPackagesShort << CommandLineOptions::scFilterPackagesLong,
+ QLatin1String("[CLI] Comma separated list of additional key-value pair filters used to query packages with the "
+ "search command. The keys can be any of the possible package information elements, like "
+ "\"DisplayName\" and \"Description\"."),
+ QLatin1String("element=regex,...")), CommandLineOnly);
// Message query options
addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptMessageQueryShort
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index 66f333377..8142cd4e1 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -300,11 +300,14 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package)
setValue(scCurrentState, scInstalled);
setValue(scCheckable, package.checkable ? scTrue : scFalse);
setValue(scExpandedByDefault, package.expandedByDefault ? scTrue : scFalse);
+ setValue(scContentSha1, package.contentSha1);
}
/*!
Sets variables according to the values set in the package.xml file of \a package.
Also loads UI files, licenses and translations if they are referenced in the package.xml.
+ If the \c PackageManagerCore object of this component is run as package viewer, then
+ only sets the variables without loading referenced files.
*/
void Component::loadDataFromPackage(const Package &package)
{
@@ -341,6 +344,10 @@ void Component::loadDataFromPackage(const Package &package)
if (PackageManagerCore::noForceInstallation())
forced = scFalse;
setValue(scForcedInstallation, forced);
+ setValue(scContentSha1, package.data(scContentSha1).toString());
+
+ if (d->m_core->isPackageViewer())
+ return;
setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url));
const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString()
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 8533d8e4c..0f74e423c 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -54,6 +54,7 @@ ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq)
, m_autoCreateOperations(true)
, m_operationsCreatedSuccessfully(true)
, m_updateIsAvailable(false)
+ , m_unstable(false)
{
}
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index aab487b45..642828ad7 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -260,9 +260,9 @@ bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, in
newValue = (oldValue == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
}
QSet<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
- foreach (const QModelIndex &index, changed) {
- emit dataChanged(index, index);
- emit checkStateChanged(index);
+ foreach (const QModelIndex &changedIndex, changed) {
+ emit dataChanged(changedIndex, changedIndex);
+ emit checkStateChanged(changedIndex);
}
updateAndEmitModelState(); // update the internal state
} else {
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index a9f4ba134..174b35038 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -49,6 +49,7 @@
#include <QFileDialog>
#include <QStackedLayout>
#include <QStackedWidget>
+#include <QToolBox>
namespace QInstaller {
@@ -66,19 +67,29 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
, m_updaterModel(m_core->updaterComponentModel())
, m_currentModel(m_allModel)
, m_allowCompressedRepositoryInstall(false)
+ , m_toolBox(nullptr)
+ , m_descriptionBaseWidget(nullptr)
, m_categoryWidget(Q_NULLPTR)
+ , m_categoryLayoutVisible(false)
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
- QVBoxLayout *descriptionVLayout = new QVBoxLayout;
+ m_descriptionBaseWidget = new QWidget(q);
+ m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
+
+ QVBoxLayout *descriptionVLayout = new QVBoxLayout(m_descriptionBaseWidget);
descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
+ descriptionVLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_toolBox = new QToolBox(q);
+ m_toolBox->setObjectName(QLatin1String("ToolBox"));
QScrollArea *descriptionScrollArea = new QScrollArea(q);
descriptionScrollArea->setWidgetResizable(true);
descriptionScrollArea->setFrameShape(QFrame::NoFrame);
descriptionScrollArea->setObjectName(QLatin1String("DescriptionScrollArea"));
- m_descriptionLabel = new QLabel(q);
+ m_descriptionLabel = new QLabel(m_descriptionBaseWidget);
m_descriptionLabel->setWordWrap(true);
m_descriptionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
m_descriptionLabel->setOpenExternalLinks(true);
@@ -87,8 +98,7 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
descriptionScrollArea->setWidget(m_descriptionLabel);
descriptionVLayout->addWidget(descriptionScrollArea);
- m_sizeLabel = new QLabel(q);
- m_sizeLabel->setMargin(5);
+ m_sizeLabel = new QLabel(m_descriptionBaseWidget);
m_sizeLabel->setWordWrap(true);
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
descriptionVLayout->addWidget(m_sizeLabel);
@@ -151,11 +161,11 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
QWidget *mainStackedWidget = new QWidget();
m_mainGLayout = new QGridLayout(mainStackedWidget);
- m_mainGLayout->addLayout(buttonHLayout, 0, 1);
- m_mainGLayout->addLayout(treeViewVLayout, 1, 1);
- m_mainGLayout->addLayout(descriptionVLayout, 1, 2);
- m_mainGLayout->setColumnStretch(1, 3);
- m_mainGLayout->setColumnStretch(2, 2);
+ m_mainGLayout->addLayout(buttonHLayout, 0, 0);
+ m_mainGLayout->addLayout(treeViewVLayout, 1, 0);
+ m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_mainGLayout->setColumnStretch(0, 3);
+ m_mainGLayout->setColumnStretch(1, 2);
m_stackedLayout = new QStackedLayout(q);
m_stackedLayout->addWidget(mainStackedWidget);
@@ -227,11 +237,11 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
vLayout->setContentsMargins(0, 0, 0, 0);
m_categoryWidget->setLayout(vLayout);
m_categoryGroupBox = new QGroupBox(q);
- m_categoryGroupBox->setTitle(m_core->settings().repositoryCategoryDisplayName());
m_categoryGroupBox->setObjectName(QLatin1String("CategoryGroupBox"));
QVBoxLayout *categoryLayout = new QVBoxLayout(m_categoryGroupBox);
QPushButton *fetchCategoryButton = new QPushButton(tr("Filter"));
fetchCategoryButton->setObjectName(QLatin1String("FetchCategoryButton"));
+ fetchCategoryButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
fetchCategoryButton->setToolTip(
ComponentSelectionPage::tr("Filter the enabled repository categories to selection."));
connect(fetchCategoryButton, &QPushButton::clicked, this,
@@ -249,16 +259,28 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
vLayout->addWidget(m_categoryGroupBox);
vLayout->addStretch();
- m_mainGLayout->addWidget(m_categoryWidget, 1, 0);
+ m_toolBox->insertItem(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
}
void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
{
+ if (!show && !m_categoryWidget)
+ return;
+
+ if (show == m_categoryLayoutVisible)
+ return;
+
+ setupCategoryLayout();
if (show) {
- setupCategoryLayout();
+ m_mainGLayout->removeWidget(m_descriptionBaseWidget);
+ m_toolBox->insertItem(0, m_descriptionBaseWidget, tr("Component Information"));
+ m_mainGLayout->addWidget(m_toolBox, 1, 1);
+ } else {
+ m_toolBox->removeItem(0);
+ m_mainGLayout->removeWidget(m_toolBox);
+ m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
}
- if (m_categoryWidget)
- m_categoryWidget->setVisible(show);
+ m_categoryLayoutVisible = show;
}
void ComponentSelectionPagePrivate::updateTreeView()
@@ -399,10 +421,9 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
{
updateWidgetVisibility(true);
- QCheckBox *checkbox;
QList<QCheckBox*> checkboxes = m_categoryGroupBox->findChildren<QCheckBox *>();
for (int i = 0; i < checkboxes.count(); i++) {
- checkbox = checkboxes.at(i);
+ QCheckBox *checkbox = checkboxes.at(i);
enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
}
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index 37f808286..d2d30e3f0 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -46,6 +46,7 @@ class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QStackedLayout;
+class QToolBox;
namespace QInstaller {
@@ -88,6 +89,8 @@ private:
ComponentSelectionPage *q;
PackageManagerCore *m_core;
QTreeView *m_treeView;
+ QToolBox *m_toolBox;
+ QWidget *m_descriptionBaseWidget;
QLabel *m_sizeLabel;
QLabel *m_descriptionLabel;
QPushButton *m_checkAll;
@@ -99,6 +102,7 @@ private:
QProgressBar *m_progressBar;
QGridLayout *m_mainGLayout;
bool m_allowCompressedRepositoryInstall;
+ bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
ComponentModel *m_updaterModel;
ComponentModel *m_currentModel;
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 42b14ce63..0e16d4c4b 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -67,6 +67,7 @@ static const QLatin1String scUncompressedSizeSum("UncompressedSizeSum");
static const QLatin1String scRequiresAdminRights("RequiresAdminRights");
static const QLatin1String scOfflineBinaryName("OfflineBinaryName");
static const QLatin1String scSHA1("SHA1");
+static const QLatin1String scContentSha1("ContentSha1");
// constants used throughout the components class
static const QLatin1String scVirtual("Virtual");
@@ -202,6 +203,8 @@ static const QLatin1String scCreateLocalRepositoryShort("cl");
static const QLatin1String scCreateLocalRepositoryLong("create-local-repository");
static const QLatin1String scNoDefaultInstallationShort("nd");
static const QLatin1String scNoDefaultInstallationLong("no-default-installations");
+static const QLatin1String scFilterPackagesShort("fp");
+static const QLatin1String scFilterPackagesLong("filter-packages");
// Developer options
static const QLatin1String scScriptShort("s");
diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp
index 9443b7f71..de1ddcc4f 100644
--- a/src/libs/installer/installiconsoperation.cpp
+++ b/src/libs/installer/installiconsoperation.cpp
@@ -134,8 +134,8 @@ bool InstallIconsOperation::performOperation()
if (status == PackageManagerCore::Canceled || status == PackageManagerCore::Failure)
return true;
- const QString &source = it.next();
- QString target = targetDir.absoluteFilePath(sourceDir.relativeFilePath(source));
+ const QString &source2 = it.next();
+ QString target = targetDir.absoluteFilePath(sourceDir.relativeFilePath(source2));
emit outputTextChanged(target);
@@ -185,7 +185,7 @@ bool InstallIconsOperation::performOperation()
}
// copy the file to its new location
- QFile cf(source);
+ QFile cf(source2);
if (!cf.copy(target)) {
setError(UserDefinedError);
setErrorString(tr("Failed to copy file \"%1\": %2").arg(
@@ -193,8 +193,8 @@ bool InstallIconsOperation::performOperation()
undoOperation();
return false;
}
- deleteFileNowOrLater(source);
- files.push_back(source);
+ deleteFileNowOrLater(source2);
+ files.push_back(source2);
files.push_back(target);
setValue(QLatin1String("files"), files);
} else if (fi.isDir() && !QDir(target).exists()) {
diff --git a/src/libs/installer/keepaliveobject.cpp b/src/libs/installer/keepaliveobject.cpp
index 94e91efd8..18ec9743a 100644
--- a/src/libs/installer/keepaliveobject.cpp
+++ b/src/libs/installer/keepaliveobject.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,7 +48,12 @@ KeepAliveObject::KeepAliveObject()
void KeepAliveObject::start()
{
+ if (m_timer)
+ delete m_timer;
m_timer = new QTimer(this);
+
+ if (m_socket)
+ delete m_socket;
m_socket = new QLocalSocket(this);
connect(m_timer, &QTimer::timeout, [this]() {
diff --git a/src/libs/installer/loggingutils.cpp b/src/libs/installer/loggingutils.cpp
index 0ebba10db..45f7aab1b 100644
--- a/src/libs/installer/loggingutils.cpp
+++ b/src/libs/installer/loggingutils.cpp
@@ -259,7 +259,7 @@ void LoggingHandler::printComponentInfo(const QList<Component *> components) con
QDomElement root = doc.createElement(QLatin1String("updates"));
doc.appendChild(root);
- foreach (Component *component, components) {
+ foreach (const Component *component, components) {
QDomElement update = doc.createElement(QLatin1String("update"));
update.setAttribute(QLatin1String("name"), component->value(scDisplayName));
update.setAttribute(QLatin1String("version"), component->value(scVersion));
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 7528a3188..26df043a0 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -428,6 +428,13 @@ using namespace QInstaller;
Emitted when installer binary marker \a magicMarker has changed.
*/
+/*!
+ \fn QInstaller::PackageManagerCore::componentsRecalculated()
+
+ Emitted when the component tree is recalculated. In a graphical interface,
+ this signal is emitted also after the categories are fetched.
+*/
+
Q_GLOBAL_STATIC(QMutex, globalModelMutex);
static QFont *sVirtualComponentsFont = nullptr;
Q_GLOBAL_STATIC(QMutex, globalVirtualComponentsFontMutex);
@@ -1549,9 +1556,8 @@ bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const L
continue;
const LocalPackage localPackage = installedPackages.value(name);
- const QString updateVersion = update->data(scVersion).toString();
- if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0)
- continue; // remote version equals or is less than the installed maintenance tool
+ if (!d->packageNeedsUpdate(localPackage, update))
+ continue;
const QDate updateDate = update->data(scReleaseDate).toDate();
if (localPackage.lastUpdateDate >= updateDate)
@@ -1577,6 +1583,7 @@ bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const L
if (success && !d->statusCanceledOrFailed())
d->setStatus(Success);
+ emit componentsRecalculated();
return success;
}
@@ -2172,26 +2179,50 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const
/*!
Lists available packages filtered with \a regexp without GUI. Virtual
- components are not listed unless set visible.
+ components are not listed unless set visible. Optionally, a \a filters
+ hash containing package information elements and regular expressions
+ can be used to further filter listed packages.
\sa setVirtualComponentsVisible()
*/
-void PackageManagerCore::listAvailablePackages(const QString &regexp)
+void PackageManagerCore::listAvailablePackages(const QString &regexp, const QHash<QString, QString> &filters)
{
+ setPackageViewer();
qCDebug(QInstaller::lcInstallerInstallLog)
<< "Searching packages with regular expression:" << regexp;
+
+ ComponentModel *model = defaultComponentModel();
d->fetchMetaInformationFromRepositories(DownloadType::UpdatesXML);
d->addUpdateResourcesFromRepositories(true);
QRegularExpression re(regexp);
const PackagesList &packages = d->remotePackages();
+ if (!fetchAllPackages(packages, LocalPackagesHash())) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "There was a problem with loading the package data.";
+ return;
+ }
PackagesList matchedPackages;
foreach (Package *package, packages) {
const QString name = package->data(scName).toString();
- if (re.match(name).hasMatch() &&
- (virtualComponentsVisible() ? true : !package->data(scVirtual, false).toBool())) {
- matchedPackages.append(package);
+ Component *component = componentByName(name);
+ if (!component)
+ continue;
+
+ const QModelIndex &idx = model->indexFromComponentName(component->treeName());
+ if (idx.isValid() && re.match(name).hasMatch()) {
+ bool ignoreComponent = false;
+ for (auto &key : filters.keys()) {
+ const QString elementValue = component->value(key);
+ QRegularExpression elementRegexp(filters.value(key));
+ if (elementValue.isEmpty() || !elementRegexp.match(elementValue).hasMatch()) {
+ ignoreComponent = true;
+ break;
+ }
+ }
+ if (!ignoreComponent)
+ matchedPackages.append(package);
}
}
if (matchedPackages.count() == 0)
@@ -2268,10 +2299,26 @@ bool PackageManagerCore::checkComponentsForInstallation(const QStringList &compo
model->setData(idx, Qt::Checked, Qt::CheckStateRole);
installComponentsFound = true;
}
- } else { // idx is invalid and component valid when we have invisible virtual component
- component->isVirtual()
- ? errorMessage.append(tr("Cannot install %1. Component is virtual.\n").arg(name))
- : errorMessage.append(tr("Cannot install %1. Component not found.\n").arg(name));
+ } else {
+ auto isDescendantOfVirtual = [&]() {
+ Component *trace = component;
+ forever {
+ trace = trace->parentComponent();
+ if (!trace) {
+ // We already checked the root component if there is no parent
+ return false;
+ } else if (trace->isVirtual()) {
+ errorMessage.append(tr("Cannot install %1. Component is descendant "
+ "of a virtual component %2.\n").arg(name, trace->name()));
+ return true;
+ }
+ }
+ };
+ // idx is invalid and component valid when we have invisible virtual component
+ if (component->isVirtual())
+ errorMessage.append(tr("Cannot install %1. Component is virtual.\n").arg(name));
+ else if (!isDescendantOfVirtual())
+ errorMessage.append(tr("Cannot install %1. Component not found.\n").arg(name));
}
}
if (!installComponentsFound)
@@ -2285,6 +2332,7 @@ bool PackageManagerCore::checkComponentsForInstallation(const QStringList &compo
*/
void PackageManagerCore::listInstalledPackages(const QString &regexp)
{
+ setPackageViewer();
LocalPackagesHash installedPackages = this->localInstalledPackages();
if (!regexp.isEmpty()) {
@@ -2322,6 +2370,7 @@ PackageManagerCore::Status PackageManagerCore::updateComponentsSilently(const QS
if (componentList.count() == 0) {
qCDebug(QInstaller::lcInstallerInstallLog) << "No updates available.";
+ setCanceled();
} else {
// Check if essential components are available (essential components are disabled).
// If essential components are found, update first essential updates,
@@ -3372,11 +3421,11 @@ bool PackageManagerCore::isPackageManager() const
}
/*!
- Sets current installer to be offline generator based on \a offlineGenerator.
+ Sets current installer to be offline generator.
*/
-void PackageManagerCore::setOfflineGenerator(bool offlineGenerator)
+void PackageManagerCore::setOfflineGenerator()
{
- d->m_offlineGenerator = offlineGenerator;
+ d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator;
}
/*!
@@ -3390,6 +3439,24 @@ bool PackageManagerCore::isOfflineGenerator() const
}
/*!
+ Sets the current installer as the package viewer.
+*/
+void PackageManagerCore::setPackageViewer()
+{
+ d->m_magicMarkerSupplement = BinaryContent::PackageViewer;
+}
+
+/*!
+ Returns \c true if the current installer is executed as package viewer.
+
+ \sa {installer::isPackageViewer}{installer.isPackageViewer}
+*/
+bool PackageManagerCore::isPackageViewer() const
+{
+ return d->isPackageViewer();
+}
+
+/*!
Sets the installer magic binary marker based on \a magicMarker and
userSetBinaryMarker to \c true.
*/
@@ -3630,7 +3697,7 @@ void PackageManagerCore::storeReplacedComponents(QHash<QString, Component *> &co
qCWarning(QInstaller::lcDeveloperBuild) << componentName << "- Does not exist in the repositories anymore.";
continue;
}
- if (!componentToReplace && !d->componentsToReplace().contains(componentName)) {
+ if (!d->componentsToReplace().contains(componentName)) {
componentToReplace = new Component(this);
componentToReplace->setValue(scName, componentName);
} else {
@@ -3769,10 +3836,8 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
continue; // Update for not installed package found, skip it.
const LocalPackage &localPackage = locals.value(name);
- const QString updateVersion = update->data(scVersion).toString();
- if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0)
+ if (!d->packageNeedsUpdate(localPackage, update))
continue;
-
// It is quite possible that we may have already installed the update. Lets check the last
// update date of the package and the release date of the update. This way we can compare and
// figure out if the update has been installed or not.
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index c845fb1fa..2c54122f5 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -33,6 +33,7 @@
#include "repository.h"
#include "qinstallerglobal.h"
#include "utils.h"
+#include "commandlineparser.h"
#include <QtCore/QHash>
#include <QtCore/QObject>
@@ -242,7 +243,8 @@ public:
ComponentModel *defaultComponentModel() const;
ComponentModel *updaterComponentModel() const;
void listInstalledPackages(const QString &regexp = QString());
- void listAvailablePackages(const QString &regexp);
+ void listAvailablePackages(const QString &regexp = QString(),
+ const QHash<QString, QString> &filters = QHash<QString, QString>());
PackageManagerCore::Status updateComponentsSilently(const QStringList &componentsToUpdate);
PackageManagerCore::Status installSelectedComponentsSilently(const QStringList& components);
PackageManagerCore::Status installDefaultComponentsSilently();
@@ -264,9 +266,12 @@ public:
Q_INVOKABLE void setPackageManager();
Q_INVOKABLE bool isPackageManager() const;
- void setOfflineGenerator(bool offlineGenerator = true);
+ void setOfflineGenerator();
Q_INVOKABLE bool isOfflineGenerator() const;
+ void setPackageViewer();
+ Q_INVOKABLE bool isPackageViewer() const;
+
void setUserSetBinaryMarker(qint64 magicMarker);
Q_INVOKABLE bool isUserSetBinaryMarker() const;
@@ -390,6 +395,7 @@ Q_SIGNALS:
void guiObjectChanged(QObject *gui);
void unstableComponentFound(const QString &type, const QString &errorMessage, const QString &component);
void installerBinaryMarkerChanged(qint64 magicMarker);
+ void componentsRecalculated();
private:
struct Data {
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index f4670c738..1b14c306b 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -225,6 +225,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(0) // initialize with pseudo marker
+ , m_magicMarkerSupplement(BinaryContent::Default)
, m_componentsToInstallCalculated(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
@@ -243,7 +244,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_autoAcceptLicenses(false)
, m_disableWriteMaintenanceTool(false)
, m_autoConfirmCommand(false)
- , m_offlineGenerator(false)
{
}
@@ -264,6 +264,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(magicInstallerMaker)
+ , m_magicMarkerSupplement(BinaryContent::Default)
, m_componentsToInstallCalculated(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
@@ -282,7 +283,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
, m_autoAcceptLicenses(false)
, m_disableWriteMaintenanceTool(false)
, m_autoConfirmCommand(false)
- , m_offlineGenerator(false)
{
foreach (const OperationBlob &operation, performedOperations) {
QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
@@ -717,7 +717,12 @@ bool PackageManagerCorePrivate::isPackageManager() const
bool PackageManagerCorePrivate::isOfflineGenerator() const
{
- return m_offlineGenerator;
+ return m_magicMarkerSupplement == BinaryContent::OfflineGenerator;
+}
+
+bool PackageManagerCorePrivate::isPackageViewer() const
+{
+ return m_magicMarkerSupplement == BinaryContent::PackageViewer;
}
bool PackageManagerCorePrivate::statusCanceledOrFailed() const
@@ -2246,7 +2251,8 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
component->value(scUncompressedSize).toULongLong(),
component->value(scInheritVersion),
component->isCheckable(),
- component->isExpandedByDefault());
+ component->isExpandedByDefault(),
+ component->value(scContentSha1));
m_localPackageHub->writeToDisk();
component->setInstalled();
@@ -2898,4 +2904,19 @@ bool PackageManagerCorePrivate::askUserConfirmCommand() const
}
}
+bool PackageManagerCorePrivate::packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const
+{
+ bool updateNeeded = true;
+ const QString contentSha1 = update->data(scContentSha1).toString();
+ if (!contentSha1.isEmpty()) {
+ if (contentSha1 == localPackage.contentSha1)
+ updateNeeded = false;
+ } else {
+ const QString updateVersion = update->data(scVersion).toString();
+ if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0)
+ updateNeeded = false;
+ }
+ return updateNeeded;
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index 858baf9eb..b29808228 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -132,6 +132,8 @@ public:
bool runOfflineGenerator();
bool isOfflineGenerator() const;
+ bool isPackageViewer() const;
+
QString replaceVariables(const QString &str) const;
QByteArray replaceVariables(const QByteArray &str) const;
@@ -254,6 +256,7 @@ private:
bool acceptLicenseAgreements() const;
bool askUserAcceptLicense(const QString &name, const QString &content) const;
bool askUserConfirmCommand() const;
+ bool packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const;
private:
PackageManagerCore *m_core;
@@ -264,9 +267,10 @@ private:
bool m_repoFetched;
bool m_updateSourcesAdded;
qint64 m_magicBinaryMarker;
+ int m_magicMarkerSupplement;
+
bool m_componentsToInstallCalculated;
- bool m_foundEssentialUpdate;
- bool m_offlineGenerator;
+ bool m_foundEssentialUpdate;;
mutable ScriptEngine *m_componentScriptEngine;
mutable ScriptEngine *m_controlScriptEngine;
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index 86c3a2d0c..d52034389 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -321,7 +321,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
QFile sheet(styleSheetFile);
if (sheet.exists()) {
if (sheet.open(QIODevice::ReadOnly)) {
- setStyleSheet(QString::fromLatin1(sheet.readAll()));
+ qApp->setStyleSheet(QString::fromLatin1(sheet.readAll()));
} else {
qCWarning(QInstaller::lcDeveloperBuild) << "The specified style sheet file "
"can not be opened.";
diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp
index 2fb6026cc..31b61ceeb 100644
--- a/src/libs/installer/performinstallationform.cpp
+++ b/src/libs/installer/performinstallationform.cpp
@@ -80,6 +80,7 @@ PerformInstallationForm::PerformInstallationForm(QObject *parent)
: QObject(parent)
, m_progressBar(nullptr)
, m_progressLabel(nullptr)
+ , m_downloadStatus(nullptr)
, m_productImagesScrollArea(nullptr)
, m_productImagesLabel(nullptr)
, m_detailsButton(nullptr)
diff --git a/src/libs/installer/qsettingswrapper.cpp b/src/libs/installer/qsettingswrapper.cpp
index 56db28d78..f57750bc1 100644
--- a/src/libs/installer/qsettingswrapper.cpp
+++ b/src/libs/installer/qsettingswrapper.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -74,9 +74,9 @@ public:
Private(const QString &fileName, QSettings::Format format)
: m_filename(fileName)
+ , m_format(format)
, settings(fileName, format)
{
- m_format = format;
m_scope = settings.scope();
m_application = settings.applicationName();
m_organization = settings.organizationName();
diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp
index 59be2171b..7506a13fe 100644
--- a/src/libs/installer/utils.cpp
+++ b/src/libs/installer/utils.cpp
@@ -351,12 +351,12 @@ static QString qt_create_commandline(const QString &program, const QStringList &
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
QString endQuote(QLatin1Char('\"'));
- int i = tmp.length();
- while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) {
- --i;
+ int j = tmp.length();
+ while (j > 0 && tmp.at(j - 1) == QLatin1Char('\\')) {
+ --j;
endQuote += QLatin1Char('\\');
}
- args += QLatin1String(" \"") + tmp.left(i) + endQuote;
+ args += QLatin1String(" \"") + tmp.left(j) + endQuote;
} else {
args += QLatin1Char(' ') + tmp;
}
diff --git a/src/libs/kdtools/filedownloader.cpp b/src/libs/kdtools/filedownloader.cpp
index 4cccfd9ca..a9f5040f0 100644
--- a/src/libs/kdtools/filedownloader.cpp
+++ b/src/libs/kdtools/filedownloader.cpp
@@ -192,6 +192,7 @@ struct KDUpdater::FileDownloader::Private
: m_hash(QCryptographicHash::Sha1)
, m_assumedSha1Sum("")
, autoRemove(true)
+ , followRedirect(false)
, m_speedTimerInterval(100)
, m_downloadDeadlineTimerInterval(30000)
, m_downloadPaused(false)
@@ -255,7 +256,6 @@ KDUpdater::FileDownloader::FileDownloader(const QString &scheme, QObject *parent
, d(new Private)
{
d->scheme = scheme;
- d->followRedirect = false;
}
/*!
diff --git a/src/libs/kdtools/localpackagehub.cpp b/src/libs/kdtools/localpackagehub.cpp
index 1a754d7d5..2ee880e04 100644
--- a/src/libs/kdtools/localpackagehub.cpp
+++ b/src/libs/kdtools/localpackagehub.cpp
@@ -327,7 +327,8 @@ void LocalPackageHub::addPackage(const QString &name,
quint64 uncompressedSize,
const QString &inheritVersionFrom,
bool checkable,
- bool expandedByDefault)
+ bool expandedByDefault,
+ const QString &contentSha1)
{
// TODO: This somewhat unexpected, remove?
if (d->m_packageInfoMap.contains(name)) {
@@ -350,6 +351,7 @@ void LocalPackageHub::addPackage(const QString &name,
info.uncompressedSize = uncompressedSize;
info.checkable = checkable;
info.expandedByDefault = expandedByDefault;
+ info.contentSha1 = contentSha1;
d->m_packageInfoMap.insert(name, info);
}
d->modified = true;
@@ -426,6 +428,8 @@ void LocalPackageHub::writeToDisk()
addTextChildHelper(&package, QLatin1String("Checkable"), QLatin1String("true"));
if (info.expandedByDefault)
addTextChildHelper(&package, QLatin1String("ExpandedByDefault"), QLatin1String("true"));
+ if (!info.contentSha1.isEmpty())
+ addTextChildHelper(&package, scContentSha1, info.contentSha1);
root.appendChild(package);
}
@@ -498,6 +502,8 @@ void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packag
info.checkable = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
else if (childNodeE.tagName() == QLatin1String("ExpandedByDefault"))
info.expandedByDefault = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
+ else if (childNodeE.tagName() == QLatin1String("ContentSha1"))
+ info.contentSha1 = childNodeE.text();
}
m_packageInfoMap.insert(info.name, info);
}
diff --git a/src/libs/kdtools/localpackagehub.h b/src/libs/kdtools/localpackagehub.h
index d43c4a6a5..648d6cf6e 100644
--- a/src/libs/kdtools/localpackagehub.h
+++ b/src/libs/kdtools/localpackagehub.h
@@ -55,6 +55,7 @@ struct KDTOOLS_EXPORT LocalPackage
quint64 uncompressedSize;
bool checkable;
bool expandedByDefault;
+ QString contentSha1;
};
class KDTOOLS_EXPORT LocalPackageHub
@@ -108,7 +109,8 @@ public:
quint64 uncompressedSize,
const QString &inheritVersionFrom,
bool checkable,
- bool expandedByDefault);
+ bool expandedByDefault,
+ const QString &contentSha1);
bool removePackage(const QString &pkgName);
void refresh();
diff --git a/src/libs/kdtools/selfrestarter.cpp b/src/libs/kdtools/selfrestarter.cpp
index e94d0fea7..7771fafac 100644
--- a/src/libs/kdtools/selfrestarter.cpp
+++ b/src/libs/kdtools/selfrestarter.cpp
@@ -36,19 +36,21 @@ class SelfRestarter::Private
{
public:
Private(int argc, char *argv[])
- : restartOnQuit(false)
+ : executable(QString::fromLocal8Bit(argv[0]))
+ , restartOnQuit(false)
+ , workingPath(QDir::currentPath())
{
- executable = QString::fromLocal8Bit(argv[0]);
- workingPath = QDir::currentPath();
+
for (int i = 1; i < argc; ++i)
args << QString::fromLocal8Bit(argv[i]);
}
Private()
+ : executable(qApp->applicationFilePath())
+ , args(qApp->arguments().mid(1))
+ , restartOnQuit(false)
+ , workingPath(QDir::currentPath())
{
- executable = qApp->applicationFilePath();
- workingPath = QDir::currentPath();
- args = qApp->arguments().mid(1);
}
~Private()
diff --git a/src/libs/kdtools/updatefinder.cpp b/src/libs/kdtools/updatefinder.cpp
index 535dfde3d..034e162d3 100644
--- a/src/libs/kdtools/updatefinder.cpp
+++ b/src/libs/kdtools/updatefinder.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -79,8 +79,9 @@ public:
RemoveExisting
};
- Private(UpdateFinder *qq)
+ explicit Private(UpdateFinder *qq)
: q(qq)
+ , cancel(false)
, downloadCompleteCount(0)
, m_downloadsToComplete(0)
{}
@@ -93,7 +94,7 @@ public:
struct Data {
Data()
: downloader(0) {}
- Data(const PackageSource &i, FileDownloader *d = 0)
+ explicit Data(const PackageSource &i, FileDownloader *d = 0)
: info(i), downloader(d) {}
PackageSource info;
diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp
index 00b059af1..897fecf1b 100644
--- a/src/libs/kdtools/updateoperation.cpp
+++ b/src/libs/kdtools/updateoperation.cpp
@@ -283,7 +283,7 @@ void UpdateOperation::setRequiresUnreplacedVariables(bool isRequired)
struct StartsWith
{
- StartsWith(const QString &searchTerm)
+ explicit StartsWith(const QString &searchTerm)
: m_searchTerm(searchTerm) {}
bool operator()(const QString &searchString)
diff --git a/src/libs/kdtools/updatesinfo.cpp b/src/libs/kdtools/updatesinfo.cpp
index 3119b6240..eaa9b039e 100644
--- a/src/libs/kdtools/updatesinfo.cpp
+++ b/src/libs/kdtools/updatesinfo.cpp
@@ -125,8 +125,8 @@ bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
} else if (childE.tagName() == QLatin1String("Licenses")) {
QHash<QString, QVariant> licenseHash;
const QDomNodeList licenseNodes = childE.childNodes();
- for (int i = 0; i < licenseNodes.count(); ++i) {
- const QDomNode licenseNode = licenseNodes.at(i);
+ for (int index = 0; index < licenseNodes.count(); ++index) {
+ const QDomNode licenseNode = licenseNodes.at(index);
if (licenseNode.nodeName() == QLatin1String("License")) {
QDomElement element = licenseNode.toElement();
QVariantMap attributes;
@@ -210,8 +210,8 @@ QVariant UpdatesInfoData::parseOperations(const QDomNodeList &operationNodes)
if (operationNode.nodeName() == QLatin1String("Operation")) {
const QDomNodeList argumentNodes = operationNode.childNodes();
QStringList attributes;
- for (int i = 0; i < argumentNodes.count(); ++i) {
- const QDomNode argumentNode = argumentNodes.at(i);
+ for (int index = 0; index < argumentNodes.count(); ++index) {
+ const QDomNode argumentNode = argumentNodes.at(index);
if (argumentNode.nodeName() == QLatin1String("Argument")) {
QDomElement argumentElement = argumentNode.toElement();
attributes.append(argumentElement.text());
diff --git a/src/sdk/commandlineinterface.cpp b/src/sdk/commandlineinterface.cpp
index 69aa36c43..a6cd22151 100644
--- a/src/sdk/commandlineinterface.cpp
+++ b/src/sdk/commandlineinterface.cpp
@@ -132,7 +132,7 @@ int CommandLineInterface::searchAvailablePackages()
QString regexp;
if (!m_positionalArguments.isEmpty())
regexp = m_positionalArguments.first();
- m_core->listAvailablePackages(regexp);
+ m_core->listAvailablePackages(regexp, parsePackageFilters());
return EXIT_SUCCESS;
}
@@ -271,3 +271,27 @@ bool CommandLineInterface::setTargetDir()
}
return false;
}
+
+QHash<QString, QString> CommandLineInterface::parsePackageFilters()
+{
+ QHash<QString, QString> filterHash;
+ if (m_parser.isSet(CommandLineOptions::scFilterPackagesLong)) {
+ const QStringList filterList = m_parser.value(CommandLineOptions::scFilterPackagesLong)
+ .split(QLatin1Char(','));
+
+ for (auto &filter : filterList) {
+ const int i = filter.indexOf(QLatin1Char('='));
+ const QString element = filter.left(i).trimmed();
+ const QString value = filter.mid(i + 1).trimmed();
+
+ if ((i == -1) || (filter.count(QLatin1Char('=') > 1))
+ || element.isEmpty() || value.isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Ignoring unknown entry "
+ << filter << "in package filter arguments. Please use syntax \"element=regex,...\".";
+ continue;
+ }
+ filterHash.insert(element, value);
+ }
+ }
+ return filterHash;
+}
diff --git a/src/sdk/commandlineinterface.h b/src/sdk/commandlineinterface.h
index 2627bc6f8..29bae74a4 100644
--- a/src/sdk/commandlineinterface.h
+++ b/src/sdk/commandlineinterface.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -54,6 +54,7 @@ private:
bool initialize();
bool checkLicense();
bool setTargetDir();
+ QHash<QString, QString> parsePackageFilters();
QStringList m_positionalArguments;
};
diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp
index 5e2f1ed13..3bcb3f11a 100644
--- a/src/sdk/tabcontroller.cpp
+++ b/src/sdk/tabcontroller.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -166,23 +166,6 @@ void TabController::restartWizard()
void TabController::onSettingsButtonClicked()
{
SettingsDialog dialog(d->m_core);
- // set custom stylesheet
- const QString styleSheetFile = d->m_core->settings().styleSheet();
- if (!styleSheetFile.isEmpty()) {
- QFile sheet(styleSheetFile);
- if (sheet.exists()) {
- if (sheet.open(QIODevice::ReadOnly)) {
- dialog.setStyleSheet(QString::fromLatin1(sheet.readAll()));
- } else {
- qCWarning(QInstaller::lcDeveloperBuild) << "The specified style sheet file "
- "can not be opened.";
- }
- } else {
- qCWarning(QInstaller::lcDeveloperBuild) << "A style sheet file is specified, "
- "but it does not exist.";
- }
- }
-
connect(&dialog, &SettingsDialog::networkSettingsChanged,
this, &TabController::onNetworkSettingsChanged);
dialog.exec();
diff --git a/src/sdk/translations/ifw_ar.ts b/src/sdk/translations/ifw_ar.ts
new file mode 100644
index 000000000..df8b6ffc2
--- /dev/null
+++ b/src/sdk/translations/ifw_ar.ts
@@ -0,0 +1,2740 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ar">
+<context>
+ <name>QInstaller::ProxyCredentialsDialog</name>
+ <message>
+ <source>Dialog</source>
+ <translation>نافذة الحوار</translation>
+ </message>
+ <message>
+ <source>The proxy %1 requires a username and password.</source>
+ <translation>الوكيل %1 بتطلب اسم مستخدم وكلمة مرور.</translation>
+ </message>
+ <message>
+ <source>Username:</source>
+ <translation>اسم المستخدم:</translation>
+ </message>
+ <message>
+ <source>Username</source>
+ <translation>اسم المستخدم</translation>
+ </message>
+ <message>
+ <source>Password:</source>
+ <translation>كلمة المرور:</translation>
+ </message>
+ <message>
+ <source>Password</source>
+ <translation>كلمة المرور</translation>
+ </message>
+ <message>
+ <source>Proxy Credentials</source>
+ <translation>اعتماديات الوكيل</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ServerAuthenticationDialog</name>
+ <message>
+ <source>Server Requires Authentication</source>
+ <translation>يحتاج الخادم إلى مصادقة</translation>
+ </message>
+ <message>
+ <source>You need to supply a username and password to access this site.</source>
+ <translation>يجب أن تدخل اسم مستخدم وكلمة مرور للوصول إلى هذا الموقع.</translation>
+ </message>
+ <message>
+ <source>Username:</source>
+ <translation>اسم المستخدم:</translation>
+ </message>
+ <message>
+ <source>Password:</source>
+ <translation>كلمة المرور:</translation>
+ </message>
+ <message>
+ <source>%1 at %2</source>
+ <translation>%1 في %2</translation>
+ </message>
+</context>
+<context>
+ <name>Dialog</name>
+ <message>
+ <source>Http authentication required</source>
+ <translation>مصادقة Http مطلوبة</translation>
+ </message>
+ <message>
+ <source>You need to supply a Username and Password to access this site.</source>
+ <translation>تحتاج إلى اسم مستخدم وكلمة مرور للوصول إلى هذا الموقع.</translation>
+ </message>
+ <message>
+ <source>Username:</source>
+ <translation>اسم المستخدم:</translation>
+ </message>
+ <message>
+ <source>Password:</source>
+ <translation>كلمة المرور:</translation>
+ </message>
+ <message>
+ <source>%1 at %2</source>
+ <translation>%1 في %2</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <source>Settings</source>
+ <translation>الإعدادات</translation>
+ </message>
+ <message>
+ <source>Network</source>
+ <translation>الشبكة</translation>
+ </message>
+ <message>
+ <source>No proxy</source>
+ <translation>لا وكيل</translation>
+ </message>
+ <message>
+ <source>System proxy settings</source>
+ <translation>إعدادات وكيل النظام</translation>
+ </message>
+ <message>
+ <source>Manual proxy configuration</source>
+ <translation>إعدادات الوكيل اليدوية</translation>
+ </message>
+ <message>
+ <source>HTTP proxy:</source>
+ <translation>وكيل HTTP:</translation>
+ </message>
+ <message>
+ <source>Port:</source>
+ <translation>المنفذ:</translation>
+ </message>
+ <message>
+ <source>FTP proxy:</source>
+ <translation>وكيل FTP:</translation>
+ </message>
+ <message>
+ <source>Repositories</source>
+ <translation>المستودعات</translation>
+ </message>
+ <message>
+ <source>Add Username and Password for authentication if needed.</source>
+ <translation>أضف اسم المستخدم وكلمة المرور للمصادقة إذا لزم الأمر.</translation>
+ </message>
+ <message>
+ <source>Use temporary repositories only</source>
+ <translation>استخدم المستودعات المؤقتة فقط</translation>
+ </message>
+ <message>
+ <source>Add</source>
+ <translation>أضف</translation>
+ </message>
+ <message>
+ <source>Remove</source>
+ <translation>احذف</translation>
+ </message>
+ <message>
+ <source>Test</source>
+ <translation>اختبر</translation>
+ </message>
+ <message>
+ <source>Select All</source>
+ <translation>حدّد الكل</translation>
+ </message>
+ <message>
+ <source>Deselect All</source>
+ <translation>ألغِ تحديد الكل</translation>
+ </message>
+ <message>
+ <source>Show Passwords</source>
+ <translation>أظهر كلمات المرور</translation>
+ </message>
+ <message>
+ <source>Check this to use repository during fetch.</source>
+ <translation>حدّد هذا لاستخدام المستودع أثناء جلب البيانات.</translation>
+ </message>
+ <message>
+ <source>Add the username to authenticate on the server.</source>
+ <translation>أضف اسم المستخدم للمصادقة على الخادم.</translation>
+ </message>
+ <message>
+ <source>Add the password to authenticate on the server.</source>
+ <translation>أضف اسم كلمة المرور للمصادقة على الخادم.</translation>
+ </message>
+ <message>
+ <source>The servers URL that contains a valid repository.</source>
+ <translation>رابط الخادم المحتوي على مستوع صالح.</translation>
+ </message>
+ <message>
+ <source>An error occurred while testing this repository.</source>
+ <translation>حدث خطأ أثناء اختبار هذا المستودع.</translation>
+ </message>
+ <message>
+ <source>The repository was tested successfully.</source>
+ <translation>اختُبر المستودع بنجاح.</translation>
+ </message>
+ <message>
+ <source>Do you want to disable the repository?</source>
+ <translation>هل تريط تعطيل هذا المستودع؟</translation>
+ </message>
+ <message>
+ <source>Do you want to enable the repository?</source>
+ <translation>هل تريد تمكين هذا المستودع؟</translation>
+ </message>
+ <message>
+ <source>Hide Passwords</source>
+ <translation>أخف كلمات المرور</translation>
+ </message>
+ <message>
+ <source>Use</source>
+ <translation>استخدم</translation>
+ </message>
+ <message>
+ <source>Username</source>
+ <translation>اسم المستخدم</translation>
+ </message>
+ <message>
+ <source>Password</source>
+ <translation>كلمة المرور</translation>
+ </message>
+ <message>
+ <source>Repository</source>
+ <translation>المستودع</translation>
+ </message>
+ <message>
+ <source>Default repositories</source>
+ <translation>المستودعات الافتراضية</translation>
+ </message>
+ <message>
+ <source>Temporary repositories</source>
+ <translation>المستودعات المؤقتة</translation>
+ </message>
+ <message>
+ <source>User defined repositories</source>
+ <translation>المستودعات المعرفة من قبل المستخدم</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <source>Authorization required</source>
+ <translation>المصادقة مطلوبة</translation>
+ </message>
+ <message>
+ <source>Enter your password to authorize for sudo:</source>
+ <translation>أدخل كلمة مرورك لمصادقة sudo:</translation>
+ </message>
+ <message>
+ <source>Error acquiring admin rights</source>
+ <translation>فشل الحصول على صلاحيات المدير</translation>
+ </message>
+ <message>
+ <source>Another %1 instance is already running. Wait until it finishes, close it, or restart your system.</source>
+ <translation type="unfinished">نسخة أخرى من %1 تعمل بالفعل. انتظر حتى تنتهي أو أغلقها أو أعد تشغيل نظامك.</translation>
+ </message>
+ <message>
+ <source>Please make sure that the current user has reading access to file &quot;%1&quot; or try running %2 as an administrator.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot start installer binary as updater.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot start installer binary as package manager.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot start installer binary as uninstaller.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty repository list for option &apos;addRepository&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty repository list for option &apos;addTempRepository&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty repository list for option &apos;setTempRepository&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty repository list for option &apos;installCompressedRepository&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The file %1 does not exist.</source>
+ <translation type="unfinished">الملف %1 ليس موجوداً.</translation>
+ </message>
+ <message>
+ <source>Arguments missing for option %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invalid button value %1 </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Incorrect arguments for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller</name>
+ <message>
+ <source>No marker found, stopped after %1.</source>
+ <translation>لم يُعثر على أي علامات، تم التوقف بعد %1.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Read failed after %1 bytes: %2</source>
+ <translation>فشلت القراءة بعد %1 بايت: %2</translation>
+ </message>
+ <message>
+ <source>Copy failed: %1</source>
+ <translation>فشل النسخ: %1</translation>
+ </message>
+ <message>
+ <source>Write failed after %1 bytes: %2</source>
+ <translation>فشل الكتابة بعد %1 بايت: %2</translation>
+ </message>
+ <message>
+ <source>bytes</source>
+ <translation>بايت</translation>
+ </message>
+ <message>
+ <source>KB</source>
+ <translation>كيلو بايت</translation>
+ </message>
+ <message>
+ <source>MB</source>
+ <translation>ميجا بايت</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>جيجا بايت</translation>
+ </message>
+ <message>
+ <source>TB</source>
+ <translation>تيرا بايت</translation>
+ </message>
+ <message>
+ <source>PB</source>
+ <translation>بيتا بايت</translation>
+ </message>
+ <message>
+ <source>EB</source>
+ <translation>إكسا بايت</translation>
+ </message>
+ <message>
+ <source>ZB</source>
+ <translation>زيتا بايت</translation>
+ </message>
+ <message>
+ <source>YB</source>
+ <translation>يوتا بايت</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف المجلد &quot;%1&quot; :%2</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;.</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot copy file from &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot move file from &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;: %2</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot open temporary file: %1</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot open temporary file for template %1: %2</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation type="unfinished">لا يمكن نسخ الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The specified module could not be found.</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Invalid content in &quot;%1&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>BinaryLayout</name>
+ <message>
+ <source>Cannot seek to %1 to read the embedded meta data count.</source>
+ <translation>لا يمكن الوصول إلى %1 لقراءة عدد بيانات التعريف المضمنة.</translation>
+ </message>
+ <message>
+ <source>Cannot seek to %1 to read the resource collection segment.</source>
+ <translation>لا يمكن الوصول إلى %1 لقراءة مقطع تجميع الموارد.</translation>
+ </message>
+ <message>
+ <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source>
+ <translation>عدم تطابق غير متوقع لمصادر التعريف. بينما المتوقع: %2، قُرأ %1.</translation>
+ </message>
+</context>
+<context>
+ <name>BinaryContent</name>
+ <message>
+ <source>Cannot seek to %1 to read the operation data.</source>
+ <translation>لا يمكن الوصول إلى %1 لقراءة بيانات العملية.</translation>
+ </message>
+ <message>
+ <source>Cannot seek to %1 to read the resource collection block.</source>
+ <translation>لا يمكن الوصول إلى %1 لقراءة كتلة مجموعة الموارد.</translation>
+ </message>
+ <message>
+ <source>Cannot open meta resource %1.</source>
+ <translation>لا يمكن فتح تعريف المصدر %1.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::Resource</name>
+ <message>
+ <source>Cannot open resource %1 for reading.</source>
+ <translation>لا يمكن فتح المصدر %1 للقراءة.</translation>
+ </message>
+ <message>
+ <source>Read failed after %1 bytes: %2</source>
+ <translation>فشلت القراءة بعد %1 بايت: %2</translation>
+ </message>
+ <message>
+ <source>Write failed after %1 bytes: %2</source>
+ <translation>فشلت الكتابة بعد %1 بايت: %2</translation>
+ </message>
+</context>
+<context>
+ <name>ResourceCollectionManager</name>
+ <message>
+ <source>Cannot open resource %1: %2</source>
+ <translation>لا يمكن فتح المصدر %1: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::Component</name>
+ <message>
+ <source>Components cannot have children in updater mode.</source>
+ <translation>لا يمكن للمكونات أن تحتوي على أطفال في وضع التحديث.</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن فتح ملف واجهة المستخدم المطلوب &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن تحميل ملف واجهة المتسخدم المطلوب &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن فتح ملف الرخصة المطلوب &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>خطأ</translation>
+ </message>
+ <message>
+ <source>Error: Operation %1 does not exist.</source>
+ <translation>خطأ: العملية %1 ليست موجودة.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve isDefault in %1</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>There was an error loading the selected component. This component can not be installed.</source>
+ <translation>كان هناك خطأ أثناء تحميل المكون المحدد. هذا المكون لا يمكن تثبيته.</translation>
+ </message>
+ <message>
+ <source>Update Info: </source>
+ <translation>معلومات التحديث: </translation>
+ </message>
+ <message>
+ <source>There was an error loading the selected component. This component can not be updated.</source>
+ <translation>كان هناك خطأ أثناء تحميل المكون المحدد. هذا المكون لا يمكن تحديثه.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ComponentModel</name>
+ <message>
+ <source>Component is marked for installation.</source>
+ <translation>المكون مُعَلّم للتثبيت.</translation>
+ </message>
+ <message>
+ <source>Component is marked for uninstallation.</source>
+ <translation>المكون مُعَلّم لإزالة التثبيت.</translation>
+ </message>
+ <message>
+ <source>Component is installed.</source>
+ <translation>المكون مُثبّت.</translation>
+ </message>
+ <message>
+ <source>Component is not installed.</source>
+ <translation>المكون غير مثبت.</translation>
+ </message>
+ <message>
+ <source>Component Name</source>
+ <translation>اسم المكون</translation>
+ </message>
+ <message>
+ <source>Action</source>
+ <translation>الإجراء</translation>
+ </message>
+ <message>
+ <source>Installed Version</source>
+ <translation>الإصدار المثبت</translation>
+ </message>
+ <message>
+ <source>New Version</source>
+ <translation>الإصدار الجديد</translation>
+ </message>
+ <message>
+ <source>Release Date</source>
+ <translation>تاريخ الإصدار</translation>
+ </message>
+ <message>
+ <source>Size</source>
+ <translation>الحجم</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ComponentSelectionPage</name>
+ <message>
+ <source>Alt+A</source>
+ <comment>select default components</comment>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Def&amp;ault</source>
+ <translation>الاف&amp;تراضي</translation>
+ </message>
+ <message>
+ <source>Select default components in the tree view.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alt+R</source>
+ <comment>reset to already installed components</comment>
+ <translation>Alt+R</translation>
+ </message>
+ <message>
+ <source>&amp;Reset</source>
+ <translation>&amp;أعد الضبط</translation>
+ </message>
+ <message>
+ <source>Reset all components to their original selection state in the tree view.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alt+S</source>
+ <comment>select all components</comment>
+ <translation>Alt+S</translation>
+ </message>
+ <message>
+ <source>&amp;Select All</source>
+ <translation>&amp;حدّد الكل</translation>
+ </message>
+ <message>
+ <source>Select all components in the tree view.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alt+D</source>
+ <comment>deselect all components</comment>
+ <translation>Alt+D</translation>
+ </message>
+ <message>
+ <source>&amp;Deselect All</source>
+ <translation>&amp;ألغِ تحديد الكل</translation>
+ </message>
+ <message>
+ <source>Deselect all components in the tree view.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&amp;Browse QBSP files</source>
+ <translation>&amp;تصفح ملفات QBSP</translation>
+ </message>
+ <message>
+ <source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter the enabled repository categories to selection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This component will occupy approximately %1 on your hard disk drive.</source>
+ <translation>سيشغل هذا المكون %1 تقريباً على قرصك الصلب.</translation>
+ </message>
+ <message>
+ <source>Open File</source>
+ <translation>فتح ملف</translation>
+ </message>
+ <message>
+ <source>Select Components</source>
+ <translation>حدّد المكونات</translation>
+ </message>
+ <message>
+ <source>Please select the components you want to update.</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Please select the components you want to install.</source>
+ <translation>يرجى تحديد المكونات التي تريد تثبيتها.</translation>
+ </message>
+ <message>
+ <source>Please select the components you want to uninstall.</source>
+ <translation>يرجى تحديد المكونات التي تريد إزالتها.</translation>
+ </message>
+ <message>
+ <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
+ <translation>حدّد المكونات التي تريد تثبيتها. ألغِ تحديد المكونات المثبتة لإزالتها. أي مكونات مثبتة بالفعل لن يتم تحديثها.</translation>
+ </message>
+ <message>
+ <source>Mandatory components need to be updated first before you can select other components to update.</source>
+ <translation>المكونات الضرورية التي يجب تحديثها أولاً قبل اختيار المكونات الآخرى للتحديث.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ComponentSelectionPagePrivate</name>
+ <message>
+ <source>Filter</source>
+ <translation>رشّح</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>خطأ</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ConsumeOutputOperation</name>
+ <message>
+ <source>&lt;to be saved installer key name&gt; &lt;executable&gt; [argument1] [argument2] [...]</source>
+ <translation>&lt;اسم مفتاح المثبت المراد حفظه&gt; &lt;الملف التنفيذي&gt; [المعامل الأول] [العامل الثاني] [...]</translation>
+ </message>
+ <message>
+ <source>Needed installer object in %1 operation is empty.</source>
+ <translation>كائن المثبت المطلوب في العملية %1 فارغ.</translation>
+ </message>
+ <message>
+ <source>Cannot save the output of &quot;%1&quot; to an empty installer key value.</source>
+ <translation>لا يمكن حفظ إخراج &quot;%1&quot; إلى قيمة مفتاح تثبيت فارغة.</translation>
+ </message>
+ <message>
+ <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
+ <translation>الملف &quot;%1&quot; غير موجود أو ليس قابلاً للتنفيذ.</translation>
+ </message>
+ <message>
+ <source>Running &quot;%1&quot; resulted in a crash.</source>
+ <translation>تشغيل &quot;%1&quot; تسبب في إنهيار.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::CopyDirectoryOperation</name>
+ <message>
+ <source>&lt;source&gt; &lt;target&gt; [&quot;forceOverwrite&quot;]</source>
+ <translation>&lt;المصدر&gt; &lt;الوجهة&gt; [&quot;إجبار إعادة الكتابة&quot;]</translation>
+ </message>
+ <message>
+ <source>Invalid argument in %1: Third argument needs to be forceOverwrite, if specified.</source>
+ <translation>معامل غير صحيح في %1: المعامل الثالث يجب أن يكون &apos;forceOverwrite&apos;، إن تم تحديد ذلك.</translation>
+ </message>
+ <message>
+ <source>Invalid argument in %1: Directory &quot;%2&quot; is invalid.</source>
+ <translation>معامل غير صحيح في %1: المجلد &quot;%2&quot; غير صحيح.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;.</source>
+ <translation>لا يمكن إنشاء المجلد &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Failed to overwrite &quot;%1&quot;.</source>
+ <translation>فشلت إعادة كتابة &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation>لا يمكن نسخ الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;.</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::CopyFileTask</name>
+ <message>
+ <source>Invalid task item count.</source>
+ <translation>عدد عناصر المهمة غير صحيح.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Writing to file &quot;%1&quot; failed: %2</source>
+ <translation>فشلت الكتابة إلى الملف &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::CreateDesktopEntryOperation</name>
+ <message>
+ <source>Cannot backup file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Failed to overwrite file &quot;%1&quot;.</source>
+ <translation>فشلت إعادة كتابة الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot write desktop entry to &quot;%1&quot;.</source>
+ <translation>لا يمكن كتابة ملف سطح المكتب إلى &quot;%1&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::CreateLinkOperation</name>
+ <message>
+ <source>Cannot create link from &quot;%1&quot; to &quot;%2&quot;.</source>
+ <translation>لا يمكن إنشاء رابط من &quot;%1&quot; إلى &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot remove link from &quot;%1&quot; to &quot;%2&quot;.</source>
+ <translation>لا يمكن حذف الرابط بين &quot;%1&quot; و&quot;%2&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::CreateLocalRepositoryOperation</name>
+ <message>
+ <source>Cannot set permissions for file &quot;%1&quot;.</source>
+ <translation>لا يمكن تعيين أذونات للملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;:%2</translation>
+ </message>
+ <message>
+ <source>Cannot move file &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation>لا يمكن نقل الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Installer at &quot;%1&quot; needs to be an offline one.</source>
+ <translation>يجب أن يكون المثبت في &quot;%1&quot; غير متصل بالإنترنت.</translation>
+ </message>
+ <message>
+ <source>Cannot create path &quot;%1&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove directory &quot;%1&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading.</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة.</translation>
+ </message>
+ <message>
+ <source>Cannot read file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن قراءة الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot create target directory: &quot;%1&quot;.</source>
+ <translation>لا يمكن إنشاء مجلد الوجهة: &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Unknown exception caught: %1.</source>
+ <translation>اكتُشف استثناء غير معروف: %1.</translation>
+ </message>
+ <message>
+ <source>Removing file &quot;%1&quot;.</source>
+ <translation>إزالة الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;.</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot remove directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف المجلد &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::CreateShortcutOperation</name>
+ <message>
+ <source>&lt;target&gt; &lt;link location&gt; [target arguments] [&quot;workingDirectory=...&quot;] [&quot;iconPath=...&quot;] [&quot;iconId=...&quot;] [&quot;description=...&quot;]</source>
+ <translation>&lt;الوجهة&gt; &lt;مكان الرابط&gt; [معاملات الوجهة] [&quot;مسار العمل&quot;] [&quot;مسار الأيقونة&quot;] [&quot;رمز الأيقونة&quot;] [&quot;الوصف&quot;]</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء المجلد &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Failed to overwrite &quot;%1&quot;: %2</source>
+ <translation>فشلت إعادة كتابة &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot create link &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء الارتباط &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::DownloadArchivesJob</name>
+ <message>
+ <source>Canceled</source>
+ <translation>أُلغيت</translation>
+ </message>
+ <message>
+ <source>Downloading hash signature failed.</source>
+ <translation>فشل تنزيل توقيع التجزئة.</translation>
+ </message>
+ <message>
+ <source>Download Error</source>
+ <translation>خطأ في التنزيل</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
+ <translation>فشل التحقق من التجزئة أثناء التنزيل. هذا خطأ مؤقت، يرجى إعادة المحاولة.</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash</source>
+ <translation>لا يمكن التحقق من التحزئة</translation>
+ </message>
+ <message>
+ <source>Cannot download archive %1: %2</source>
+ <translation>لا يمكن تنزيل الأرشيف %1: %2</translation>
+ </message>
+ <message>
+ <source>Cannot fetch archives: %1
+Error while loading %2</source>
+ <translation>لا يمكن جلب الأرشيفات: %1
+خطأ أثناء تحميل %2</translation>
+ </message>
+ <message>
+ <source>Downloading archive &quot;%1&quot; for component %2.</source>
+ <translation>تنزيل الأرشيف &quot;%1&quot; للمكون %2.</translation>
+ </message>
+ <message>
+ <source>Scheme %1 not supported (URL: %2).</source>
+ <translation>الصيغة %1 غير مدعومة (الرابط: %2).</translation>
+ </message>
+ <message>
+ <source>Cannot find component for %1.</source>
+ <translation>لا يمكن العثور على المكون لـ %1.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::Downloader</name>
+ <message>
+ <source>Target file &quot;%1&quot; already exists but is not a file.</source>
+ <translation>ملف الوجهة &quot;%1&quot; موجود بالفعل ولكنه ليس ملفاً.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <extracomment>%2 is a sentence describing the error</extracomment>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>File &quot;%1&quot; not open for writing: %2</source>
+ <extracomment>%2 is a sentence describing the error.</extracomment>
+ <translation>الملف &quot;%1&quot; غير مفتوح للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Writing to file &quot;%1&quot; failed: %2</source>
+ <extracomment>%2 is a sentence describing the error.</extracomment>
+ <translation>فشلت الكتابة إلى الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Redirect loop detected for &quot;%1&quot;.</source>
+ <translation>اكتُشفت حلقة إعادة توجيه لـ &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Network error while downloading &apos;%1&apos;: %2.</source>
+ <translation>خطأ في الشبكة أثناء تنزيل &quot;%1&quot;: %2.</translation>
+ </message>
+ <message>
+ <source>Unknown network error while downloading &quot;%1&quot;.</source>
+ <extracomment>%1 is a sentence describing the error</extracomment>
+ <translation>خطأ غير معروف في الشبكة أثناء تنزيل &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Network transfers canceled.</source>
+ <translation>أُلغيت تحويلات الشبكة.</translation>
+ </message>
+ <message>
+ <source>Pause and resume not supported by network transfers.</source>
+ <translation>التوقف المؤقت والاستئناف غير مدعوم من قبل ناقلات الشبكة.</translation>
+ </message>
+ <message>
+ <source>Invalid source URL &quot;%1&quot;: %2</source>
+ <extracomment>%2 is a sentence describing the error</extracomment>
+ <translation>الرابط المصدري &quot;%1&quot; غير صحيح: %2</translation>
+ </message>
+</context>
+<context>
+ <name>AuthenticationRequiredException</name>
+ <message>
+ <source>%1 at %2</source>
+ <translation>%1 في %2</translation>
+ </message>
+ <message>
+ <source>Proxy requires authentication.</source>
+ <translation>يتطلب الوكيل المصادقة.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ElevatedExecuteOperation</name>
+ <message>
+ <source>Cannot start detached: &quot;%1&quot;</source>
+ <translation>لا يمكن بدأ &quot;%1&quot; منفصلاً</translation>
+ </message>
+ <message>
+ <source>Cannot start: &quot;%1&quot;: %2</source>
+ <translation>لا يمكن بدأ &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Program crashed: &quot;%1&quot;</source>
+ <translation>انهار البرنامج: &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Execution failed (Unexpected exit code: %1): &quot;%2&quot;</source>
+ <translation>فشل التنفيذ (رمز الخروج %1 غير متوقع): &quot;%2&quot;</translation>
+ </message>
+</context>
+<context>
+ <name>UpdateOperation</name>
+ <message>
+ <source>Cannot write to registry path %1.</source>
+ <translation>لا يمكن الكتابة إلى مسار التسجيل %1.</translation>
+ </message>
+ <message>
+ <source>Registry path %1 is not writable.</source>
+ <translation>مسار التسجيل %1 غير قابل للكتابة.</translation>
+ </message>
+ <message>
+ <source>exactly %1</source>
+ <translation>%1 بالضبط</translation>
+ </message>
+ <message>
+ <source>at least %1</source>
+ <translation>%1 على الأقل</translation>
+ </message>
+ <message>
+ <source>not more than %1</source>
+ <translation>ليس أكثر من %1</translation>
+ </message>
+ <message>
+ <source>%1 or %2</source>
+ <translation>%1 أو %2</translation>
+ </message>
+ <message>
+ <source>%1 to %2</source>
+ <translation>%1 إلى %2</translation>
+ </message>
+ <message numerus="yes">
+ <source>Invalid arguments in %1: %n arguments given, %2 arguments expected.</source>
+ <translation>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2، المعاملات المعطاة %n.</numerusform>
+ <numerusform>معامل غير صحيح في %1: بينما المعامل المتوقع %2، المعامل المعطى %n.</numerusform>
+ <numerusform>معاملان غير صحيحين في %1: بينما المعاملان المتوقعان %2، المعاملان المعطان %n.</numerusform>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2، المعاملات المعطاة %n.</numerusform>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2، المعاملات المعطاة %n.</numerusform>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2، المعاملات المعطاة %n.</numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>Invalid arguments in %1: %n arguments given, %2 arguments expected in the form: %3.</source>
+ <translation>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2 على الصورة %3، المعاملات المعطاة %n.</numerusform>
+ <numerusform>معامل غير صحيح في %1: بينما المعامل المتوقع %2 على الصورة %3، المعامل المعطى %n.</numerusform>
+ <numerusform>معاملان غير صحيحين في %1: بينما المعاملان المتوقعان %2 على الصورة %3، المعاملان المعطان %n.</numerusform>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2 على الصورة %3، المعاملات المعطاة %n.</numerusform>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2 على الصورة %3، المعاملات المعطاة %n.</numerusform>
+ <numerusform>معاملات غير صحيحة في %1: بينما المعاملات المتوقعة %2 على الصورة %3، المعاملات المعطاة %n.</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Renaming file &quot;%1&quot; to &quot;%2&quot; failed: %3</source>
+ <translation>فشلت إعادة تسمية الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation</name>
+ <message>
+ <source>Extracting &quot;%1&quot;</source>
+ <translation>استخراج &quot;%1&quot;</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation::Runnable</name>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الأرشيف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>خطأ أثناء استخراج الأرشيف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Unknown exception caught while extracting &quot;%1&quot;.</source>
+ <translation>اكتُشف خطأ غير معروف أثناء استخراج &quot;%1&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::FakeStopProcessForUpdateOperation</name>
+ <message>
+ <source>Cannot get package manager core.</source>
+ <translation>لا يمكن الحصول على نواة مدير الحزم.</translation>
+ </message>
+ <message>
+ <source>This process should be stopped before continuing: %1</source>
+ <translation>هذه العملية يجب أن تتوقف قبل الاستمرار: %1</translation>
+ </message>
+ <message>
+ <source>These processes should be stopped before continuing: %1</source>
+ <translation>هذه العمليات يجب أن تتوقف قبل الاستمرار: %1</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::GlobalSettingsOperation</name>
+ <message>
+ <source>Settings are not writable.</source>
+ <translation>الإعدادات غير قابلة للكتابة.</translation>
+ </message>
+ <message>
+ <source>Failed to write settings.</source>
+ <translation>فشلت كتابة الإعدادات.</translation>
+ </message>
+</context>
+<context>
+ <name>InstallerCalculator</name>
+ <message>
+ <source>Components added as automatic dependencies:</source>
+ <translation>المكونات المضافة كاعتماديات تلقائية:</translation>
+ </message>
+ <message>
+ <source>Components added as dependency for &quot;%1&quot;:</source>
+ <translation>المكونات المضافة كاعتمادية لـ &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Components that have resolved dependencies:</source>
+ <translation>المكونات المُستوفاة اعتمادياتها:</translation>
+ </message>
+ <message>
+ <source>Selected components without dependencies:</source>
+ <translation>المكونات المحددة دون اعتماديات:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component &quot;%1&quot; already added with reason: &quot;%2&quot;</source>
+ <translation>اكتُشف تكرار، المكون &quot;%1&quot; مضاف بالفعل مع السبب: &quot;%2&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
+ <translation>لا يمكن العثور على الاعتمادية المفقودة &quot;%1&quot; لـ &quot;%2&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::InstallIconsOperation</name>
+ <message>
+ <source>&lt;source path&gt; [vendor prefix]</source>
+ <translation>&lt;مسار المصدر&gt; [اختصار المزود]</translation>
+ </message>
+ <message>
+ <source>Invalid Argument: source directory must not be empty.</source>
+ <translation>معامل غير صحيح: مجلد المصدر يجب ألا يكون فارغاً.</translation>
+ </message>
+ <message>
+ <source>Cannot backup file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Failed to overwrite &quot;%1&quot;: %2</source>
+ <translation>فشلت إعادة كتابة &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Failed to copy file &quot;%1&quot;: %2</source>
+ <translation>فشل نسخ الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء المجلد &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>Lib7z</name>
+ <message>
+ <source>internal code: %1</source>
+ <translation>الرمز الداخلي: %1</translation>
+ </message>
+ <message>
+ <source>not enough memory</source>
+ <translation>لا توجد ذاكرة كافية</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>خطأ: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve property %1 for item %2.</source>
+ <translation>لا يمكن الحصول على الخاصية %1 للعتصر %2.</translation>
+ </message>
+ <message>
+ <source>Property %1 for item %2 not of type VT_FILETIME but %3.</source>
+ <translation>الخاصية %1 للعنصر %2 ليست من نوع VT_FILETIME لكن %3.</translation>
+ </message>
+ <message>
+ <source>Cannot convert UTC file time to system time.</source>
+ <translation>لا يمكن تحويل التوقيت العالمي الموحد للملف إلى توقيت النظام.</translation>
+ </message>
+ <message>
+ <source>Cannot load codecs.</source>
+ <translation>لا يمكن تحميل برامج الترميز.</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot;.</source>
+ <translation>لا يمكن فتح الأرشيف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve number of items in archive.</source>
+ <translation>لا يمكن الحصول على عدد العناصر في الأرشيف.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve path of archive item &quot;%1&quot;.</source>
+ <translation>لا يمكن الحصول على مسار عنصر الأرشيف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Unknown exception caught (%1).</source>
+ <translation>اكتُشف استثناء مجهول (%1).</translation>
+ </message>
+ <message>
+ <source>Cannot create temporary file: %1</source>
+ <translation>لا يمكن إنشاء الملف المؤقت: %1</translation>
+ </message>
+ <message>
+ <source>Unsupported archive type.</source>
+ <translation>نوع الأرشيف غير مدعوم.</translation>
+ </message>
+ <message>
+ <source>Cannot create archive &quot;%1&quot;</source>
+ <translation>لا يمكن إنشاء الأرشيف &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot create archive &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء الأرشيف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove old archive &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف الأرشيف القديم &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot rename temporary archive &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation>لا يمكن إعادة تسمية الأرشيف المؤقت &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Unknown exception caught (%1)</source>
+ <translation>اكتُشف استثناء مجهول (%1)</translation>
+ </message>
+</context>
+<context>
+ <name>DirectoryGuard</name>
+ <message>
+ <source>Path &quot;%1&quot; exists but is not a directory.</source>
+ <translation>المسار %1 موجود بالفعل لكنه ليس مجلداً.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;.</source>
+ <translation>لا يمكن إنشاء المجلد &quot;%1&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>ExtractCallbackImpl</name>
+ <message>
+ <source>Cannot retrieve path of archive item %1.</source>
+ <translation>لا يمكن الحصول على مسار عنصر الأرشيف %1.</translation>
+ </message>
+ <message>
+ <source>Cannot remove already existing symlink %1.</source>
+ <translation>لا يمكن حذف الارتباط الموجود بالفعل %1.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot create symlink at &quot;%1&quot;. Another one is already existing.</source>
+ <translation>لا يمكن إنشاء الارتباط في &quot;%1&quot;. ارتباط آخر موجود بالفعل.</translation>
+ </message>
+ <message>
+ <source>Cannot read symlink target from file &quot;%1&quot;.</source>
+ <translation>لا يمكن قراءة وجهة الارتباط من الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot create symlink at %1: %2</source>
+ <translation>لا يمكن إنشاء الارتباط في %1: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LicenseOperation</name>
+ <message>
+ <source>No license files found to copy.</source>
+ <translation>لم يُعثر على ملفات ترخيص موجودة لنسخها.</translation>
+ </message>
+ <message>
+ <source>Needed installer object in %1 operation is empty.</source>
+ <translation>كائن المثبت المطلوب في العملية %1 فارغ.</translation>
+ </message>
+ <message>
+ <source>Can not write license file &quot;%1&quot;.</source>
+ <translation>لا يمكن كتابة ملف الرخصة &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>No license files found to delete.</source>
+ <translation>لم يُعثر على ملفات ترخيص لحذفها.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LineReplaceOperation</name>
+ <message>
+ <source>Invalid argument in %1: Empty search argument is not supported.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::MetadataJob</name>
+ <message>
+ <source>Missing package manager core engine.</source>
+ <translation>محرك نواة مدير الحزم مفقود.</translation>
+ </message>
+ <message>
+ <source>Preparing meta information download...</source>
+ <translation>جارٍ تحضير تنزيل ملفات التعريف...</translation>
+ </message>
+ <message>
+ <source>Unpacking compressed repositories. This may take a while...</source>
+ <translation>جارٍ فك ضغط المستودعات المضغوطة. قد يستغرق هذا بعص الوقت...</translation>
+ </message>
+ <message>
+ <source>Meta data download canceled.</source>
+ <translation>أُلغي تحميل ملفات التعريف.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during extracting.</source>
+ <translation>حدث خطأ غير معروف أثناء الاستخراج.</translation>
+ </message>
+ <message>
+ <source>Missing proxy credentials.</source>
+ <translation>بيانات اعتماد الوكيل مفقودة.</translation>
+ </message>
+ <message>
+ <source>Authentication failed.</source>
+ <translation>فشلت المصادقة.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during download.</source>
+ <translation>حدث استثناء غير معروف أثناء التنزيل.</translation>
+ </message>
+ <message>
+ <source>Failure to fetch repositories.</source>
+ <translation>فشل جلب المستودعات.</translation>
+ </message>
+ <message>
+ <source>Extracting meta information...</source>
+ <translation>جارٍ استخراج معلومات التعريف...</translation>
+ </message>
+ <message>
+ <source>Checksum mismatch detected for &quot;%1&quot;.</source>
+ <translation>اكتُشف عدم تطابق تدقيق المجموع لـ &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository... %1/%2 </source>
+ <translation>جارٍ الحصول على معلومات التعريف من المستودع البعيد... %1/%2 </translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository... </source>
+ <translation>جارٍ الحصول على معلومات التعريف من المستودع البعيد... </translation>
+ </message>
+ <message>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>حدث خطأ أثناء إستخراج الأرشيف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Unknown exception caught while extracting archive &quot;%1&quot;.</source>
+ <translation>اكتُشف استثناء غير معروف أثناء استخراج الأرشيف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::FileTaskObserver</name>
+ <message>
+ <source>%1 of %2</source>
+ <translation>%1 من %2</translation>
+ </message>
+ <message>
+ <source>%1 received.</source>
+ <translation>استُقبل %1.</translation>
+ </message>
+ <message>
+ <source>(%1/sec)</source>
+ <translation>(%1/ثانية)</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s), </source>
+ <translation>
+ <numerusform>%n يوم(أيام)، </numerusform>
+ <numerusform>يوم، </numerusform>
+ <numerusform>يومان </numerusform>
+ <numerusform>%n أيام، </numerusform>
+ <numerusform>%n يوم، </numerusform>
+ <numerusform>%n يوم، </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s), </source>
+ <translation>
+ <numerusform>%n ساعة(ساعات)، </numerusform>
+ <numerusform>ساعة، </numerusform>
+ <numerusform>ساعتان، </numerusform>
+ <numerusform>%n ساعات، </numerusform>
+ <numerusform>%n ساعة، </numerusform>
+ <numerusform>%n ساعة، </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation>
+ <numerusform>%n دقيقة(دقائق)</numerusform>
+ <numerusform>دقيقة</numerusform>
+ <numerusform>دقيقتان</numerusform>
+ <numerusform>%n دقائق</numerusform>
+ <numerusform>%n دقيقة</numerusform>
+ <numerusform>%n دقيقة</numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation>
+ <numerusform>%n ثانية(ثوان)</numerusform>
+ <numerusform>ثانية</numerusform>
+ <numerusform>ثانيتان</numerusform>
+ <numerusform>%n ثوان</numerusform>
+ <numerusform>%n ثانية</numerusform>
+ <numerusform>%n ثانية</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source> - %1%2%3%4 remaining.</source>
+ <translation> - يتبقى %1%2%3%4.</translation>
+ </message>
+ <message>
+ <source> - unknown time remaining.</source>
+ <translation> - الوقت المتبقي غير معلوم.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::PackageManagerCore</name>
+ <message>
+ <source>Error writing Maintenance Tool</source>
+ <translation>خطأ في كتابة أداة الصيانة</translation>
+ </message>
+ <message>
+ <source>
+Downloading packages...</source>
+ <translation>
+جارٍ تنزيل الحزم...</translation>
+ </message>
+ <message>
+ <source>Installation canceled by user.</source>
+ <translation>ألغى المستخدم التحديث.</translation>
+ </message>
+ <message>
+ <source>All downloads finished.</source>
+ <translation>انتهت كل التنزيلات.</translation>
+ </message>
+ <message>
+ <source>Cancelling the Installer</source>
+ <translation>إلغاء المثبت</translation>
+ </message>
+ <message>
+ <source>Authentication Error</source>
+ <translation>حدث خطأ في المصادقة</translation>
+ </message>
+ <message>
+ <source>Some components could not be removed completely because administrative rights could not be acquired: %1.</source>
+ <translation>لا يمكن حذف بعص المكونات كاملاً لتعذر الحصول على صلاحيات المسؤول: %1.</translation>
+ </message>
+ <message>
+ <source>Unknown error.</source>
+ <translation>خطأ غير معروف.</translation>
+ </message>
+ <message>
+ <source>Some components could not be removed completely because an unknown error happened.</source>
+ <translation>لا يمكن حذف بعض المكونات كاملاً لحدوث خطأ غير معروف.</translation>
+ </message>
+ <message>
+ <source>User input is required but the output device is not associated with a terminal.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The directory you selected already exists and contains an installation. Choose a different target for installation.</source>
+ <translation type="unfinished">المجلد الذي اخترته موجود بالفعل ويحتوي على تثبيت. اختر وجهة مختلفة للتثبيت.</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation type="unfinished">تحذير</translation>
+ </message>
+ <message>
+ <source>You have selected an existing, non-empty directory for installation.
+Note that it will be completely wiped on uninstallation of this application.
+It is not advisable to install into this directory as installation might fail.
+Do you want to continue?</source>
+ <translation type="unfinished">لقد اخترت مجلداً موجودأ وغير فارغ للتثبيت.
+سيتم حذفه تماماً عند إزالة تثبيت هذا التطبيق.
+لا يُنصح بالتثبيت إلى هذا المجلد لاحتمالية فشل التثبيت.
+هل تريد الاستمرار؟</translation>
+ </message>
+ <message>
+ <source>You have selected an existing file or symlink, please choose a different target for installation.</source>
+ <translation type="unfinished">لقد حددت ملفاً موجوداً أو اختصاراً، يرجى اختيار وجهة مختلفة للتثبيت.</translation>
+ </message>
+ <message>
+ <source>The installation path cannot be empty, please specify a valid directory.</source>
+ <translation type="unfinished">لا يمكن أن يكون مسار التثبيت فارغاً. يرجى تحديد مجلد صالح.</translation>
+ </message>
+ <message>
+ <source>The installation path cannot be relative, please specify an absolute path.</source>
+ <translation type="unfinished">لا يمكن أن يكون مسار التثبيت نسبياً، يرجى تحديد مسار مطلق.</translation>
+ </message>
+ <message>
+ <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source>
+ <translation type="unfinished">المسار أو مجلد التثبيت لا يحتوي على محارف ASCII. هذا غير مدعوم حالياً يرجى اختيار مسار أو مجلد تثبيت مختلف.</translation>
+ </message>
+ <message>
+ <source>As the install directory is completely deleted, installing in %1 is forbidden.</source>
+ <translation type="unfinished">التثبيت في %1 محظور لأن مجلد التثبيت سيُمسح تماماً.</translation>
+ </message>
+ <message>
+ <source>The path you have entered is too long, please make sure to specify a valid path.</source>
+ <translation type="unfinished">المسار الذي أدخلته طويل جداً، يرجى التأكد من تخصيص مسار صالح.</translation>
+ </message>
+ <message>
+ <source>The path you have entered is not valid, please make sure to specify a valid target.</source>
+ <translation type="unfinished">المسار الذي أدخلته غير صالح، يرحى التأكد من تخصيص وجهة صالحة.</translation>
+ </message>
+ <message>
+ <source>The path you have entered is not valid, please make sure to specify a valid drive.</source>
+ <translation type="unfinished">المسار الذي أدخلته غير صالح، يرحى التأكد من تخصيص قرص صالح.</translation>
+ </message>
+ <message>
+ <source>The installation path must not end with &apos;.&apos;, please specify a valid directory.</source>
+ <translation type="unfinished">يجب ألا ينتهي مسار التثبيت بنقطة &quot;.&quot;، يرجى تخصيص مجلد صالح.</translation>
+ </message>
+ <message>
+ <source>The installation path must not contain &quot;%1&quot;, please specify a valid directory.</source>
+ <translation type="unfinished">يجب ألا يحتوي مسار التثبيت على &quot;%1&quot;، يرجى تخصيص مجلد صالح.</translation>
+ </message>
+ <message>
+ <source>Application not running in Package Manager mode.</source>
+ <translation>لا يعمل التطبيق في وضع مدير الحزم.</translation>
+ </message>
+ <message>
+ <source>No installed packages found.</source>
+ <translation>لم يُعثر على حزم مثبتة.</translation>
+ </message>
+ <message>
+ <source>Cannot register component! Component with identifier %1 already exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Application running in Uninstaller mode.</source>
+ <translation>يعمل التطبيق في وضع إزالة التثبيت.</translation>
+ </message>
+ <message>
+ <source>There is an important update available, please run the updater first.</source>
+ <translation>هناك تحديث مهم متاح يرجى تشغيل المحدث أولاً.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve all dependencies.</source>
+ <translation>لا يمكن الحصول على كافة الاعتماديات.</translation>
+ </message>
+ <message>
+ <source>Components about to be removed.</source>
+ <translation>المكونات على وشك الحذف.</translation>
+ </message>
+ <message>
+ <source>Cannot install %1. Component not found.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. Component is not checkable meaning you have to select one of the subcomponents.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Component %1 already installed
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install %1. Component is virtual.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Running processes found.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while elevating access rights.</source>
+ <translation>خطأ أثناء رفع حقوق الوصول.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>خطأ</translation>
+ </message>
+ <message>
+ <source>Not enough disk space to store temporary files and the installation. %1 are available, while %2 are at least required.</source>
+ <translation type="unfinished">لا توجد مساحة كافية لتخزين الملفات المؤقتة والتثبيت! بينما المساحة المطلوبة هي %2 على الأقل، المساحة المتاحة هي %1.</translation>
+ </message>
+ <message>
+ <source>Not enough disk space to store all selected components! %1 are available while %2 are at least required.</source>
+ <translation type="unfinished">لا توجد مساحة كافية لتخزين جميع المكونات المحددة! بينما المساحة المطلوبة هي %2 على الأقل، المساحة المتاحة هي %1.</translation>
+ </message>
+ <message>
+ <source>Not enough disk space to store temporary files! %1 are available while %2 are at least required.</source>
+ <translation type="unfinished">لا توجد مساحة كافية لتخزين الملفات المؤقتة!. بينما المساحة المطلوبة هي %2 على الأقل، المساحة المتاحة هي %1.</translation>
+ </message>
+ <message>
+ <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Installation will use %1 of disk space.</source>
+ <translation type="unfinished">سيستخدم التثبيت %1 من مساحة القرص الصلب.</translation>
+ </message>
+ <message>
+ <source>invalid</source>
+ <translation>غير صالح</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::PackageManagerCorePrivate</name>
+ <message>
+ <source>Unresolved dependencies</source>
+ <translation>الاعتماديات التي لم يُحصل عليها</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>خطأ</translation>
+ </message>
+ <message>
+ <source>Access error</source>
+ <translation>خطأ في الوصول</translation>
+ </message>
+ <message>
+ <source>Format error</source>
+ <translation>خطأ في التنسيق</translation>
+ </message>
+ <message>
+ <source>Cannot write installer configuration to %1: %2</source>
+ <translation>لا يمكن كتابة إعدادات المثبت إلى %1: %2</translation>
+ </message>
+ <message>
+ <source>Stop Processes</source>
+ <translation>أوقف العمليات</translation>
+ </message>
+ <message>
+ <source>These processes should be stopped to continue:
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Installation canceled by user</source>
+ <translation>ألغى المستخدم التثبيت</translation>
+ </message>
+ <message>
+ <source>Retry count exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Writing maintenance tool.</source>
+ <translation>كتابة أداة الصيانة.</translation>
+ </message>
+ <message>
+ <source>Failed to seek in file %1: %2</source>
+ <translation>فشل البحث في الملف %1: %2</translation>
+ </message>
+ <message>
+ <source>Maintenance tool is not a bundle</source>
+ <translation>أداة الصيانة ليست حزمة</translation>
+ </message>
+ <message>
+ <source>Cannot remove data file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف ملف البيانات &quot;%1&quot;:%2</translation>
+ </message>
+ <message>
+ <source>Cannot write maintenance tool data to %1: %2</source>
+ <translation>لا يمكن كتابة بيانات أداة الصيانة إلى %1: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write maintenance tool to &quot;%1&quot;: %2</source>
+ <translation>لا يمكن كتابة أداة الصيانة إلى &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove temporary data file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف بيانات الملف المؤقت &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write maintenance tool binary data to %1: %2</source>
+ <translation>لا يمكن كتابة البيانات الثنائية لأداة الصيانة إلى %1: %2</translation>
+ </message>
+ <message>
+ <source>Writing offline base binary.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;.</source>
+ <translation type="unfinished">لا يمكن إنشاء المجلد &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot write offline binary to &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove temporary file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Variable &apos;TargetDir&apos; not set.</source>
+ <translation>المتغير &apos;TargetDir&apos; غير مضبوط.</translation>
+ </message>
+ <message>
+ <source>Preparing the installation...</source>
+ <translation>جارٍ تحضير التثبيت...</translation>
+ </message>
+ <message>
+ <source>It is not possible to install from network location</source>
+ <translation>لا يمكن التثبيت من موقع في الشبكة</translation>
+ </message>
+ <message>
+ <source>Creating local repository</source>
+ <translation>إنشاء مستودع محلي</translation>
+ </message>
+ <message>
+ <source>Creating Maintenance Tool</source>
+ <translation>إنشاء أداة الصيانة</translation>
+ </message>
+ <message>
+ <source>
+Installation finished!</source>
+ <translation>
+انتهى التثبيت!</translation>
+ </message>
+ <message>
+ <source>
+Installation aborted!</source>
+ <translation>
+أُلغي التثبيت!</translation>
+ </message>
+ <message>
+ <source>It is not possible to run that operation from a network location</source>
+ <translation>لا يمكن تشغيل تلك العملية من موقع في الشبكة</translation>
+ </message>
+ <message>
+ <source>Removing deselected components...</source>
+ <translation>جارٍ حذف المكونات الغير محددة...</translation>
+ </message>
+ <message>
+ <source>
+Update finished!</source>
+ <translation>
+انتهى التحديث!</translation>
+ </message>
+ <message>
+ <source>
+Update aborted!</source>
+ <translation>
+أُلغي التحديث!</translation>
+ </message>
+ <message>
+ <source>Uninstallation completed successfully.</source>
+ <translation>أُزيل التثبيت بنجاح.</translation>
+ </message>
+ <message>
+ <source>Uninstallation aborted.</source>
+ <translation>أُلغيت إزالة التثبيت.</translation>
+ </message>
+ <message>
+ <source>Cannot create target directory for installer.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Preparing offline generation...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Preparing installer configuration...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating the installer...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Failed to create offline installer. %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove temporary directory &quot;%1&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Offline generation completed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Offline generation aborted!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>
+Installing component %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Installer Error</source>
+ <translation>خطأ المثبت</translation>
+ </message>
+ <message>
+ <source>Error during installation process (%1):
+%2</source>
+ <translation>حدث خطأ أثناء عملية التثبيت (%1) :
+%2</translation>
+ </message>
+ <message>
+ <source>Done</source>
+ <translation>انتهى</translation>
+ </message>
+ <message>
+ <source>Cannot prepare uninstall</source>
+ <translation>لا يمكن تحضير إزالة التثبيت</translation>
+ </message>
+ <message>
+ <source>Cannot start uninstall</source>
+ <translation>لا يمكن بدأ إزالة التثبيت</translation>
+ </message>
+ <message>
+ <source>Error during uninstallation process:
+%1</source>
+ <translation>حدث خطأ أثناء عملية إزالة التثبيت :
+%1</translation>
+ </message>
+ <message>
+ <source>Unknown error</source>
+ <translation>خطأ غير معروف</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve remote tree %1.</source>
+ <translation>لا يمكن الحصول على الشجرة البعيدة %1.</translation>
+ </message>
+ <message>
+ <source>Failure to read packages from %1.</source>
+ <translation>فشلت قراءة الحزم من %1.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve meta information: %1</source>
+ <translation>لا يمكن الحصول على بيانات التعريف: %1</translation>
+ </message>
+ <message>
+ <source>Cannot add temporary update source information.</source>
+ <translation>لا تمكن إضافة معلومات مصدر التحديث المؤقت.</translation>
+ </message>
+ <message>
+ <source>Cannot find any update source information.</source>
+ <translation>لا يمكن العثور على أي معلومات مصدر تحديث.</translation>
+ </message>
+ <message>
+ <source>Dependency cycle between components &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>اكتُشفت دورة اعتماديات بين المكونان &quot;%1&quot; و&quot;%2&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::PackageManagerGui</name>
+ <message>
+ <source>%1 Setup</source>
+ <translation>إعداد %1</translation>
+ </message>
+ <message>
+ <source>Maintain %1</source>
+ <translation>صيانة %1</translation>
+ </message>
+ <message>
+ <source>Do you want to cancel the installation process?</source>
+ <translation>هل تريد إلغاء عملية التثبيت؟</translation>
+ </message>
+ <message>
+ <source>Do you want to cancel the uninstallation process?</source>
+ <translation>هل تريد إلغاء عملية إزالة التثبيت؟</translation>
+ </message>
+ <message>
+ <source>Do you want to quit the installer application?</source>
+ <translation>هل تريد الخروج من برنامج المثبت؟</translation>
+ </message>
+ <message>
+ <source>Do you want to quit the uninstaller application?</source>
+ <translation>هل تريد الخروج من برنامج إزالة التثبيت؟</translation>
+ </message>
+ <message>
+ <source>Do you want to quit the maintenance application?</source>
+ <translation>هل تريد الخروج من تطبيق الصيانة؟</translation>
+ </message>
+ <message>
+ <source>%1 Question</source>
+ <translation>سؤال %1</translation>
+ </message>
+ <message>
+ <source>Settings</source>
+ <translation>الإعدادات</translation>
+ </message>
+ <message>
+ <source>Specify proxy settings and configure repositories for add-on components.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>خطأ</translation>
+ </message>
+ <message>
+ <source>It is not possible to install from network location.
+Please copy the installer to a local drive</source>
+ <translation>من غير الممكن التثبيت من موقع في الشبكة
+يرجى نسخ المثبت إلى فرص محلي</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::IntroductionPage</name>
+ <message>
+ <source>Setup - %1</source>
+ <translation>الإعداد - %1</translation>
+ </message>
+ <message>
+ <source>Welcome to the %1 Setup Wizard.</source>
+ <translation>مرحباً بك في إعداد %1.</translation>
+ </message>
+ <message>
+ <source>&amp;Add or remove components</source>
+ <translation>&amp;أضف أو أزل المكونات</translation>
+ </message>
+ <message>
+ <source>&amp;Update components</source>
+ <translation>&amp;حدّث المكونات</translation>
+ </message>
+ <message>
+ <source>&amp;Remove all components</source>
+ <translation>&amp;احذف جميع المكونات</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote installation sources...</source>
+ <translation>جارٍ الحصول على المعلومات من مصادر التثبيت عن بعد...</translation>
+ </message>
+ <message>
+ <source>At least one valid and enabled repository required for this action to succeed.</source>
+ <translation>يجب أن يكون هناك مستودع واحد مفعل وصالح على الأقل لنجاخ هذا الإجراء.</translation>
+ </message>
+ <message>
+ <source>No updates available.</source>
+ <translation>لا تحديثات متوفرة.</translation>
+ </message>
+ <message>
+ <source> Only local package management available.</source>
+ <translation> إدارة الحزم المحلية هي المتاحة فقط.</translation>
+ </message>
+ <message>
+ <source>&amp;Quit</source>
+ <translation>&amp;اخرج</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LicenseAgreementPage</name>
+ <message>
+ <source>License Agreement</source>
+ <translation>اتفاقية الترخيص</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <comment>agree license</comment>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source>
+ <translation>يرجى قراءة اتفاقية الترخيص الآتية. يجب أن توافق على الشروط الواردة في هذه الاتفاقية قبل متابعة التثبيت.</translation>
+ </message>
+ <message>
+ <source>I accept the license.</source>
+ <translation>أقبل الرخصة.</translation>
+ </message>
+ <message>
+ <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source>
+ <translation>يرجى قراءة اتفاقيات الترخيص الآتية. يجب أن توافق على الشروط الواردة في هذه الاتفاقيات قبل متابعة التثبيت.</translation>
+ </message>
+ <message>
+ <source>I accept the licenses.</source>
+ <translation>أقبل الرخص.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::TargetDirectoryPage</name>
+ <message>
+ <source>Installation Folder</source>
+ <translation>مجلد التثبيت</translation>
+ </message>
+ <message>
+ <source>Please specify the directory where %1 will be installed.</source>
+ <translation>يرجى تخصيص مجلد تثبيت %1.</translation>
+ </message>
+ <message>
+ <source>Alt+R</source>
+ <comment>browse file system to choose a file</comment>
+ <translation>Alt+R</translation>
+ </message>
+ <message>
+ <source>B&amp;rowse...</source>
+ <translation>ا&amp;ستعرض...</translation>
+ </message>
+ <message>
+ <source>Browse file system to choose the installation directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select Installation Folder</source>
+ <translation>اختر مجلد التثبيت</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::StartMenuDirectoryPage</name>
+ <message>
+ <source>Start Menu shortcuts</source>
+ <translation>اختصارات قائمة ابدأ</translation>
+ </message>
+ <message>
+ <source>Select the Start Menu in which you would like to create the program&apos;s shortcuts. You can also enter a name to create a new directory.</source>
+ <translation>حدّد قائمة ابدأ التي تريد إنشاء اختصارات البرنامج فيها. يمكنك أيضاً إدخال اسم لإنشاء مجلد جديد.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ReadyForInstallationPage</name>
+ <message>
+ <source>U&amp;ninstall</source>
+ <translation>أ&amp;زل التثبيت</translation>
+ </message>
+ <message>
+ <source>Ready to Uninstall</source>
+ <translation>جاهز لإزالة التثبيت</translation>
+ </message>
+ <message>
+ <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <translation>الإعداد جاهز الآن لبدأ حذف %1 من حاسوبك. .&lt;br&gt;&lt;font color=&quot;red&quot;&gt;مجلد البرنامج %2 سيُحذف تماماً&lt;/font&gt; مع كل المحتوى فيه!</translation>
+ </message>
+ <message>
+ <source>U&amp;pdate</source>
+ <translation>ح&amp;دث</translation>
+ </message>
+ <message>
+ <source>Ready to Update Packages</source>
+ <translation>جاهز لتحديث الحزم</translation>
+ </message>
+ <message>
+ <source>Setup is now ready to begin updating your installation.</source>
+ <translation>الإعداد جاهز لبدأ تحديث تثبيتك.</translation>
+ </message>
+ <message>
+ <source>&amp;Install</source>
+ <translation>&amp;ثبت</translation>
+ </message>
+ <message>
+ <source>Ready to Install</source>
+ <translation>جاهز للتثبيت</translation>
+ </message>
+ <message>
+ <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <translation>الإعداد جاهز الآن لبدأ تثبيت %1 على حاسوبك.</translation>
+ </message>
+ <message>
+ <source>Ready to Update</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::PerformInstallationPage</name>
+ <message>
+ <source>U&amp;ninstall</source>
+ <translation>أ&amp;زل التثبيت</translation>
+ </message>
+ <message>
+ <source>Uninstalling %1</source>
+ <translation>إزالة تثبيت %1</translation>
+ </message>
+ <message>
+ <source>&amp;Update</source>
+ <translation>&amp;حدّث</translation>
+ </message>
+ <message>
+ <source>Updating components of %1</source>
+ <translation>تحديث مكونات %1</translation>
+ </message>
+ <message>
+ <source>&amp;Install</source>
+ <translation>&amp;ثبت</translation>
+ </message>
+ <message>
+ <source>Installing %1</source>
+ <translation>تثبيت %1</translation>
+ </message>
+ <message>
+ <source>Installing</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Updating</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Uninstalling</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::FinishedPage</name>
+ <message>
+ <source>Completing the %1 Wizard</source>
+ <translation>إكمال معالج %1</translation>
+ </message>
+ <message>
+ <source>Finished</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Click %1 to exit the %2 Wizard.</source>
+ <translation>اضغط %1 للخروج من معالج %2.</translation>
+ </message>
+ <message>
+ <source>Restart</source>
+ <translation>أعد التشغيل</translation>
+ </message>
+ <message>
+ <source>Run %1 now.</source>
+ <translation>شغل %1 الآن.</translation>
+ </message>
+ <message>
+ <source>The %1 Wizard failed.</source>
+ <translation>فشل معالج %1.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::RestartPage</name>
+ <message>
+ <source>Completing the %1 Setup Wizard</source>
+ <translation>إكمال معالج إعداد %1</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::PerformInstallationForm</name>
+ <message>
+ <source>&amp;Show Details</source>
+ <translation>&amp;اعرض التفاصيل</translation>
+ </message>
+ <message>
+ <source>&amp;Hide Details</source>
+ <translation>&amp;أخف التفاصيل</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::RegisterFileTypeOperation</name>
+ <message>
+ <source>Registering file types is only supported on Windows.</source>
+ <translation>تسجيل أنواع الملفات متاح فقط على نظام Windows.</translation>
+ </message>
+ <message>
+ <source>Register File Type: Invalid arguments</source>
+ <translation>تسجيل أنواع الملفات: معاملات غير صحيحة</translation>
+ </message>
+</context>
+<context>
+ <name>RemoteClient</name>
+ <message>
+ <source>Cannot get authorization.</source>
+ <translation>لا يمكن الحصول على المصادقة.</translation>
+ </message>
+ <message>
+ <source>Cannot get authorization that is needed for continuing the installation.
+Please start the setup program as a user with the appropriate rights.
+Or accept the elevation of access rights if being asked.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot get authorization that is needed for continuing the installation.
+ Either abort the installation or use the fallback solution by running
+%1
+as a user with the appropriate rights and then clicking OK.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::RemoteObject</name>
+ <message>
+ <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source>
+ <translation>لا يمكن قراءة جميع البيانات بعد إرسال الأمر: %1. البايت المتوقع: %2، البايت المُستقبل: %3. الخطأ: %4</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ReplaceOperation</name>
+ <message>
+ <source>Current search argument calling &quot;%1&quot; with empty search argument is not supported.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Current mode argument calling &quot;%1&quot; with arguments &quot;%2&quot; is not supported. Please use string or regex.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ScriptEngine</name>
+ <message>
+ <source>Cannot open script file at %1: %2</source>
+ <translation>لا يمكن فتح ملف النص البرمجي في %1: %2</translation>
+ </message>
+ <message>
+ <source>Exception while loading the component script &quot;%1&quot;: %2</source>
+ <translation>حدث استثناء أثناء تحميل النص البرمجي للمكون %1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Unknown error.</source>
+ <translation>خطأ غير معروف.</translation>
+ </message>
+ <message>
+ <source>on line number: </source>
+ <translation>الرقم على الخط: </translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::SelfRestartOperation</name>
+ <message>
+ <source>Installer object needed in operation %1 is empty.</source>
+ <translation>كائن المثبت المطلوب في العملية %1 فارغ.</translation>
+ </message>
+ <message>
+ <source>Self Restart: Only valid within updater or packagemanager mode.</source>
+ <translation>إعادة التشغيل التلقائية: متاحة فقط في وضع المحدث أو وضع مدير الحزم.</translation>
+ </message>
+ <message>
+ <source>Self Restart: Invalid arguments</source>
+ <translation>إعادة التشغيل التلقائية: معاملات غير صحيحة</translation>
+ </message>
+</context>
+<context>
+ <name>Settings</name>
+ <message>
+ <source>Cannot open settings file %1 for reading: %2</source>
+ <translation>لا يمكن فتح ملف الإعدادات %1 للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Select Categories</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::SettingsOperation</name>
+ <message>
+ <source>Missing argument(s) &quot;%1&quot; calling %2 with arguments &quot;%3&quot;.</source>
+ <translation>هناك معامل (معاملات) مفقودة &quot;%1&quot; يستدعي %2 بالمعامل &quot;%3&quot;.</translation>
+ </message>
+ <message>
+ <source>Current method argument calling &quot;%1&quot; with arguments &quot;%2&quot; is not supported. Please use set, remove, add_array_value or remove_array_value.</source>
+ <translation>معامل الدالة الحالية المستدعاة &quot;%1&quot; بالمعاملات &quot;%2&quot; غير مدعوم. يرجى استخدام set أو remove أو add_array_value أو remove_array_value.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::SimpleMoveFileOperation</name>
+ <message>
+ <source>None of the arguments can be empty: source &quot;%1&quot;, target &quot;%2&quot;.</source>
+ <translation>لا يمكن أن تكون المعاملات الآتية فارغة: المصدر &quot;%1&quot; والوجهة &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot move file from &quot;%1&quot; to &quot;%2&quot;, because the target path exists and is not removable.</source>
+ <translation>لا يمكن نقل الملف من &quot;%1&quot; إلى &quot;%2&quot;، لأن مسار الوجهة موجود وغير قابل للحذف.</translation>
+ </message>
+ <message>
+ <source>Cannot move file &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation>لا يمكن نقل الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Moving file &quot;%1&quot; to &quot;%2&quot;.</source>
+ <translation>جارٍ نقل الملف &quot;%1&quot; إلى &quot;%2&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::TestRepository</name>
+ <message>
+ <source>Missing package manager core engine.</source>
+ <translation>محرك نواة مدير الحزم مفقود.</translation>
+ </message>
+ <message>
+ <source>Empty repository URL.</source>
+ <translation>رابط المستودع فارع.</translation>
+ </message>
+ <message>
+ <source>Download canceled.</source>
+ <translation>أُلغي التنزيل.</translation>
+ </message>
+ <message>
+ <source>Timeout while testing repository &quot;%1&quot;.</source>
+ <translation>نفذ الوقت أثناء اختبار المستودع &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot parse Updates.xml: %1</source>
+ <translation>لا يمكن فرز ملف Updates.xml: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open Updates.xml for reading: %1</source>
+ <translation>لا يمكن فتح ملف Updates.xml للقراءة: %1</translation>
+ </message>
+ <message>
+ <source>Authentication failed.</source>
+ <translation>فشلت المصادقة.</translation>
+ </message>
+ <message>
+ <source>Unknown error while testing repository &quot;%1&quot;.</source>
+ <translation>حدث خطأ غير معروف أثناء اختبار المستودع &quot;%1&quot;.</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::FileDownloader</name>
+ <message>
+ <source>Download finished.</source>
+ <translation>انتهى التنزيل.</translation>
+ </message>
+ <message>
+ <source>Cryptographic hashes do not match.</source>
+ <translation>تجزئة التشفير لا تتطابق.</translation>
+ </message>
+ <message>
+ <source>Download canceled.</source>
+ <translation>أُلغي التنزيل.</translation>
+ </message>
+ <message>
+ <source>%1 of %2</source>
+ <translation>%1 من %2</translation>
+ </message>
+ <message>
+ <source>%1 downloaded.</source>
+ <translation>حُمّل %1.</translation>
+ </message>
+ <message>
+ <source>(%1/sec)</source>
+ <translation>(%1/ثانية)</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s), </source>
+ <translation>
+ <numerusform>%n يوم(أيام)، </numerusform>
+ <numerusform>يوم، </numerusform>
+ <numerusform>يومان، </numerusform>
+ <numerusform>%n أيام، </numerusform>
+ <numerusform>%n يوم، </numerusform>
+ <numerusform>%n يوم، </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s), </source>
+ <translation>
+ <numerusform>%n ساعة(ساعات)، </numerusform>
+ <numerusform>ساعة، </numerusform>
+ <numerusform>ساعتان، </numerusform>
+ <numerusform>%n ساعات، </numerusform>
+ <numerusform>%n ساعة، </numerusform>
+ <numerusform>%n ساعة، </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation>
+ <numerusform>%n دقيقة(دقائق)</numerusform>
+ <numerusform>دقيقة</numerusform>
+ <numerusform>دقيقتان</numerusform>
+ <numerusform>%n دقائق</numerusform>
+ <numerusform>%n دقيقة</numerusform>
+ <numerusform>%n دقيقة</numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation>
+ <numerusform>%n ثانية(ثوان)</numerusform>
+ <numerusform>ثانية</numerusform>
+ <numerusform>ثانيتان</numerusform>
+ <numerusform>%n ثوان</numerusform>
+ <numerusform>%n ثانية</numerusform>
+ <numerusform>%n ثانية</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source> - %1%2%3%4 remaining.</source>
+ <translation> - يتبقى %1%2%3%4.</translation>
+ </message>
+ <message>
+ <source> - unknown time remaining.</source>
+ <translation> - الوقت المتبقي غير معلوم.</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::LocalFileDownloader</name>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Writing to file &quot;%1&quot; failed: %2</source>
+ <translation>فشلت الكتابة إلى الملف &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::ResourceFileDownloader</name>
+ <message>
+ <source>Cannot read resource file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن قراءة ملف المصادر &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::HttpDownloader</name>
+ <message>
+ <source>Cannot download %1. Writing to file &quot;%2&quot; failed: %3</source>
+ <translation>لا يمكن تنزيل %1. فشلت الكتابة إلى الملف &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Cannot download %1. Cannot create file &quot;%2&quot;: %3</source>
+ <translation>لا يمكن تنزيل %1. لا يمكن إنشاء الملف &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>%1 at %2</source>
+ <translation>%1 في %2</translation>
+ </message>
+ <message>
+ <source>Authentication request canceled.</source>
+ <translation>أُلغي طلب المصادقة.</translation>
+ </message>
+ <message>
+ <source>Secure Connection Failed</source>
+ <translation>فشل الاتصال الآمن</translation>
+ </message>
+ <message>
+ <source>There was an error during connection to: %1.</source>
+ <translation>كان هناك خطأ أثناء الاتصال إلى: %1.</translation>
+ </message>
+ <message>
+ <source>This could be a problem with the server&apos;s configuration, or it could be someone trying to impersonate the server.</source>
+ <translation>يمكن أن يكون هذا مشكلة في إعدادات الخادم، أو شخص ما يحاول انتحال الخادم.</translation>
+ </message>
+ <message>
+ <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source>
+ <translation>إن اتصلت بالخادم بنجاحٍ في الماضي أو أنك تثق بهذا الخادم، قد يكون الخطأ مؤقتاً ويمكنك المحاولة مرة أخرى.</translation>
+ </message>
+ <message>
+ <source>Try again</source>
+ <translation>حاول مرة آخرى</translation>
+ </message>
+</context>
+<context>
+ <name>Job</name>
+ <message>
+ <source>Canceled</source>
+ <translation>ملغاة</translation>
+ </message>
+</context>
+<context>
+ <name>LocalPackageHub</name>
+ <message>
+ <source>%1 contains invalid content: %2</source>
+ <translation>%1 يحتوي على محتوى غير صالح: %2</translation>
+ </message>
+ <message>
+ <source>The file %1 does not exist.</source>
+ <translation>الملف %1 ليس موجوداً.</translation>
+ </message>
+ <message>
+ <source>Cannot open %1.</source>
+ <translation>لا يمكن فتح %1.</translation>
+ </message>
+ <message>
+ <source>Parse error in %1 at %2, %3: %4</source>
+ <translation>خطأ فرز في %1 في %2، %3، %4</translation>
+ </message>
+ <message>
+ <source>Root element %1 unexpected, should be &apos;Packages&apos;.</source>
+ <translation>عنصر الجذر %1 غير متوقع، يجب أن يكون &apos;Packages&apos;.</translation>
+ </message>
+</context>
+<context>
+ <name>LockFile</name>
+ <message>
+ <source>Cannot create lock file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء ملف القفل &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write PID to lock file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن كتابة PID لإقفال الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot obtain the lock for file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن الحصول على القفل للملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot release the lock for file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن فك القفل للملف &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::Task</name>
+ <message>
+ <source>%1 started</source>
+ <translation>بدأ %1</translation>
+ </message>
+ <message>
+ <source>%1 cannot be stopped</source>
+ <translation>لا يمكن إيقاف %1</translation>
+ </message>
+ <message>
+ <source>Cannot stop task %1</source>
+ <translation>لا يمكن إيقاف العملية %1</translation>
+ </message>
+ <message>
+ <source>%1 cannot be paused</source>
+ <translation>لا يمكن الإيقاف المؤقت لـ %1</translation>
+ </message>
+ <message>
+ <source>Cannot pause task %1</source>
+ <translation>لا يمكن الإيقاف المؤقت للعملية %1</translation>
+ </message>
+ <message>
+ <source>Cannot resume task %1</source>
+ <translation>لا يمكن استئناف العملية %1</translation>
+ </message>
+ <message>
+ <source>%1 done</source>
+ <translation>انتهى %1</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::UpdateFinder</name>
+ <message>
+ <source>Cannot access the package information of this application.</source>
+ <translation>لا يمكن الوصول إلى معلومات الحزمة لهذا التطبيق.</translation>
+ </message>
+ <message>
+ <source>No package sources set for this application.</source>
+ <translation>لم تُعين مصادر حزم لهذا التطبيق.</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n update(s) found.</source>
+ <translation>
+ <numerusform>عثر على %n تحديث(تحديثات).</numerusform>
+ <numerusform>عثر على تحديث.</numerusform>
+ <numerusform>عثر على تحديثين.</numerusform>
+ <numerusform>عثر على %n تحديثات.</numerusform>
+ <numerusform>عثر على %n تحديث.</numerusform>
+ <numerusform>عثر على %n تحديث.</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Downloading Updates.xml from update sources.</source>
+ <translation>جارٍ تنزيل ملف Updates.xml من مصادر التحديثات.</translation>
+ </message>
+ <message>
+ <source>Cannot download package source %1 from &quot;%2&quot;.</source>
+ <translation>لا يمكن تحميل مصدر الحزمة %1 من &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Updates.xml file(s) downloaded from update sources.</source>
+ <translation>نُزّل ملف (ملفات) Updates.xml من مصادر التحديث.</translation>
+ </message>
+ <message>
+ <source>Computing applicable updates.</source>
+ <translation>جارٍ حساب التحديثات القابلة للتطبيق.</translation>
+ </message>
+ <message>
+ <source>Application updates computed.</source>
+ <translation>حُسبت تحديثات التطبيق.</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::CopyOperation</name>
+ <message>
+ <source>Cannot backup file &quot;%1&quot;.</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot copy a non-existent file: %1</source>
+ <translation>لا يمكن نسخ ملف غير موجود: %1</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;:%2</translation>
+ </message>
+ <message>
+ <source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation>لا يمكن نسخ الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Cannot delete file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot; %2</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file into &quot;%1&quot;: %2</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية إلى &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::MoveOperation</name>
+ <message>
+ <source>Cannot backup file &quot;%1&quot;.</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;: %3</source>
+ <translation>لا يمكن نسخ الملف &quot;%1&quot; إلى &quot;%2&quot;: %3</translation>
+ </message>
+ <message>
+ <source>Cannot remove file &quot;%1&quot;.</source>
+ <translation>لا يمكن حذف الملف &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file for &quot;%1&quot;: %2</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية لـ &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::DeleteOperation</name>
+ <message>
+ <source>Cannot create backup of file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file for &quot;%1&quot;: %2</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية لـ &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::MkdirOperation</name>
+ <message>
+ <source>Cannot create directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء المجلد &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Unknown error.</source>
+ <translation>خطأ غير معروف.</translation>
+ </message>
+ <message>
+ <source>Cannot remove directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف المجلد &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::RmdirOperation</name>
+ <message>
+ <source>Cannot remove directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن حذف المجلد &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>The directory does not exist.</source>
+ <translation>المجلد غير موجود.</translation>
+ </message>
+ <message>
+ <source>Cannot recreate directory &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إعادة إنشاء المجلد &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::AppendFileOperation</name>
+ <message>
+ <source>Cannot backup file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot find backup file for &quot;%1&quot;.</source>
+ <translation>لا يمكن العثور على ملف النسخة الاحتياطية لـ &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file for &quot;%1&quot;.</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية لـ &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file for &quot;%1&quot;: %2</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية لـ &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::PrependFileOperation</name>
+ <message>
+ <source>Cannot backup file &quot;%1&quot;: %2</source>
+ <translation>لا يمكن إنشاء نسخة احتياطية من الملف &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot find backup file for &quot;%1&quot;.</source>
+ <translation>لا يمكن العثور على ملف النسخة الاحتياطية لـ &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file for &quot;%1&quot;.</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية لـ &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot restore backup file for &quot;%1&quot;: %2</source>
+ <translation>لا يمكن استعادة ملف النسخة الاحتياطية لـ &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>KDUpdater::UpdatesInfoData</name>
+ <message>
+ <source>Updates.xml contains invalid content: %1</source>
+ <translation>يشتمل ملف Updates.xml على محتوى غير صالح: %1</translation>
+ </message>
+ <message>
+ <source>Cannot read &quot;%1&quot;</source>
+ <translation>لا يمكن قراءة &quot;%1&apos;</translation>
+ </message>
+ <message>
+ <source>Parse error in %1 at %2, %3: %4</source>
+ <translation>خطأ في فرز %1 في %2، %3:%4</translation>
+ </message>
+ <message>
+ <source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
+ <translation>عنصر الجذر %1 غير متوقع، يجب أن يكون &quot;Updates&quot;.</translation>
+ </message>
+ <message>
+ <source>ApplicationName element is missing.</source>
+ <translation>عنصر ApplicationName مفقود.</translation>
+ </message>
+ <message>
+ <source>ApplicationVersion element is missing.</source>
+ <translation>عنصر ApplicationVersion مفقود.</translation>
+ </message>
+ <message>
+ <source>PackageUpdate element without Name</source>
+ <translation>عنصر PackageUpdate بدون Name</translation>
+ </message>
+ <message>
+ <source>PackageUpdate element without Version</source>
+ <translation>عنصر PackageUpdate بدون Version</translation>
+ </message>
+ <message>
+ <source>PackageUpdate element without ReleaseDate</source>
+ <translation>عنصر PackageUpdate بدون ReleaseDate</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::QFileDialogProxy</name>
+ <message>
+ <source>User input is required but the output device is not associated with a terminal.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>InstallerBase</name>
+ <message>
+ <source>Unable to start installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/sdk/translations/ifw_de.ts b/src/sdk/translations/ifw_de.ts
index 381c739da..39b541c8d 100644
--- a/src/sdk/translations/ifw_de.ts
+++ b/src/sdk/translations/ifw_de.ts
@@ -104,6 +104,13 @@
</message>
</context>
<context>
+ <name>InstallerBase</name>
+ <message>
+ <source>Unable to start installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>InstallerCalculator</name>
<message>
<source>Components added as automatic dependencies:</source>
@@ -703,7 +710,7 @@
</message>
<message>
<source>Cannot copy file from &quot;%1&quot; to &quot;%2&quot;: %3</source>
- <translation>Konnte Datei &quot;%1&quot; nicht nach &quot;%1&quot; kopieren: %3</translation>
+ <translation>Konnte Datei &quot;%1&quot; nicht nach &quot;%2&quot; kopieren: %3</translation>
</message>
<message>
<source>Cannot move file from &quot;%1&quot; to &quot;%2&quot;: %3</source>
@@ -723,7 +730,7 @@
</message>
<message>
<source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;: %3</source>
- <translation type="unfinished"></translation>
+ <translation>Konnte Datei &quot;%1&quot; nicht nach &quot;%2&quot; kopieren: %3</translation>
</message>
<message>
<source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;.</source>
@@ -766,7 +773,7 @@
</message>
<message>
<source>Cannot resolve isDefault in %1</source>
- <translation>Kann isDefault in %1 nicht auflösen.</translation>
+ <translation>Kann isDefault in %1 nicht auflösen</translation>
</message>
<message>
<source>There was an error loading the selected component. This component can not be installed.</source>
@@ -924,6 +931,17 @@
</message>
</context>
<context>
+ <name>QInstaller::ComponentSelectionPagePrivate</name>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Fehler</translation>
+ </message>
+</context>
+<context>
<name>QInstaller::ConsumeOutputOperation</name>
<message>
<source>&lt;to be saved installer key name&gt; &lt;executable&gt; [argument1] [argument2] [...]</source>
@@ -1213,6 +1231,13 @@ Fehler beim Laden von %2</translation>
</message>
</context>
<context>
+ <name>QInstaller::ExtractArchiveOperation</name>
+ <message>
+ <source>Extracting &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>QInstaller::ExtractArchiveOperation::Runnable</name>
<message>
<source>Cannot open archive &quot;%1&quot; for reading: %2</source>
@@ -1301,7 +1326,7 @@ Fehler beim Laden von %2</translation>
</message>
<message>
<source>Finished</source>
- <translation type="unfinished"></translation>
+ <translation>Abschließen</translation>
</message>
<message>
<source>Click %1 to exit the %2 Wizard.</source>
@@ -1516,7 +1541,7 @@ Fehler beim Laden von %2</translation>
</message>
<message>
<source>Retrieving meta information from remote repository... </source>
- <translation type="unfinished"></translation>
+ <translation>Metainformationen werden vom Installationsserver empfangen ... </translation>
</message>
<message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
@@ -1572,6 +1597,10 @@ Pakete werden heruntergeladen ...</translation>
<translation>Einige Komponenten konnten nicht vollständig entfernt werden, weil ein unbekannter Fehler aufgetreten ist.</translation>
</message>
<message>
+ <source>User input is required but the output device is not associated with a terminal.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>The directory you selected already exists and contains an installation. Choose a different target for installation.</source>
<translation>Der ausgewählte Verzeichnis existiert bereits und enthält eine Installation. Bitte wählen Sie einen anderen Zielordner aus.</translation>
</message>
@@ -1638,6 +1667,10 @@ Möchten Sie trotzdem fortsetzen?</translation>
<translation>Keine installierten Pakete gefunden.</translation>
</message>
<message>
+ <source>Cannot register component! Component with identifier %1 already exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Application running in Uninstaller mode.</source>
<translation>Die Anwendung läuft im Deinstallationsmodus.</translation>
</message>
@@ -1823,7 +1856,7 @@ Möchten Sie trotzdem fortsetzen?</translation>
</message>
<message>
<source>Cannot remove file &quot;%1&quot;: %2</source>
- <translation type="unfinished"></translation>
+ <translation>Konnte Datei &quot;%1&quot; nicht löschen: %2</translation>
</message>
<message>
<source>Cannot create directory &quot;%1&quot;.</source>
@@ -2064,7 +2097,7 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
</message>
<message>
<source>Installing</source>
- <translation type="unfinished"></translation>
+ <translation>Installieren</translation>
</message>
<message>
<source>Updating</source>
@@ -2107,6 +2140,13 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
</message>
</context>
<context>
+ <name>QInstaller::QFileDialogProxy</name>
+ <message>
+ <source>User input is required but the output device is not associated with a terminal.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>QInstaller::ReadyForInstallationPage</name>
<message>
<source>U&amp;ninstall</source>
@@ -2611,11 +2651,11 @@ als root aufrufen und dann &quot;Ok&quot; auswählen.</translation>
</message>
<message>
<source>Select All</source>
- <translation type="unfinished"></translation>
+ <translation>Alle auswählen</translation>
</message>
<message>
<source>Deselect All</source>
- <translation type="unfinished"></translation>
+ <translation>Alle abwählen</translation>
</message>
</context>
<context>
@@ -2667,29 +2707,4 @@ als root aufrufen und dann &quot;Ok&quot; auswählen.</translation>
<translation>Verbleibende Datei &quot;%1&quot; wegen &quot;%2&quot;: %3</translation>
</message>
</context>
-<context>
- <name>QInstaller::ComponentSelectionPagePrivate</name>
- <message>
- <source>Filter</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Error</source>
- <translation>Fehler</translation>
- </message>
-</context>
-<context>
- <name>QInstaller::ExtractArchiveOperation</name>
- <message>
- <source>Extracting &quot;%1&quot;</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>InstallerBase</name>
- <message>
- <source>Unable to start installer</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
</TS>
diff --git a/tests/auto/installer/binaryformat/tst_binaryformat.cpp b/tests/auto/installer/binaryformat/tst_binaryformat.cpp
index c9cd2b00f..7e0f5ed24 100644
--- a/tests/auto/installer/binaryformat/tst_binaryformat.cpp
+++ b/tests/auto/installer/binaryformat/tst_binaryformat.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -51,7 +51,7 @@ struct Layout : public QInstaller::BinaryLayout
class TestOperation : public KDUpdater::UpdateOperation
{
public:
- TestOperation(const QString &name)
+ explicit TestOperation(const QString &name)
: KDUpdater::UpdateOperation(nullptr)
{ setName(name); }
diff --git a/tests/auto/installer/clientserver/tst_clientserver.cpp b/tests/auto/installer/clientserver/tst_clientserver.cpp
index 23442d5fa..29d4283b9 100644
--- a/tests/auto/installer/clientserver/tst_clientserver.cpp
+++ b/tests/auto/installer/clientserver/tst_clientserver.cpp
@@ -501,7 +501,7 @@ private slots:
QFile file;
file.setFileName(filename);
file.open(QIODevice::ReadWrite);
- const QByteArray ba = file.readLine();
+ file.readLine();
file.seek(0);
QCOMPARE(file.atEnd(), false);
diff --git a/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z b/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z
deleted file mode 100644
index 5a9383e7e..000000000
--- a/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z.sha1 b/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z.sha1
deleted file mode 100644
index fd0bc548c..000000000
--- a/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1content.7z.sha1
+++ /dev/null
@@ -1 +0,0 @@
-643cb71b2337d5a49d57a5bc3c636ee9b84c0802 \ No newline at end of file
diff --git a/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1meta.7z b/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1meta.7z
deleted file mode 100644
index 6ef0b7959..000000000
--- a/tests/auto/installer/cliinterface/data/repository/A/1.0.2-1meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/AB/1.0.2-1meta.7z b/tests/auto/installer/cliinterface/data/repository/AB/1.0.2-1meta.7z
deleted file mode 100644
index 6b5e5ac91..000000000
--- a/tests/auto/installer/cliinterface/data/repository/AB/1.0.2-1meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z b/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z
deleted file mode 100644
index dfe41ad15..000000000
--- a/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z.sha1 b/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z.sha1
deleted file mode 100644
index 50a632b49..000000000
--- a/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1content.7z.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c7b9ab370efe036171dda7b71cd95021747cb101 \ No newline at end of file
diff --git a/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1meta.7z b/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1meta.7z
deleted file mode 100644
index 12d54f94c..000000000
--- a/tests/auto/installer/cliinterface/data/repository/B/1.0.0-1meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z b/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z
deleted file mode 100644
index 7d03dca9c..000000000
--- a/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z.sha1 b/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z.sha1
deleted file mode 100644
index 91ead97f0..000000000
--- a/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1content.7z.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c8b7076fabaaf6b9d27f27350c577118c24f426b \ No newline at end of file
diff --git a/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1meta.7z b/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1meta.7z
deleted file mode 100644
index 46bae0179..000000000
--- a/tests/auto/installer/cliinterface/data/repository/C/1.0.0-1meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/Updates.xml b/tests/auto/installer/cliinterface/data/repository/Updates.xml
index c7b11dc03..52707d09d 100644
--- a/tests/auto/installer/cliinterface/data/repository/Updates.xml
+++ b/tests/auto/installer/cliinterface/data/repository/Updates.xml
@@ -19,7 +19,7 @@
<Description>Example component B</Description>
<Version>1.0.0-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
- <Default>true</Default>
+ <Default>false</Default>
<UpdateFile CompressedSize="222" OS="Any" UncompressedSize="72"/>
<DownloadableArchives>content.7z</DownloadableArchives>
<SHA1>9170d55a6af81c1a6a63d708a4ab6ed359775cd9</SHA1>
@@ -35,6 +35,27 @@
<DownloadableArchives>content.7z</DownloadableArchives>
<SHA1>5b3939da1af492382c68388fc796837e4c36b876</SHA1>
</PackageUpdate>
+ <PackageUpdate>
+ <Name>C.virt</Name>
+ <DisplayName>Virtual subcomponent of C</DisplayName>
+ <Description>Example virtual component</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Virtual>true</Virtual>
+ <UpdateFile CompressedSize="222" OS="Any" UncompressedSize="72"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>5b3939da1af492382c68388fc796837e4c36b876</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>C.virt.subcomponent</Name>
+ <DisplayName>Subcomponent of virtual component</DisplayName>
+ <Description>Example subcomponent of virtual component</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <UpdateFile CompressedSize="222" OS="Any" UncompressedSize="72"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>5b3939da1af492382c68388fc796837e4c36b876</SHA1>
+ </PackageUpdate>
<PackageUpdate>
<Name>AB</Name>
<DisplayName>AB</DisplayName>
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z
deleted file mode 100644
index d7fbf4d8e..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z.sha1 b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z.sha1
deleted file mode 100644
index c5825de47..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0content.7z.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f33d2028e1c61061f7f29e5189f7d66800361dc2 \ No newline at end of file
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0meta.7z b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0meta.7z
deleted file mode 100644
index d783c7c1c..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/A/2.0.0meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/AB/1.0.2-1meta.7z b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/AB/1.0.2-1meta.7z
deleted file mode 100644
index 20f2aca2d..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/AB/1.0.2-1meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z
deleted file mode 100644
index bd0bc9352..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z.sha1 b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z.sha1
deleted file mode 100644
index e66535388..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0content.7z.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bb256f1eda0f452c7ab33d1f364a4fad062f0a73 \ No newline at end of file
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0meta.7z b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0meta.7z
deleted file mode 100644
index f92853734..000000000
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/B/2.0.0meta.7z
+++ /dev/null
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml
index e0aedffdd..f0c724db8 100644
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml
+++ b/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml
@@ -37,4 +37,14 @@
<SHA1>cfa136fa1d7a4196896c90af72d510727ba799ae</SHA1>
<Virtual>true</Virtual>
</PackageUpdate>
+ <PackageUpdate>
+ <Name>B.subcomponent</Name>
+ <DisplayName>Subcomponent of B</DisplayName>
+ <Description>Example non-virtual subcomponent of B</Description>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <UpdateFile CompressedSize="224" OS="Any" UncompressedSize="74"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>cfa136fa1d7a4196896c90af72d510727ba799ae</SHA1>
+ </PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/cliinterface/tst_cliinterface.cpp b/tests/auto/installer/cliinterface/tst_cliinterface.cpp
index 550f45da2..d9497d53f 100644
--- a/tests/auto/installer/cliinterface/tst_cliinterface.cpp
+++ b/tests/auto/installer/cliinterface/tst_cliinterface.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -89,6 +89,33 @@ private slots:
" <package name=\"C\" displayname=\"C\" version=\"1.0.0-1\"/>\n"
"</availablepackages>\n");
core->listAvailablePackages(QLatin1String("^C"));
+
+ // Test with filters
+ QTest::ignoreMessage(QtDebugMsg, "<availablepackages>\n"
+ " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
+ "</availablepackages>\n");
+ QHash<QString, QString> searchHash {
+ { "Version", "1.0.2" },
+ { "DisplayName", "A" }
+ };
+ core->listAvailablePackages(QString(), searchHash);
+
+ QTest::ignoreMessage(QtDebugMsg, "<availablepackages>\n"
+ " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
+ "</availablepackages>\n");
+ searchHash.clear();
+ searchHash.insert("Default", "false");
+ core->listAvailablePackages(QString(), searchHash);
+
+ // Need to change rules here to catch messages
+ QLoggingCategory::setFilterRules("ifw.* = true\n");
+
+ QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
+ core->listAvailablePackages(QLatin1String("C.virt"));
+
+ QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
+ core->listAvailablePackages(QLatin1String("C.virt.subcomponent"));
}
void testInstallPackageFails()
@@ -117,6 +144,11 @@ private slots:
<< QLatin1String("B")));
QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
+ QTest::ignoreMessage(QtDebugMsg, "Cannot install B.subcomponent. Component is descendant of a virtual component B.\n");
+ QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("B.subcomponent")));
+
+ QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
QTest::ignoreMessage(QtDebugMsg, "Cannot install MissingComponent. Component not found.\n");
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("MissingComponent")));
diff --git a/tests/auto/installer/contentshaupdate/contentshaupdate.pro b/tests/auto/installer/contentshaupdate/contentshaupdate.pro
new file mode 100644
index 000000000..84402794f
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/contentshaupdate.pro
@@ -0,0 +1,9 @@
+include(../../qttest.pri)
+
+QT += qml
+
+SOURCES += tst_contentshaupdate.cpp
+
+RESOURCES += \
+ settings.qrc \
+ ..\shared\config.qrc
diff --git a/tests/auto/installer/contentshaupdate/data/repository/Updates.xml b/tests/auto/installer/contentshaupdate/data/repository/Updates.xml
new file mode 100644
index 000000000..a83d41384
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repository/Updates.xml
@@ -0,0 +1,48 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>componentA</Name>
+ <DisplayName>Component A</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+ <UpdateFile OS="Any" CompressedSize="283" UncompressedSize="101"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentB</Name>
+ <DisplayName>Component B</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>90</SortingPriority>
+ <UpdateFile OS="Any" CompressedSize="283" UncompressedSize="101"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentC</Name>
+ <DisplayName>Component C</DisplayName>
+ <Description>Component C</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile OS="Any" CompressedSize="283" UncompressedSize="101"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentD</Name>
+ <DisplayName>Component D</DisplayName>
+ <Description>Component D</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile CompressedSize="283" UncompressedSize="101" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentshaupdate/data/repository/componentA/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repository/componentA/1.0.0content.7z
new file mode 100644
index 000000000..46a9f1d1e
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repository/componentA/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repository/componentB/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repository/componentB/1.0.0content.7z
new file mode 100644
index 000000000..5f1fb2e1b
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repository/componentB/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repository/componentC/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repository/componentC/1.0.0content.7z
new file mode 100644
index 000000000..83e82b5a3
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repository/componentC/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repository/componentD/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repository/componentD/1.0.0content.7z
new file mode 100644
index 000000000..da50742a4
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repository/componentD/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdate/Updates.xml b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/Updates.xml
new file mode 100644
index 000000000..87017cf91
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/Updates.xml
@@ -0,0 +1,48 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>componentA</Name>
+ <DisplayName>Component A</DisplayName>
+ <Description>Component A.</Description>
+ <Version>0.1.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+ <UpdateFile CompressedSize="283" UncompressedSize="101" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>5</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentB</Name>
+ <DisplayName>Component B</DisplayName>
+ <Description>Component B.</Description>
+ <Version>0.1.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>90</SortingPriority>
+ <UpdateFile CompressedSize="283" UncompressedSize="101" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentC</Name>
+ <DisplayName>Component C</DisplayName>
+ <Description>Component C</Description>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile CompressedSize="283" UncompressedSize="101" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentD</Name>
+ <DisplayName>Component D</DisplayName>
+ <Description>Component D</Description>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile UncompressedSize="101" CompressedSize="283" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentA/0.1.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentA/0.1.0content.7z
new file mode 100644
index 000000000..bdbabc7fd
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentA/0.1.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentB/0.1.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentB/0.1.0content.7z
new file mode 100644
index 000000000..1c1129b9f
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentB/0.1.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentC/2.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentC/2.0.0content.7z
new file mode 100644
index 000000000..7f75bf503
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentC/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentD/2.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentD/2.0.0content.7z
new file mode 100644
index 000000000..4207dfbf2
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdate/componentD/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/settings.qrc b/tests/auto/installer/contentshaupdate/settings.qrc
new file mode 100644
index 000000000..a5d045f46
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/settings.qrc
@@ -0,0 +1,14 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/repository/Updates.xml</file>
+ <file>data/repository/componentA/1.0.0content.7z</file>
+ <file>data/repository/componentB/1.0.0content.7z</file>
+ <file>data/repository/componentC/1.0.0content.7z</file>
+ <file>data/repository/componentD/1.0.0content.7z</file>
+ <file>data/repositoryUpdate/Updates.xml</file>
+ <file>data/repositoryUpdate/componentA/0.1.0content.7z</file>
+ <file>data/repositoryUpdate/componentB/0.1.0content.7z</file>
+ <file>data/repositoryUpdate/componentC/2.0.0content.7z</file>
+ <file>data/repositoryUpdate/componentD/2.0.0content.7z</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp b/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
new file mode 100644
index 000000000..d8d2f5377
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
@@ -0,0 +1,108 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 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 "../shared/packagemanager.h"
+#include "../shared/verifyinstaller.h"
+
+#include <QTest>
+
+using namespace QInstaller;
+
+class tst_ContentSha1Update : public QObject
+{
+ Q_OBJECT
+
+private:
+ void setRepository(const QString &repository)
+ {
+ core->reset();
+ core->cancelMetaInfoJob(); //Call cancel to reset metadata so that update repositories are fetched
+
+ QSet<Repository> repoList;
+ Repository repo = Repository::fromUserInput(repository);
+ repoList.insert(repo);
+ core->settings().setDefaultRepositories(repoList);
+ }
+
+private slots:
+ void initTestCase()
+ {
+ m_installDir = QInstaller::generateTemporaryFileName();
+ core = PackageManager::getPackageManagerWithInit(m_installDir);
+ }
+
+ void updateWithContentSha1_data()
+ {
+ QTest::addColumn<QString>("component");
+ QTest::addColumn<QString>("content");
+ QTest::addColumn<QString>("updatedContent");
+ QTest::addColumn<PackageManagerCore::Status>("expectedStatusAfterInstall");
+ QTest::addColumn<PackageManagerCore::Status>("expectedStatusAfterUpdate");
+
+ QTest::newRow("ContentSha1Change") << "componentA" << "1.0.0content.txt" << "0.1.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("NewContentSha1") << "componentB" << "1.0.0content.txt" << "0.1.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("SameContentSha1") << "componentC" << "1.0.0content.txt" << "1.0.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Canceled;
+ QTest::newRow("Sha1RemovedFromRepo") << "componentD" << "1.0.0content.txt" << "2.0.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Success;
+ }
+
+ void updateWithContentSha1()
+ {
+ QFETCH(QString, component);
+ QFETCH(QString, content);
+ QFETCH(QString, updatedContent);
+ QFETCH(PackageManagerCore::Status, expectedStatusAfterInstall);
+ QFETCH(PackageManagerCore::Status, expectedStatusAfterUpdate);
+
+ setRepository(":///data/repository");
+ QCOMPARE(expectedStatusAfterInstall, core->installSelectedComponentsSilently(QStringList() << component));
+ QCOMPARE(expectedStatusAfterInstall, core->status());
+ VerifyInstaller::verifyInstallerResources(m_installDir, component, content);
+
+ core->commitSessionOperations();
+ core->setPackageManager();
+ setRepository(":///data/repositoryUpdate");
+ QCOMPARE(expectedStatusAfterUpdate, core->updateComponentsSilently(QStringList()));
+ VerifyInstaller::verifyInstallerResources(m_installDir, component, updatedContent);
+ }
+
+ void cleanupTestCase()
+ {
+ QDir dir(m_installDir);
+ QVERIFY(dir.removeRecursively());
+ delete core;
+ }
+
+private:
+ QString m_installDir;
+ PackageManagerCore *core;
+};
+
+
+QTEST_MAIN(tst_ContentSha1Update)
+
+#include "tst_contentshaupdate.moc"
diff --git a/tests/auto/installer/factory/tst_factory.cpp b/tests/auto/installer/factory/tst_factory.cpp
index f44a6febe..eb7e2c36c 100644
--- a/tests/auto/installer/factory/tst_factory.cpp
+++ b/tests/auto/installer/factory/tst_factory.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -82,7 +82,7 @@ public:
: Food(amount)
, m_expireDate(expireDate)
{ qDebug().nospace().noquote() << "Constructor."; }
- QDate expireDate() const {
+ QDate expireDate() const Q_DECL_OVERRIDE {
return m_expireDate;
}
private:
@@ -92,7 +92,7 @@ private:
class Butter : public Food
{
public:
- QDate expireDate() const {
+ QDate expireDate() const Q_DECL_OVERRIDE {
return m_expireDate;
}
static Food *create(int amount, const QDate expireDate) {
@@ -117,7 +117,7 @@ public:
: Food(amount)
, m_expireDate(expireDate)
{ qDebug().nospace().noquote() << "Constructor."; }
- QDate expireDate() const {
+ QDate expireDate() const Q_DECL_OVERRIDE {
return m_expireDate;
}
diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro
index 114c43d7a..cb0438ab3 100644
--- a/tests/auto/installer/installer.pro
+++ b/tests/auto/installer/installer.pro
@@ -39,7 +39,8 @@ SUBDIRS += \
globalsettingsoperation \
elevatedexecuteoperation \
treename \
- createoffline
+ createoffline \
+ contentshaupdate
win32 {
SUBDIRS += registerfiletypeoperation \
diff --git a/tests/auto/installer/metadatajob/data/config.xml b/tests/auto/installer/metadatajob/data/config.xml
deleted file mode 100644
index 041ce5062..000000000
--- a/tests/auto/installer/metadatajob/data/config.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Installer>
- <Name>Your application</Name>
- <Version>1.2.3</Version>
- <MaintenanceToolName></MaintenanceToolName>
- <MaintenanceToolIniFile></MaintenanceToolIniFile>
- <TargetConfigurationFile></TargetConfigurationFile>
-</Installer>
diff --git a/tests/auto/installer/metadatajob/settings.qrc b/tests/auto/installer/metadatajob/settings.qrc
index c881f294b..6e56a7854 100644
--- a/tests/auto/installer/metadatajob/settings.qrc
+++ b/tests/auto/installer/metadatajob/settings.qrc
@@ -1,6 +1,5 @@
<RCC>
<qresource prefix="/">
- <file>data/config.xml</file>
<file>data/repository/Updates.xml</file>
<file>data/repositoryActionAdd/Updates.xml</file>
<file>data/repositoryActionRemove/Updates.xml</file>
diff --git a/tests/auto/installer/metadatajob/tst_metadatajob.cpp b/tests/auto/installer/metadatajob/tst_metadatajob.cpp
index ef7b42940..54609ab95 100644
--- a/tests/auto/installer/metadatajob/tst_metadatajob.cpp
+++ b/tests/auto/installer/metadatajob/tst_metadatajob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,7 +47,6 @@ class tst_MetaDataJob : public QObject
private slots:
void testRepository()
{
- Settings settings = Settings::fromFileAndPrefix(":///data/config.xml", ":///data");
PackageManagerCore core;
core.setInstaller();
QSet<Repository> repoList;
@@ -63,7 +62,6 @@ private slots:
void testRepositoryUpdateActionAdd()
{
- Settings settings = Settings::fromFileAndPrefix(":///data/config.xml", ":///data");
PackageManagerCore core;
core.setInstaller();
QSet<Repository> repoList;
@@ -82,7 +80,6 @@ private slots:
void testRepositoryUpdateActionRemove()
{
- Settings settings = Settings::fromFileAndPrefix(":///data/config.xml", ":///data");
PackageManagerCore core;
core.setInstaller();
QSet<Repository> repoList;
diff --git a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
index ae2c3b211..a3e86c2f8 100644
--- a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
+++ b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -46,7 +46,7 @@ using namespace QInstaller;
class DummyComponent : public Component
{
public:
- DummyComponent(PackageManagerCore *core)
+ explicit DummyComponent(PackageManagerCore *core)
: Component(core)
{
setCheckState(Qt::Checked);
@@ -306,7 +306,7 @@ private slots:
QTest::ignoreMessage(QtWarningMsg, re);
QTest::ignoreMessage(QtDebugMsg, "No updates available.");
- QCOMPARE(PackageManagerCore::Failure, core.updateComponentsSilently(QStringList()));
+ QCOMPARE(PackageManagerCore::Canceled, core.updateComponentsSilently(QStringList()));
QVERIFY(QDir().rmdir(testDirectory));
}
diff --git a/tests/auto/installer/registerfiletypeoperation/tst_registerfiletypeoperation.cpp b/tests/auto/installer/registerfiletypeoperation/tst_registerfiletypeoperation.cpp
index 5a4377d7b..3e3005621 100644
--- a/tests/auto/installer/registerfiletypeoperation/tst_registerfiletypeoperation.cpp
+++ b/tests/auto/installer/registerfiletypeoperation/tst_registerfiletypeoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -80,7 +80,6 @@ private slots:
QInstaller::init();
QString randomString = "";
const QString possible = "abcdefghijklmnopqrstuvwxyz0123456789";
- qsrand(QTime::currentTime().msec());
for (int i = 0; i < 5; i++) {
int index = QRandomGenerator::global()->generate() % possible.length();
QChar nextChar = possible.at(index);
diff --git a/tests/auto/tools/repotest/settings.qrc b/tests/auto/tools/repotest/settings.qrc
index e731af222..a0b5743fe 100644
--- a/tests/auto/tools/repotest/settings.qrc
+++ b/tests/auto/tools/repotest/settings.qrc
@@ -31,5 +31,29 @@
<file>repository_unite/C/1.0.0content.7z.sha1</file>
<file>repository_unite/2020-11-10-0931_meta.7z</file>
<file>repository_unite/Updates.xml</file>
+
+ <file>test_package_versions/repository_1/A/2.0.0content.7z</file>
+ <file>test_package_versions/repository_1/A/2.0.0content.7z.sha1</file>
+ <file>test_package_versions/repository_1/A/2.0.0meta.7z</file>
+ <file>test_package_versions/repository_1/B/1.0.0content.7z</file>
+ <file>test_package_versions/repository_1/B/1.0.0content.7z.sha1</file>
+ <file>test_package_versions/repository_1/B/1.0.0meta.7z</file>
+ <file>test_package_versions/repository_1/Updates.xml</file>
+
+ <file>test_package_versions/repository_2/A/1.0.0content.7z</file>
+ <file>test_package_versions/repository_2/A/1.0.0content.7z.sha1</file>
+ <file>test_package_versions/repository_2/A/1.0.0meta.7z</file>
+ <file>test_package_versions/repository_2/B/1.0.0content.7z</file>
+ <file>test_package_versions/repository_2/B/1.0.0content.7z.sha1</file>
+ <file>test_package_versions/repository_2/B/1.0.0meta.7z</file>
+ <file>test_package_versions/repository_2/Updates.xml</file>
+
+ <file>test_package_versions/repository_3/A/1.0.0content.7z</file>
+ <file>test_package_versions/repository_3/A/1.0.0content.7z.sha1</file>
+ <file>test_package_versions/repository_3/A/1.0.0meta.7z</file>
+ <file>test_package_versions/repository_3/B/3.0.0content.7z</file>
+ <file>test_package_versions/repository_3/B/3.0.0content.7z.sha1</file>
+ <file>test_package_versions/repository_3/B/3.0.0meta.7z</file>
+ <file>test_package_versions/repository_3/Updates.xml</file>
</qresource>
</RCC>
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z b/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z
new file mode 100644
index 000000000..46e2c9124
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z.sha1 b/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z.sha1
new file mode 100644
index 000000000..53010cea2
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0content.7z.sha1
@@ -0,0 +1 @@
+059e5ed8cd3a1fbca08cccfa4075265192603e3f \ No newline at end of file
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0meta.7z b/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0meta.7z
new file mode 100644
index 000000000..af0863696
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/A/2.0.0meta.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z b/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z
new file mode 100644
index 000000000..46e2c9124
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z.sha1 b/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z.sha1
new file mode 100644
index 000000000..53010cea2
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0content.7z.sha1
@@ -0,0 +1 @@
+059e5ed8cd3a1fbca08cccfa4075265192603e3f \ No newline at end of file
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0meta.7z b/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0meta.7z
new file mode 100644
index 000000000..e5be1148e
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/B/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml b/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml
new file mode 100644
index 000000000..dfae8c8bd
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml
@@ -0,0 +1,28 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component A</Description>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <Script>script2.0.0.qs</Script>
+ <UpdateFile UncompressedSize="40" CompressedSize="72" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>da6dae55a8cc3fb9f012e33fa7b9c187a823aa9a</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="40" CompressedSize="72" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>d88b373458b4bbec38132692061bf1e7aa68e7f7</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z b/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z
new file mode 100644
index 000000000..46e2c9124
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z.sha1 b/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z.sha1
new file mode 100644
index 000000000..53010cea2
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0content.7z.sha1
@@ -0,0 +1 @@
+059e5ed8cd3a1fbca08cccfa4075265192603e3f \ No newline at end of file
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0meta.7z b/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0meta.7z
new file mode 100644
index 000000000..c4da482bf
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/A/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z b/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z
new file mode 100644
index 000000000..46e2c9124
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z.sha1 b/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z.sha1
new file mode 100644
index 000000000..53010cea2
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0content.7z.sha1
@@ -0,0 +1 @@
+059e5ed8cd3a1fbca08cccfa4075265192603e3f \ No newline at end of file
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0meta.7z b/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0meta.7z
new file mode 100644
index 000000000..e5be1148e
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/B/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_2/Updates.xml b/tests/auto/tools/repotest/test_package_versions/repository_2/Updates.xml
new file mode 100644
index 000000000..ac12fba4b
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_2/Updates.xml
@@ -0,0 +1,28 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component A</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <Script>script1.0.0.qs</Script>
+ <UpdateFile UncompressedSize="40" CompressedSize="72" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>da6dae55a8cc3fb9f012e33fa7b9c187a823aa9a</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="40" CompressedSize="72" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>d88b373458b4bbec38132692061bf1e7aa68e7f7</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z b/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z
new file mode 100644
index 000000000..46e2c9124
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z.sha1 b/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z.sha1
new file mode 100644
index 000000000..53010cea2
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0content.7z.sha1
@@ -0,0 +1 @@
+059e5ed8cd3a1fbca08cccfa4075265192603e3f \ No newline at end of file
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0meta.7z b/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0meta.7z
new file mode 100644
index 000000000..3706e8dd8
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/A/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z b/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z
new file mode 100644
index 000000000..46e2c9124
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z.sha1 b/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z.sha1
new file mode 100644
index 000000000..53010cea2
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0content.7z.sha1
@@ -0,0 +1 @@
+059e5ed8cd3a1fbca08cccfa4075265192603e3f \ No newline at end of file
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0meta.7z b/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0meta.7z
new file mode 100644
index 000000000..e5be1148e
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/B/3.0.0meta.7z
Binary files differ
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_3/Updates.xml b/tests/auto/tools/repotest/test_package_versions/repository_3/Updates.xml
new file mode 100644
index 000000000..40d86538c
--- /dev/null
+++ b/tests/auto/tools/repotest/test_package_versions/repository_3/Updates.xml
@@ -0,0 +1,28 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component A</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <Script>script1.0.0.qs</Script>
+ <UpdateFile UncompressedSize="40" CompressedSize="72" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>da6dae55a8cc3fb9f012e33fa7b9c187a823aa9a</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>3.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="40" CompressedSize="72" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>d88b373458b4bbec38132692061bf1e7aa68e7f7</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/tools/repotest/tst_repotest.cpp b/tests/auto/tools/repotest/tst_repotest.cpp
index 5aa614454..02594f400 100644
--- a/tests/auto/tools/repotest/tst_repotest.cpp
+++ b/tests/auto/tools/repotest/tst_repotest.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,114 +39,63 @@ class tst_repotest : public QObject
{
Q_OBJECT
private:
- // TODO generateRepo() is almost direct copy from repogen.cpp.
- // Move the needed parts of repogen.cpp to a usable function for easier maintenance.
- void generateRepo(bool createSplitMetadata, bool createUnifiedMetadata, bool updateNewComponents)
+ void generateRepo(bool createSplitMetadata, bool createUnifiedMetadata, bool updateNewComponents,
+ QStringList packagesUpdatedWithSha = QStringList())
{
QStringList filteredPackages;
- QInstallerTools::FilterType filterType = QInstallerTools::Exclude;
- QInstallerTools::PackageInfoVector precompressedPackages = QInstallerTools::createListOfRepositoryPackages
- (m_repositoryDirectories, &filteredPackages, filterType);
- m_packages.append(precompressedPackages);
-
- QInstallerTools::PackageInfoVector preparedPackages = QInstallerTools::createListOfPackages(m_packagesDirectories,
- &filteredPackages, filterType);
- m_packages.append(preparedPackages);
-
- if (updateNewComponents)
- QInstallerTools::filterNewComponents(m_repositoryDir, m_packages);
- QHash<QString, QString> pathToVersionMapping = QInstallerTools::buildPathToVersionMapping(m_packages);
-
- foreach (const QInstallerTools::PackageInfo &package, m_packages) {
- const QFileInfo fi(m_repositoryDir, package.name);
- if (fi.exists())
- removeDirectory(fi.absoluteFilePath());
- }
+ m_packages = QInstallerTools::collectPackages(m_repoInfo,
+ &filteredPackages, QInstallerTools::Exclude, updateNewComponents, packagesUpdatedWithSha);
if (updateNewComponents) { //Verify that component B exists as that is not updated
if (createSplitMetadata) {
- VerifyInstaller::verifyFileExistence(m_repositoryDir + "/B", QStringList() << "1.0.0content.7z"
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir + "/B", QStringList() << "1.0.0content.7z"
<< "1.0.0content.7z.sha1" << "1.0.0meta.7z");
} else {
- VerifyInstaller::verifyFileExistence(m_repositoryDir + "/B", QStringList() << "1.0.0content.7z"
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir + "/B", QStringList() << "1.0.0content.7z"
<< "1.0.0content.7z.sha1");
}
} else {
- QDir dir(m_repositoryDir + "/B");
+ QDir dir(m_repoInfo.repositoryDir + "/B");
QVERIFY(!dir.exists());
}
- QStringList directories;
- directories.append(m_packagesDirectories);
- directories.append(m_repositoryDirectories);
-
- QStringList unite7zFiles;
- foreach (const QString &repositoryDirectory, m_repositoryDirectories) {
- QDirIterator it(repositoryDirectory, QStringList(QLatin1String("*_meta.7z"))
- , QDir::Files | QDir::CaseSensitive);
- while (it.hasNext()) {
- it.next();
- unite7zFiles.append(it.fileInfo().absoluteFilePath());
- }
- }
-
- QInstallerTools::copyComponentData(directories, m_repositoryDir, &m_packages);
- QInstallerTools::copyMetaData(m_tmpMetaDir, m_repositoryDir, m_packages, QLatin1String("{AnyApplication}"),
- QLatin1String("1.0.0"), unite7zFiles);
- QString existing7z = QInstallerTools::existingUniteMeta7z(m_repositoryDir);
- if (!existing7z.isEmpty())
- existing7z = m_repositoryDir + QDir::separator() + existing7z;
- QInstallerTools::compressMetaDirectories(m_tmpMetaDir, existing7z, pathToVersionMapping,
- createSplitMetadata, createUnifiedMetadata);
- QDirIterator it(m_repositoryDir, QStringList(QLatin1String("Updates*.xml"))
- << QLatin1String("*_meta.7z"), QDir::Files | QDir::CaseSensitive);
- while (it.hasNext()) {
- it.next();
- QFile::remove(it.fileInfo().absoluteFilePath());
- }
- QInstaller::moveDirectoryContents(m_tmpMetaDir, m_repositoryDir);
- }
-
- void generateTempMetaDir()
- {
- if (!m_tmpMetaDir.isEmpty())
- m_tempDirDeleter.releaseAndDelete(m_tmpMetaDir);
QTemporaryDir tmp;
tmp.setAutoRemove(false);
- m_tmpMetaDir = tmp.path();
- m_tempDirDeleter.add(m_tmpMetaDir);
+ const QString tmpMetaDir = tmp.path();
+ QInstallerTools::createRepository(m_repoInfo, &m_packages, tmpMetaDir, createSplitMetadata,
+ createUnifiedMetadata);
+ QInstaller::removeDirectory(tmpMetaDir, true);
}
void clearData()
{
- generateTempMetaDir();
- m_packagesDirectories.clear();
- m_repositoryDirectories.clear();
+ m_repoInfo.packages.clear();
+ m_repoInfo.repositoryPackages.clear();
m_packages.clear();
}
void initRepoUpdate()
{
clearData();
- m_packagesDirectories << ":///packages_update";
+ m_repoInfo.packages << ":///packages_update";
}
- void initRepoUpdateFromRepository(const QString &repository)
+ void initRepoUpdateFromRepositories(const QStringList &repositories)
{
clearData();
- m_repositoryDirectories << repository;
+ m_repoInfo.repositoryPackages << repositories;
}
void verifyUniteMetadata(const QString &scriptVersion)
{
- QString fileContent = VerifyInstaller::fileContent(m_repositoryDir + QDir::separator()
+ QString fileContent = VerifyInstaller::fileContent(m_repoInfo.repositoryDir + QDir::separator()
+ "Updates.xml");
QRegularExpression re("<MetadataName>(.*)<.MetadataName>");
QStringList matches = re.match(fileContent).capturedTexts();
- QString existingUniteMeta7z = QInstallerTools::existingUniteMeta7z(m_repositoryDir);
+ QString existingUniteMeta7z = QInstallerTools::existingUniteMeta7z(m_repoInfo.repositoryDir);
QCOMPARE(2, matches.count());
QCOMPARE(existingUniteMeta7z, matches.at(1));
- QFile file(m_repositoryDir + QDir::separator() + matches.at(1));
+ QFile file(m_repoInfo.repositoryDir + QDir::separator() + matches.at(1));
QVERIFY(file.open(QIODevice::ReadOnly));
//We have script<version>.qs for package A in the unite metadata
@@ -159,35 +108,35 @@ private:
QCOMPARE(qPrintable(fileName.arg(scriptVersion)), fileIt->path);
}
- VerifyInstaller::verifyFileExistence(m_repositoryDir, QStringList() << "Updates.xml"
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir, QStringList() << "Updates.xml"
<< matches.at(1));
- VerifyInstaller::verifyFileContent(m_repositoryDir + QDir::separator() + "Updates.xml",
+ VerifyInstaller::verifyFileContent(m_repoInfo.repositoryDir + QDir::separator() + "Updates.xml",
"SHA1");
- VerifyInstaller::verifyFileContent(m_repositoryDir + QDir::separator() + "Updates.xml",
+ VerifyInstaller::verifyFileContent(m_repoInfo.repositoryDir + QDir::separator() + "Updates.xml",
"MetadataName");
}
- void verifyComponentRepository(const QString &componentAVersion, bool hasComponentMeta)
+ void verifyComponentRepository(const QString &componentAVersion, const QString &componentBVersion, bool hasComponentMeta)
{
const QString content = "%1content.7z";
- const QString contentSha = "%1content.7z.sha1";
+ const QString contentSha1 = "%1content.7z.sha1";
const QString meta = "%1meta.7z";
QStringList componentA;
QStringList componentB;
- componentA << qPrintable(content.arg(componentAVersion)) << qPrintable(contentSha.arg(componentAVersion));
- componentB << "1.0.0content.7z" << "1.0.0content.7z.sha1";
+ componentA << qPrintable(content.arg(componentAVersion)) << qPrintable(contentSha1.arg(componentAVersion));
+ componentB << qPrintable(content.arg(componentBVersion)) << qPrintable(contentSha1.arg(componentBVersion));
if (hasComponentMeta) {
componentA << qPrintable(meta.arg(componentAVersion));
- componentB << "1.0.0meta.7z";
+ componentB << qPrintable(meta.arg(componentBVersion));
}
- VerifyInstaller::verifyFileExistence(m_repositoryDir + "/A", componentA);
- VerifyInstaller::verifyFileExistence(m_repositoryDir + "/B", componentB);
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir + "/A", componentA);
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir + "/B", componentB);
}
void verifyComponentMetaUpdatesXml()
{
- VerifyInstaller::verifyFileExistence(m_repositoryDir, QStringList() << "Updates.xml");
- VerifyInstaller::verifyFileHasNoContent(m_repositoryDir + QDir::separator() + "Updates.xml",
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir, QStringList() << "Updates.xml");
+ VerifyInstaller::verifyFileHasNoContent(m_repoInfo.repositoryDir + QDir::separator() + "Updates.xml",
"MetadataName");
}
@@ -216,18 +165,18 @@ private:
foreach (const QString &fileName, contentFiles) {
message = "Copying file from \":///%5/%1/%2%4\" to \"%3/%1/%2%4\"";
QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(component).arg(version)
- .arg(m_repositoryDir).arg(fileName).arg(repository)));
+ .arg(m_repoInfo.repositoryDir).arg(fileName).arg(repository)));
}
}
- void ignoreMessageForCollectingRepository(const QString &repository)
+ void ignoreMessageForCollectingRepository(const QStringList &repositories)
{
const QString message = "Process repository \"%2\"";
QTest::ignoreMessage(QtDebugMsg, "Collecting information about available repository packages...");
- QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(repository)));
+ for (auto &repo : repositories)
+ QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(repo)));
QTest::ignoreMessage(QtDebugMsg, "Collecting information about available packages...");
QTest::ignoreMessage(QtDebugMsg, "No available packages found at the specified location.");
- QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"A\" - \"2.0.0\"");
}
void ignoreMessagesForCopyMetadata(const QString &component, bool hasMeta, bool update)
@@ -303,9 +252,23 @@ private:
void ignoreMessageForUpdateComponent()
{
QString message = "Update component \"A\" in \"%1\" .";
- QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(m_repositoryDir)));
+ QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(m_repoInfo.repositoryDir)));
message = "Update component \"C\" in \"%1\" .";
- QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(m_repositoryDir)));
+ QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(m_repoInfo.repositoryDir)));
+ }
+
+ void verifyComponentShaUpdate(int shaUpdateComponents)
+ {
+ QString updatesXmlFile(m_repoInfo.repositoryDir + QDir::separator() + "Updates.xml");
+ QFile file(updatesXmlFile);
+ QDomDocument dom;
+
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QVERIFY(dom.setContent(&file));
+ file.close();
+ QCOMPARE(dom.elementsByTagName("ContentSha1").count(), shaUpdateComponents);
+ VerifyInstaller::verifyFileContent(updatesXmlFile,
+ "<ContentSha1>059e5ed8cd3a1fbca08cccfa4075265192603e3f</ContentSha1>");
}
private slots:
@@ -313,11 +276,10 @@ private slots:
{
ignoreMessageForCollectingPackages("1.0.0", "1.0.0");
- m_repositoryDir = QInstallerTools::makePathAbsolute(QInstaller::generateTemporaryFileName());
- m_tempDirDeleter.add(m_repositoryDir);
- generateTempMetaDir();
+ m_repoInfo.repositoryDir = QInstallerTools::makePathAbsolute(QInstaller::generateTemporaryFileName());
+ m_tempDirDeleter.add(m_repoInfo.repositoryDir);
- m_packagesDirectories << ":///packages";
+ m_repoInfo.packages << ":///packages";
ignoreMessagesForComponentHash(QStringList() << "A" << "B", false);
ignoreMessagesForCopyMetadata("A", true, false); //Only A has metadata
@@ -334,7 +296,7 @@ private slots:
ignoreMessagesForComponentSha(QStringList () << "A" << "B", false);
generateRepo(true, false, false);
- verifyComponentRepository("1.0.0", true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
verifyComponentMetaUpdatesXml();
}
@@ -344,7 +306,7 @@ private slots:
ignoreMessagesForUniteMeta(false);
generateRepo(true, true, false);
- verifyComponentRepository("1.0.0", true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
verifyUniteMetadata("1.0.0");
}
@@ -353,16 +315,36 @@ private slots:
ignoreMessagesForUniteMeta(false);
generateRepo(false, true, false);
- verifyComponentRepository("1.0.0", false);
+ verifyComponentRepository("1.0.0", "1.0.0", false);
verifyUniteMetadata("1.0.0");
}
+ void testWithComponentShaUpdate()
+ {
+ ignoreMessagesForComponentSha(QStringList () << "A" << "B", false);
+ generateRepo(true, false, false, QStringList () << "A");
+
+ verifyComponentRepository("1.0.0", "1.0.0", true);
+ verifyComponentMetaUpdatesXml();
+ verifyComponentShaUpdate(1);
+ }
+
+ void testWithTwoComponentsShaUpdate()
+ {
+ ignoreMessagesForComponentSha(QStringList () << "A" << "B", false);
+ generateRepo(true, false, false, QStringList () << "A" << "B");
+
+ verifyComponentRepository("1.0.0", "1.0.0", true);
+ verifyComponentMetaUpdatesXml();
+ verifyComponentShaUpdate(2);
+ }
+
void testUpdateNewComponents()
{
// Create 'base' repository which will be updated
ignoreMessagesForComponentSha(QStringList() << "A" << "B", false);
generateRepo(true, false, false);
- verifyComponentRepository("1.0.0", true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
// Update the 'base' repository
initRepoUpdate();
@@ -371,9 +353,9 @@ private slots:
ignoreMessagesForComponentHash(QStringList() << "A", true);
ignoreMessagesForCopyMetadata("A", true, true);
const QString &message = "Update component \"A\" in \"%1\" .";
- QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(m_repositoryDir)));
+ QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(m_repoInfo.repositoryDir)));
generateRepo(true, false, true);
- verifyComponentRepository("2.0.0", true);
+ verifyComponentRepository("2.0.0", "1.0.0", true);
verifyComponentMetaUpdatesXml();
}
@@ -381,12 +363,12 @@ private slots:
{
ignoreMessagesForComponentSha(QStringList() << "A" << "B", false);
generateRepo(true, false, false);
- verifyComponentRepository("1.0.0",true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
initRepoUpdate();
ignoreMessagesForUpdateComponents();
generateRepo(true, false, false);
- verifyComponentRepository("2.0.0", true);
+ verifyComponentRepository("2.0.0", "1.0.0", true);
verifyComponentMetaUpdatesXml();
}
@@ -395,13 +377,13 @@ private slots:
ignoreMessagesForComponentSha(QStringList() << "A" << "B", false);
ignoreMessagesForUniteMeta(false);
generateRepo(true, true, false);
- verifyComponentRepository("1.0.0", true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
initRepoUpdate();
ignoreMessagesForUpdateComponents();
ignoreMessagesForUniteMeta(true);
generateRepo(true, true, false);
- verifyComponentRepository("2.0.0", true);
+ verifyComponentRepository("2.0.0", "1.0.0", true);
verifyUniteMetadata("2.0.0");
}
@@ -409,7 +391,7 @@ private slots:
{
ignoreMessagesForUniteMeta(false);
generateRepo(false, true, false);
- verifyComponentRepository("1.0.0", false);
+ verifyComponentRepository("1.0.0", "1.0.0", false);
initRepoUpdate();
ignoreMessageForCollectingPackages("2.0.0", "1.0.0");
@@ -418,7 +400,7 @@ private slots:
ignoreMessagesForCopyMetadata("B", false, true);
ignoreMessagesForUniteMeta(true);
generateRepo(false, true, false);
- verifyComponentRepository("2.0.0", false);
+ verifyComponentRepository("2.0.0", "1.0.0", false);
verifyUniteMetadata("2.0.0");
}
@@ -426,17 +408,47 @@ private slots:
{
ignoreMessagesForComponentSha(QStringList() << "A" << "B", false);
generateRepo(true, false, false);
- verifyComponentRepository("1.0.0", true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
- initRepoUpdateFromRepository(":///repository_component");
- ignoreMessageForCollectingRepository("repository_component");
+ initRepoUpdateFromRepositories(QStringList() << ":///repository_component");
+ ignoreMessageForCollectingRepository(QStringList() << "repository_component");
+ QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"A\" - \"2.0.0\"");
QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"B\" - \"1.0.0\"");
ignoreMessagesForCopyRepository("B", "1.0.0", "repository_component");
ignoreMessagesForCopyRepository("A", "2.0.0", "repository_component");
ignoreMessagesForComponentSha(QStringList() << "A" << "B", true);
generateRepo(true, false, false);
- verifyComponentRepository("2.0.0", true);
+ verifyComponentRepository("2.0.0", "1.0.0", true);
+ verifyComponentMetaUpdatesXml();
+ }
+
+ void testUpdateComponentsFromMultipleRepositories()
+ {
+ ignoreMessagesForComponentSha(QStringList() << "A" << "B", false);
+ generateRepo(true, false, false);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
+
+ initRepoUpdateFromRepositories(QStringList() << ":///test_package_versions/repository_1"
+ << ":///test_package_versions/repository_2" << ":///test_package_versions/repository_3");
+
+ ignoreMessageForCollectingRepository(QStringList()
+ << "repository_1" << "repository_2" << "repository_3");
+
+ QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"A\" - \"2.0.0\"");
+ QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"B\" - \"1.0.0\"");
+
+ QTest::ignoreMessage(QtDebugMsg, "- it provides an old version of the package \"A\" - \"1.0.0\" - ignored");
+ QTest::ignoreMessage(QtDebugMsg, "- it provides an old version of the package \"B\" - \"1.0.0\" - ignored");
+
+ QTest::ignoreMessage(QtDebugMsg, "- it provides an old version of the package \"A\" - \"1.0.0\" - ignored");
+ QTest::ignoreMessage(QtDebugMsg, "- it provides a new version of the package \"B\" - \"3.0.0\" - replaced");
+
+ ignoreMessagesForCopyRepository("B", "3.0.0", "test_package_versions/repository_3");
+ ignoreMessagesForCopyRepository("A", "2.0.0", "test_package_versions/repository_1");
+ ignoreMessagesForComponentSha(QStringList() << "A" << "B", true);
+ generateRepo(true, false, false);
+ verifyComponentRepository("2.0.0", "3.0.0", true);
verifyComponentMetaUpdatesXml();
}
@@ -445,10 +457,11 @@ private slots:
ignoreMessagesForComponentSha(QStringList() << "A" << "B", false);
ignoreMessagesForUniteMeta(false);
generateRepo(true, true, false);
- verifyComponentRepository("1.0.0", true);
+ verifyComponentRepository("1.0.0", "1.0.0", true);
- initRepoUpdateFromRepository(":///repository_componentAndUnite");
- ignoreMessageForCollectingRepository("repository_componentAndUnite");
+ initRepoUpdateFromRepositories(QStringList() << ":///repository_componentAndUnite");
+ ignoreMessageForCollectingRepository(QStringList() << "repository_componentAndUnite");
+ QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"A\" - \"2.0.0\"");
QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"C\" - \"1.0.0\"");
ignoreMessageForUpdateComponent();
ignoreMessagesForCopyRepository("A", "2.0.0", "repository_componentAndUnite");
@@ -457,8 +470,8 @@ private slots:
ignoreMessagesForComponentSha(QStringList() << "A" << "C", true);
generateRepo(true, true, true);
- verifyComponentRepository("2.0.0", true);
- VerifyInstaller::verifyFileExistence(m_repositoryDir + "/C", QStringList() << "1.0.0content.7z" << "1.0.0content.7z.sha1" << "1.0.0meta.7z");
+ verifyComponentRepository("2.0.0", "1.0.0", true);
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir + "/C", QStringList() << "1.0.0content.7z" << "1.0.0content.7z.sha1" << "1.0.0meta.7z");
verifyUniteMetadata("2.0.0");
}
@@ -466,10 +479,11 @@ private slots:
{
ignoreMessagesForUniteMeta(false);
generateRepo(false, true, false);
- verifyComponentRepository("1.0.0", false);
+ verifyComponentRepository("1.0.0", "1.0.0", false);
- initRepoUpdateFromRepository(":///repository_unite");
- ignoreMessageForCollectingRepository("repository_unite");
+ initRepoUpdateFromRepositories(QStringList() << ":///repository_unite");
+ ignoreMessageForCollectingRepository(QStringList() << "repository_unite");
+ QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"A\" - \"2.0.0\"");
QTest::ignoreMessage(QtDebugMsg, "- it provides the package \"C\" - \"1.0.0\"");
ignoreMessageForUpdateComponent();
ignoreMessagesForCopyRepository("A", "2.0.0", "repository_unite");
@@ -477,24 +491,21 @@ private slots:
ignoreMessagesForUniteMeta(true);
generateRepo(false, true, true);
- verifyComponentRepository("2.0.0", false);
- VerifyInstaller::verifyFileExistence(m_repositoryDir + "/C", QStringList() << "1.0.0content.7z" << "1.0.0content.7z.sha1");
+ verifyComponentRepository("2.0.0", "1.0.0", false);
+ VerifyInstaller::verifyFileExistence(m_repoInfo.repositoryDir + "/C", QStringList() << "1.0.0content.7z" << "1.0.0content.7z.sha1");
verifyUniteMetadata("2.0.0");
}
void cleanup()
{
m_tempDirDeleter.releaseAndDeleteAll();
- m_packagesDirectories.clear();
+ m_repoInfo.packages.clear();
m_packages.clear();
- m_repositoryDirectories.clear();
+ m_repoInfo.repositoryPackages.clear();
}
private:
- QString m_tmpMetaDir;
- QString m_repositoryDir;
- QStringList m_packagesDirectories;
- QStringList m_repositoryDirectories;
+ QInstallerTools::RepositoryInfo m_repoInfo;
QInstallerTools::PackageInfoVector m_packages;
TempDirDeleter m_tempDirDeleter;
};
diff --git a/tools/devtool/binarydump.cpp b/tools/devtool/binarydump.cpp
index bd8e9053c..bb0700149 100644
--- a/tools/devtool/binarydump.cpp
+++ b/tools/devtool/binarydump.cpp
@@ -117,10 +117,10 @@ int BinaryDump::dump(const QInstaller::ResourceCollectionManager &manager, const
if ((!isOpen) && (!resource->open()))
continue; // TODO: should we throw here?
- QFile target(targetDir.filePath(name) + QDir::separator()
+ QFile targetFile(targetDir.filePath(name) + QDir::separator()
+ QString::fromUtf8(resource->name()));
- QInstaller::openForWrite(&target);
- resource->copyData(&target); // copy the 7z files into the target directory
+ QInstaller::openForWrite(&targetFile);
+ resource->copyData(&targetFile); // copy the 7z files into the target directory
if (!isOpen) // If we reach that point, either the resource was opened already...
resource->close(); // or we did open it and have to close it again.
diff --git a/tools/devtool/main.cpp b/tools/devtool/main.cpp
index 52db7d8b3..8da6dfb75 100644
--- a/tools/devtool/main.cpp
+++ b/tools/devtool/main.cpp
@@ -182,31 +182,31 @@ int main(int argc, char *argv[])
quint64 cookie = QInstaller::BinaryContent::MagicCookie;
try {
{
- QFile tmp(path);
- QInstaller::openForRead(&tmp);
+ QFile tmpFile(path);
+ QInstaller::openForRead(&tmpFile);
- if (!tmp.seek(QInstaller::BinaryContent::findMagicCookie(&tmp, cookie) - sizeof(qint64)))
+ if (!tmpFile.seek(QInstaller::BinaryContent::findMagicCookie(&tmpFile, cookie) - sizeof(qint64)))
throw QInstaller::Error(QLatin1String("Cannot seek to read magic marker."));
QInstaller::BinaryLayout layout;
- layout.magicMarker = QInstaller::retrieveInt64(&tmp);
+ layout.magicMarker = QInstaller::retrieveInt64(&tmpFile);
if (layout.magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) {
- QFileInfo fi(path);
+ QFileInfo fileInfo(path);
- QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath);
- fi.setFile(bundlePath);
+ QInstaller::isInBundle(fileInfo.absoluteFilePath(), &bundlePath);
+ fileInfo.setFile(bundlePath);
- path = fi.absolutePath() + QLatin1Char('/') + fi.baseName() + QLatin1String(".dat");
+ path = fileInfo.absolutePath() + QLatin1Char('/') + fileInfo.baseName() + QLatin1String(".dat");
- tmp.close();
- tmp.setFileName(path);
- QInstaller::openForRead(&tmp);
+ tmpFile.close();
+ tmpFile.setFileName(path);
+ QInstaller::openForRead(&tmpFile);
cookie = QInstaller::BinaryContent::MagicCookieDat;
}
- layout = QInstaller::BinaryContent::binaryLayout(&tmp, cookie);
- tmp.close();
+ layout = QInstaller::BinaryContent::binaryLayout(&tmpFile, cookie);
+ tmpFile.close();
if (command == QLatin1String("update")) {
BinaryReplace br(layout); // To update the binary we do not need any mapping.
diff --git a/tools/repocompare/repositorymanager.cpp b/tools/repocompare/repositorymanager.cpp
index 3fb863eaf..e4405d103 100644
--- a/tools/repocompare/repositorymanager.cpp
+++ b/tools/repocompare/repositorymanager.cpp
@@ -120,6 +120,7 @@ void RepositoryManager::createRepositoryMap(const QByteArray &data, QMap<QString
currentDescription.updateText.clear();
currentDescription.version.clear();
currentDescription.checksum.clear();
+ currentDescription.update = false;
}
if (reader.name() == QLatin1String("SHA1"))
currentDescription.checksum = reader.readElementText();
diff --git a/tools/repogen/repogen.cpp b/tools/repogen/repogen.cpp
index a88867e92..b630f8cd3 100644
--- a/tools/repogen/repogen.cpp
+++ b/tools/repogen/repogen.cpp
@@ -44,8 +44,6 @@
#include <iostream>
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
using namespace QInstaller;
@@ -100,8 +98,8 @@ int main(int argc, char** argv)
QStringList filteredPackages;
bool updateExistingRepository = false;
- QStringList packagesDirectories;
- QStringList repositoryDirectories;
+ QInstallerTools::RepositoryInfo repoInfo;
+ QStringList packagesUpdatedWithSha;
QInstallerTools::FilterType filterType = QInstallerTools::Exclude;
bool remove = false;
bool updateExistingRepositoryWithNewComponents = false;
@@ -168,7 +166,7 @@ int main(int argc, char** argv)
"Error: Package directory is empty"));
}
- packagesDirectories.append(args.first());
+ repoInfo.packages.append(args.first());
args.removeFirst();
} else if (args.first() == QLatin1String("--repository")) {
args.removeFirst();
@@ -181,7 +179,7 @@ int main(int argc, char** argv)
return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller",
"Error: Only local filesystem repositories now supported"));
}
- repositoryDirectories.append(args.first());
+ repoInfo.repositoryPackages.append(args.first());
args.removeFirst();
} else if (args.first() == QLatin1String("--ignore-translations")
|| args.first() == QLatin1String("--ignore-invalid-packages")) {
@@ -195,14 +193,17 @@ int main(int argc, char** argv)
} else if (args.first() == QLatin1String("--component-metadata")) {
createUnifiedMetadata = false;
args.removeFirst();
- }
- else {
+ } else if (args.first() == QLatin1String("--sha-update") || args.first() == QLatin1String("-s")) {
+ args.removeFirst();
+ packagesUpdatedWithSha = args.first().split(QLatin1Char(','));
+ args.removeFirst();
+ } else {
printUsage();
return 1;
}
}
- if ((packagesDirectories.isEmpty() && repositoryDirectories.isEmpty()) || (args.count() != 1)) {
+ if ((repoInfo.packages.isEmpty() && repoInfo.repositoryPackages.isEmpty()) || (args.count() != 1)) {
printUsage();
return 1;
}
@@ -213,12 +214,12 @@ int main(int argc, char** argv)
"Argument -r|--remove and --update|--update-new-components are mutually exclusive!"));
}
- const QString repositoryDir = QInstallerTools::makePathAbsolute(args.first());
+ repoInfo.repositoryDir = QInstallerTools::makePathAbsolute(args.first());
if (remove)
- QInstaller::removeDirectory(repositoryDir);
+ QInstaller::removeDirectory(repoInfo.repositoryDir);
if (updateExistingRepositoryWithNewComponents) {
- QStringList meta7z = QDir(repositoryDir).entryList(QStringList()
+ QStringList meta7z = QDir(repoInfo.repositoryDir).entryList(QStringList()
<< QLatin1String("*_meta.7z"), QDir::Files);
if (!meta7z.isEmpty()) {
throw QInstaller::Error(QCoreApplication::translate("QInstaller",
@@ -228,72 +229,27 @@ int main(int argc, char** argv)
}
}
- if (!update && QFile::exists(repositoryDir) && !QDir(repositoryDir).entryList(
+ if (!update && QFile::exists(repoInfo.repositoryDir) && !QDir(repoInfo.repositoryDir).entryList(
QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) {
throw QInstaller::Error(QCoreApplication::translate("QInstaller",
- "Repository target directory \"%1\" already exists.").arg(QDir::toNativeSeparators(repositoryDir)));
- }
-
- 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) {
- QInstallerTools::filterNewComponents(repositoryDir, packages);
- if (packages.isEmpty()) {
- std::cout << QString::fromLatin1("Cannot find new components to update \"%1\".")
- .arg(repositoryDir) << std::endl;
- return EXIT_SUCCESS;
- }
+ "Repository target directory \"%1\" already exists.").arg(QDir::toNativeSeparators(repoInfo.repositoryDir)));
}
- QHash<QString, QString> pathToVersionMapping = QInstallerTools::buildPathToVersionMapping(packages);
-
- foreach (const QInstallerTools::PackageInfo &package, packages) {
- const QFileInfo fi(repositoryDir, package.name);
- if (fi.exists())
- removeDirectory(fi.absoluteFilePath());
+ QInstallerTools::PackageInfoVector packages = QInstallerTools::collectPackages(repoInfo,
+ &filteredPackages, filterType, updateExistingRepositoryWithNewComponents, packagesUpdatedWithSha);
+ if (packages.isEmpty()) {
+ std::cout << QString::fromLatin1("Cannot find components to update \"%1\".")
+ .arg(repoInfo.repositoryDir) << std::endl;
+ return EXIT_SUCCESS;
}
QTemporaryDir tmp;
tmp.setAutoRemove(false);
tmpMetaDir = tmp.path();
- QStringList directories;
- directories.append(packagesDirectories);
- directories.append(repositoryDirectories);
- QStringList unite7zFiles;
- foreach (const QString &repositoryDirectory, repositoryDirectories) {
- QDirIterator it(repositoryDirectory, QStringList(QLatin1String("*_meta.7z"))
- , QDir::Files | QDir::CaseSensitive);
- while (it.hasNext()) {
- it.next();
- unite7zFiles.append(it.fileInfo().absoluteFilePath());
- }
- }
- QInstallerTools::copyComponentData(directories, repositoryDir, &packages);
- QInstallerTools::copyMetaData(tmpMetaDir, repositoryDir, packages, QLatin1String("{AnyApplication}"),
- QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)), unite7zFiles);
-
- QString existing7z = QInstallerTools::existingUniteMeta7z(repositoryDir);
- if (!existing7z.isEmpty())
- existing7z = repositoryDir + QDir::separator() + existing7z;
- QInstallerTools::compressMetaDirectories(tmpMetaDir, existing7z, pathToVersionMapping,
- createComponentMetadata, createUnifiedMetadata);
-
- QDirIterator it(repositoryDir, QStringList(QLatin1String("Updates*.xml"))
- << QLatin1String("*_meta.7z"), QDir::Files | QDir::CaseSensitive);
- while (it.hasNext()) {
- it.next();
- QFile::remove(it.fileInfo().absoluteFilePath());
- }
- QInstaller::moveDirectoryContents(tmpMetaDir, repositoryDir);
+ QInstallerTools::createRepository(repoInfo, &packages, tmpMetaDir,
+ createComponentMetadata, createUnifiedMetadata);
+
exitCode = EXIT_SUCCESS;
} catch (const Lib7z::SevenZipException &e) {
std::cerr << "Caught 7zip exception: " << e.message() << std::endl;