summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.qmake.conf2
-rw-r--r--INSTALL2
-rw-r--r--coin/instructions/make_instructions.yaml22
-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/includes/installerfw-examples-configuring.qdocinc22
-rw-r--r--doc/includes/installerfw-examples-generating.qdocinc2
-rw-r--r--doc/includes/installerfw-examples-packaging.qdocinc10
-rw-r--r--doc/installerfw-getting-started.qdoc70
-rw-r--r--doc/installerfw-overview.qdoc26
-rw-r--r--doc/installerfw.qdoc22
-rw-r--r--doc/noninteractive.qdoc15
-rw-r--r--doc/scripting-api/gui.qdoc7
-rw-r--r--doc/scripting.qdoc24
-rw-r--r--doc/tutorial.qdoc100
-rw-r--r--examples/doc/changeuserinterface.qdoc26
-rw-r--r--installerfw.pri9
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp2
-rw-r--r--src/libs/3rdparty/libarchive/archive.h6
-rw-r--r--src/libs/3rdparty/libarchive/archive_digest.c16
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry.c14
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry.h4
-rw-r--r--src/libs/3rdparty/libarchive/archive_hmac.c29
-rw-r--r--src/libs/3rdparty/libarchive/archive_hmac_private.h7
-rw-r--r--src/libs/3rdparty/libarchive/archive_platform.h3
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_posix.c7
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_windows.c18
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c2
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c2
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c8
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_cab.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c2
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_lha.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c24
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_rar.c10
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c9
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_tar.c20
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_xar.c4
-rw-r--r--src/libs/3rdparty/libarchive/archive_string.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_write.c8
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_posix.c4
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_windows.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_pax.c2
-rw-r--r--src/libs/3rdparty/libarchive/filter_fork_posix.c2
-rw-r--r--src/libs/3rdparty/libarchive/qt_attribution.json2
-rw-r--r--src/libs/ifwtools/binarycreator.cpp14
-rw-r--r--src/libs/ifwtools/binarycreator.h2
-rw-r--r--src/libs/ifwtools/rcc/rcc.cpp12
-rw-r--r--src/libs/ifwtools/repositorygen.cpp87
-rw-r--r--src/libs/ifwtools/repositorygen.h3
-rw-r--r--src/libs/installer/abstracttask.h2
-rw-r--r--src/libs/installer/binaryformatengine.cpp16
-rw-r--r--src/libs/installer/calculatorbase.cpp76
-rw-r--r--src/libs/installer/calculatorbase.h84
-rw-r--r--src/libs/installer/commandlineparser.cpp2
-rw-r--r--src/libs/installer/component.cpp274
-rw-r--r--src/libs/installer/component.h13
-rw-r--r--src/libs/installer/component_p.cpp9
-rw-r--r--src/libs/installer/component_p.h6
-rw-r--r--src/libs/installer/componentmodel.cpp8
-rw-r--r--src/libs/installer/componentmodel.h4
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp253
-rw-r--r--src/libs/installer/componentselectionpage_p.h14
-rw-r--r--src/libs/installer/concurrentoperationrunner.cpp2
-rw-r--r--src/libs/installer/constants.h58
-rw-r--r--src/libs/installer/createdesktopentryoperation.cpp2
-rw-r--r--src/libs/installer/createlinkoperation.cpp2
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp28
-rw-r--r--src/libs/installer/downloadarchivesjob.h7
-rw-r--r--src/libs/installer/elevatedexecuteoperation.cpp9
-rw-r--r--src/libs/installer/fileutils.cpp10
-rw-r--r--src/libs/installer/genericdatacache.cpp74
-rw-r--r--src/libs/installer/genericdatacache.h5
-rw-r--r--src/libs/installer/globals.cpp12
-rw-r--r--src/libs/installer/globals.h17
-rw-r--r--src/libs/installer/installer.pro7
-rw-r--r--src/libs/installer/installercalculator.cpp94
-rw-r--r--src/libs/installer/installercalculator.h40
-rw-r--r--src/libs/installer/lib7z_create.h3
-rw-r--r--src/libs/installer/lib7z_facade.cpp2
-rw-r--r--src/libs/installer/libarchivearchive.cpp2
-rw-r--r--src/libs/installer/licenseoperation.cpp4
-rw-r--r--src/libs/installer/link.cpp2
-rw-r--r--src/libs/installer/loggingutils.cpp6
-rw-r--r--src/libs/installer/loggingutils.h2
-rw-r--r--src/libs/installer/messageboxhandler.cpp3
-rw-r--r--src/libs/installer/metadata.cpp40
-rw-r--r--src/libs/installer/metadata.h4
-rw-r--r--src/libs/installer/metadatajob.cpp295
-rw-r--r--src/libs/installer/metadatajob.h10
-rw-r--r--src/libs/installer/metadatajob_p.h75
-rw-r--r--src/libs/installer/packagemanagercore.cpp135
-rw-r--r--src/libs/installer/packagemanagercore.h18
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp75
-rw-r--r--src/libs/installer/packagemanagercore_p.h6
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp6
-rw-r--r--src/libs/installer/packagemanagergui.cpp44
-rw-r--r--src/libs/installer/packagemanagergui.h1
-rw-r--r--src/libs/installer/performinstallationform.cpp6
-rw-r--r--src/libs/installer/permissionsettings.cpp8
-rw-r--r--src/libs/installer/qprocesswrapper.cpp3
-rw-r--r--src/libs/installer/qtpatch.cpp6
-rw-r--r--src/libs/installer/remoteclient_p.h5
-rw-r--r--src/libs/installer/remoteobject.h4
-rw-r--r--src/libs/installer/remoteserverconnection.cpp10
-rw-r--r--src/libs/installer/repository.cpp34
-rw-r--r--src/libs/installer/repository.h4
-rw-r--r--src/libs/installer/repositorycategory.cpp2
-rw-r--r--src/libs/installer/runextensions.h374
-rw-r--r--src/libs/installer/scriptengine.cpp35
-rw-r--r--src/libs/installer/scriptengine.h5
-rw-r--r--src/libs/installer/settings.cpp8
-rw-r--r--src/libs/installer/settings.h2
-rw-r--r--src/libs/installer/sysinfo_win.cpp2
-rw-r--r--src/libs/installer/uninstallercalculator.cpp86
-rw-r--r--src/libs/installer/uninstallercalculator.h37
-rw-r--r--src/libs/installer/utils.cpp4
-rw-r--r--src/libs/installer/utils.h2
-rw-r--r--src/libs/kdtools/filedownloader.h2
-rw-r--r--src/libs/kdtools/kdsysinfo_win.cpp12
-rw-r--r--src/libs/kdtools/lockfile_win.cpp2
-rw-r--r--src/libs/kdtools/sysinfo_x11.cpp8
-rw-r--r--src/libs/kdtools/updatefinder.cpp402
-rw-r--r--src/libs/kdtools/updatefinder.h80
-rw-r--r--src/libs/kdtools/updateoperation.cpp2
-rw-r--r--src/libs/kdtools/updatesinfo.cpp244
-rw-r--r--src/libs/kdtools/updatesinfo_p.h4
-rw-r--r--src/libs/kdtools/updatesinfodata_p.h11
-rw-r--r--src/sdk/commandlineinterface.cpp2
-rw-r--r--src/sdk/installerbase.cpp4
-rw-r--r--src/sdk/main.cpp12
-rw-r--r--src/sdk/sdk.pro2
-rw-r--r--src/sdk/sdkapp.h5
-rw-r--r--src/sdk/settingsdialog.cpp7
-rw-r--r--src/sdk/settingsdialog.h2
-rw-r--r--src/sdk/settingsdialog.ui36
-rw-r--r--src/sdk/tabcontroller.cpp26
-rw-r--r--src/sdk/translations/ifw_ar.ts44
-rw-r--r--src/sdk/translations/ifw_ca.ts22
-rw-r--r--src/sdk/translations/ifw_da.ts20
-rw-r--r--src/sdk/translations/ifw_de.ts44
-rw-r--r--src/sdk/translations/ifw_es.ts44
-rw-r--r--src/sdk/translations/ifw_fr.ts44
-rw-r--r--src/sdk/translations/ifw_hr.ts20
-rw-r--r--src/sdk/translations/ifw_hu.ts40
-rw-r--r--src/sdk/translations/ifw_it.ts20
-rw-r--r--src/sdk/translations/ifw_ja.ts44
-rw-r--r--src/sdk/translations/ifw_ko.ts44
-rw-r--r--src/sdk/translations/ifw_pl.ts24
-rw-r--r--src/sdk/translations/ifw_pt.ts44
-rw-r--r--src/sdk/translations/ifw_pt_BR.ts24
-rw-r--r--src/sdk/translations/ifw_ru.ts24
-rw-r--r--src/sdk/translations/ifw_zh_CN.ts44
-rw-r--r--tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp3
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml10
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7zbin0 -> 209 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7zbin0 -> 1010 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/settings.qrc2
-rw-r--r--tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp70
-rw-r--r--tests/auto/installer/componentmodel/data/updates.xml2
-rw-r--r--tests/auto/installer/componentmodel/tst_componentmodel.cpp15
-rw-r--r--tests/auto/installer/contentsha1check/contentsha1check.pro9
-rw-r--r--tests/auto/installer/contentsha1check/data/config.xml8
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml27
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7zbin0 -> 148 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml27
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml27
-rw-r--r--tests/auto/installer/contentsha1check/settings.qrc18
-rw-r--r--tests/auto/installer/contentsha1check/tst_contentsha1check.cpp181
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml28
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml27
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7zbin0 -> 227 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/settings.qrc6
-rw-r--r--tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp28
-rw-r--r--tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp10
-rw-r--r--tests/auto/installer/installer.pro3
-rw-r--r--tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7zbin0 -> 1057 bytes
-rw-r--r--tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7zbin0 -> 931 bytes
-rw-r--r--tests/auto/installer/metadatajob/settings.qrc2
-rw-r--r--tests/auto/installer/metadatajob/tst_metadatajob.cpp45
-rw-r--r--tests/auto/installer/scriptengine/tst_scriptengine.cpp52
-rw-r--r--tests/auto/installer/solver/tst_solver.cpp50
-rw-r--r--tools/repogen/repogen.cpp2
202 files changed, 3129 insertions, 2159 deletions
diff --git a/.gitignore b/.gitignore
index 26cbc3220..2f5e116d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
core
.qmake.cache
.qmake.vars
+.qmake.stash
*.prl
tags
.DS_Store
@@ -90,4 +91,8 @@ doc-build
.metadata
*.qtc_clangd
+doc/codeattributions.qdoc
+/doc/qdoc_wrapper.sh
+/src/sdk/installerbase.qrc
/src/sdk/translations/untranslated.ts
+/src/sdk/translations/ifw_en.ts
diff --git a/.qmake.conf b/.qmake.conf
index 4109ad569..95e84750e 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,2 +1,2 @@
-VERSION=4.5.2
+VERSION=4.6.0
CONFIG=prepare_docs qt_docs_targets $$CONFIG
diff --git a/INSTALL b/INSTALL
index 412b5063a..39d0057c2 100644
--- a/INSTALL
+++ b/INSTALL
@@ -37,7 +37,7 @@ nmake (or 'mingw32-make') module-qtbase module-qtdeclarative module-qttools modu
Recommended configuration options for Linux:
-configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev
+configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-feature-gssapi -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev
Build Qt:
make module-qtbase module-qtdeclarative module-qttools module-qttranslations
### macOS
diff --git a/coin/instructions/make_instructions.yaml b/coin/instructions/make_instructions.yaml
index 13bfe736a..b11d0161c 100644
--- a/coin/instructions/make_instructions.yaml
+++ b/coin/instructions/make_instructions.yaml
@@ -77,13 +77,13 @@ 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}}/IfwInstaller --target-name QtInstallerFramework-linux-x64-4.5.2.run"
+ command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-linux-x64-4.6.0.run"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.5.2.run"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.5.2.run"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.6.0.run"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.6.0.run"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -97,7 +97,7 @@ 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}}/IfwInstaller --target-name QtInstallerFramework-macOS-x64-4.5.2.app"
+ command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-macOS-x64-4.6.0.app"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
@@ -117,18 +117,18 @@ instructions:
maxTimeBetweenOutput: 600
userMessageOnFailure: "Failed to install Pipenv requirements"
- type: ExecuteCommand
- command: "python3 -m pipenv run python sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.5.2.app"
+ command: "python3 -m pipenv run python sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.6.0.app"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to sign the ifw installer"
- type: ExecuteCommand
- command: "python3 -m pipenv run python notarize.py --dmg={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.5.2.dmg"
+ command: "python3 -m pipenv run python notarize.py --dmg={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.6.0.dmg"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to notarize the ifw installer"
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.5.2.dmg"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.5.2.dmg"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.6.0.dmg"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.6.0.dmg"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -142,7 +142,7 @@ 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}}\\IfwInstaller --target-name QtInstallerFramework-windows-x64-4.5.2"
+ command: "{{.Env.PYTHON3_PATH}}\\python {{.SourceDir}}\\coin\\create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir C:\\{{.SourceDir}}\\IfwInstaller --target-name QtInstallerFramework-windows-x64-4.6.0"
maxTimeInSeconds: 1200
maxTimeBetweenOutput: 1200
userMessageOnFailure: "Failed to create ifw installer."
@@ -163,8 +163,8 @@ instructions:
equals_value: Windows
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.5.2.exe"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.5.2.exe"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.6.0.exe"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.6.0.exe"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: property
diff --git a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml
index eb838e1b3..1ccc2f96e 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.5.2</Version>
- <ReleaseDate>2022-01-13</ReleaseDate>
+ <Version>4.6.0</Version>
+ <ReleaseDate>2023-01-30</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 b0dd4ee57..93f4aa7d2 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.5.2</Version>
- <ReleaseDate>2022-01-13</ReleaseDate>
+ <Version>4.6.0</Version>
+ <ReleaseDate>2023-01-30</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/includes/installerfw-examples-configuring.qdocinc b/doc/includes/installerfw-examples-configuring.qdocinc
index d89ace6a5..afbeef6a8 100644
--- a/doc/includes/installerfw-examples-configuring.qdocinc
+++ b/doc/includes/installerfw-examples-configuring.qdocinc
@@ -4,18 +4,18 @@
specifies the text and default values used in the installer:
\list
- \li The \c <Name> element specifies the application name that is added
+ \li The \c <Name> element sets the application name and adds it
to the page name and introduction text.
- \li The \c <Version> element specifies the application version number.
- \li The \c <Title> element specifies the installer name displayed on the
- title bar.
- \li The \c <Publisher> element specifies the publisher of the software
+ \li The \c <Version> element sets the application version number.
+ \li The \c <Title> element sets the installer name and displays it on
+ the title bar.
+ \li The \c <Publisher> element sets the publisher of the software
(as shown in the Windows Control Panel, for example).
- \li The \c <StartMenuDir> element specifies the name of the default
+ \li The \c <StartMenuDir> element sets the name of the default
program group for the product in the Windows \gui Start menu.
- \li The \c <TargetDir> element specifies that the default target
- directory is located in the \c IfwExamples directory in the home directory
- of the current user (because the predefined variable\c @HomeDir@ is
- used as a part of the value). For more information, see
- \l{Predefined Variables}.
+ \li The \c <TargetDir> element sets the default target directory
+ location to be within the \c IfwExamples directory in the home
+ directory of the current user (because it uses the pre-existing
+ variable \c, @HomeDir@, as part of the value). For more information,
+ see \l{Predefined Variables}.
\endlist
diff --git a/doc/includes/installerfw-examples-generating.qdocinc b/doc/includes/installerfw-examples-generating.qdocinc
index 65ef313b5..12b0d20b5 100644
--- a/doc/includes/installerfw-examples-generating.qdocinc
+++ b/doc/includes/installerfw-examples-generating.qdocinc
@@ -14,4 +14,4 @@
\endcode
\endlist
- The installer is created in the current directory.
+ This creates the installer to the current directory.
diff --git a/doc/includes/installerfw-examples-packaging.qdocinc b/doc/includes/installerfw-examples-packaging.qdocinc
index f8111629a..260b7e23e 100644
--- a/doc/includes/installerfw-examples-packaging.qdocinc
+++ b/doc/includes/installerfw-examples-packaging.qdocinc
@@ -4,12 +4,12 @@
directory specifies the components that are available for installation:
\list
- \li The \c <DisplayName> element specifies the human-readable name of
+ \li The \c <DisplayName> element sets the human-readable name of
the component.
- \li The \c <Description> element specifies the human-readable
+ \li The \c <Description> element sets the human-readable
description of the component.
- \li The \c <Version> element specifies the version number of the
+ \li The \c <Version> element sets the version number of the
component.
- \li The \c <ReleaseDate> element specifies the date when this component
- version was released.
+ \li The \c <ReleaseDate> element sets the date of release for this
+ component version.
\endlist
diff --git a/doc/installerfw-getting-started.qdoc b/doc/installerfw-getting-started.qdoc
index f3fe3f0d6..8607164a0 100644
--- a/doc/installerfw-getting-started.qdoc
+++ b/doc/installerfw-getting-started.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,23 +32,15 @@
\title Getting Started
- Qt Installer Framework is developed as part of the Qt project. The
- framework itself uses Qt. However, it can be used to install all kind of
- applications, including (but not limited to) applications built with Qt.
+ You can use the Qt Installer Framework to create installation programs for
+ all kinds of applications, including (but not limited to) applications built
+ with Qt.
\section1 Supported Platforms
You can use the Qt Installer Framework to create installers for all
platforms supported by \l[QtDoc]{Supported Platforms}{desktop Qt}.
- The installers have been tested on the following platforms:
-
- \list
- \li Microsoft Windows 7, and later
- \li Ubuntu Linux 18.04, and later
- \li macOS 10.13, and later
- \endlist
-
\section1 Building from Sources
The following steps describe how to build the Qt Installer Framework
@@ -57,7 +49,7 @@
\section2 Supported Compilers
- The Qt Installer Framework can be compiled with Microsoft Visual Studio
+ You can compile the Qt Installer Framework with Microsoft Visual Studio
2015 and newer, GCC 5 and newer, and Clang 11.0.0 and newer. Currently, the
tested combination for Windows is Qt 5.15.2 with MSVC 2015 (Windows 10).
@@ -65,16 +57,15 @@
If you use a statically built Qt to build the Qt Installer Framework
you do not have to deliver Qt libraries, which enables you to distribute
- installers as one file. Please read SSL Import and Export Restrictions
- from http://doc.qt.io/qt-5/ssl.html if you are statically linking against
- OpenSSL libraries.
+ installers as one file. For more information about statically linking
+ against OpenSSL libraries, see \l{http://doc.qt.io/qt-5/ssl.html}{SSL
+ Import and Export Restrictions}.
The supported Qt version is 5.15.2.
\section3 Configuring Qt for Windows
- We recommend that you use the following options when you configure Qt for
- Windows:
+ Use the following configuration options for Windows:
\code
configure -prefix %CD%\qtbase -release -static -static-runtime -accessibility -no-icu -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests
@@ -88,10 +79,10 @@
\section3 Configuring Qt for Linux
- We recommend that you use the following configuration options for Linux:
+ Use the following configuration options for Linux:
\code
- configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev
+ configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-feature-gssapi -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev
\endcode
Build Qt:
@@ -101,7 +92,7 @@
\section3 Configuring Qt for macOS
- We recommend that you use the following configuration options for macOS:
+ Use the following configuration options for macOS:
\code
configure -prefix $PWD/qtbase -release -static -no-securetransport -accessibility -qt-zlib -qt-libpng -qt-libjpeg -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -no-freetype
@@ -115,34 +106,36 @@
\section2 Third Party Dependencies
The Qt Installer Framework sources contain a redistribution of parts of the
- libarchive compression and archive library, which requires you to link against
- additional libraries; \c liblzma, \c zlib, \c libbzip2, and on macOS, \c libiconv.
+ \c libarchive compression and archive library, which requires you to link
+ against the following libraries; \c liblzma, \c zlib, \c libbzip2, and on
+ macOS, \c libiconv.
- The usage of libarchive is recommended and can be enabled by adding the libarchive
- configuration feature to the list of values specified by the \c CONFIG variable. Installers
- created with this configuration support the (de)compression of 7zip, zip, and tar archive
- files, with gzip, bzip2, and xz as available compression methods.
+ To enable the use of \c libarchive, add the \c libarchive configuration
+ feature to the list of values specified by the \c CONFIG variable.
+ Installers created with this configuration support the creating and
+ extracting of 7zip, zip, and tar archive files, with \c gzip, bzip2, and
+ \c xz as available compression methods.
\code
qmake CONFIG+=libarchive
\endcode
- The \c IFW_ZLIB_LIBRARY, \c IFW_BZIP2_LIBRARY, \c IFW_LZMA_LIBRARY, and \c IFW_ICONV_LIBRARY
- variables can be used to specify the exact library files if required.
+ You can use the \c IFW_ZLIB_LIBRARY, \c IFW_BZIP2_LIBRARY, \c IFW_LZMA_LIBRARY, and \c IFW_ICONV_LIBRARY
+ variables to specify the exact library files.
- If the Qt version used to build the Qt Installer Framework was configured with \c{-qt-zlib} and
- \c IFW_ZLIB_LIBRARY variable is empty, libarchive will attempt to use the \c zlib library compiled
+ If you add the \c{-qt-zlib} configuration to the Qt version used to build the Qt Installer Framework, and
+ \c IFW_ZLIB_LIBRARY variable is empty, \c libarchive will try to use the \c zlib library compiled
into the QtCore module, which removes the need for an external library.
- If you do not enable libarchive support, the builtin LZMA SDK library will be used as a fallback and
- installation of the additional dependencies can be skipped, but created installers will
+ If you do not enable \c libarchive support, the builtin LZMA SDK library will act as a fallback and
+ installation of the extra dependencies will not occur, but created installers will
only support the 7zip format.
\note Building IFW with LZMA SDK is deprecated and may not be available in future versions.
\section3 Installing Dependencies for Windows
- The source archives for the dependencies can be downloaded from:
+ You can download the source archives for the dependencies from:
\list
\li \l https://tukaani.org/xz/
@@ -171,7 +164,7 @@
The easiest way to install the missing libraries is with a third party
package manager solution, like Homebrew or MacPorts. On macOS 10.15 you
- should only need to additionally install the liblzma library.
+ should only need to additionally install the \c liblzma library.
On Homebrew this would be:
@@ -181,9 +174,10 @@
\section3 Troubleshooting
- For libarchive related compilation errors, you may need to edit the definitions in
- a configuration header file respective to your platform, which can be found from the
- 'src/libs/3rdparty/libarchive/config/' directory of the Installer Framework sources.
+ For \c libarchive related compilation errors, you may need to edit the definitions in
+ a configuration header file respective to your platform. You can find this file in
+ the \c src/libs/3rdparty/libarchive/config/ directory of the Installer Framework sources.
+
\section2 Setting up Qt Installer Framework
diff --git a/doc/installerfw-overview.qdoc b/doc/installerfw-overview.qdoc
index f5b86e679..7589aa915 100644
--- a/doc/installerfw-overview.qdoc
+++ b/doc/installerfw-overview.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,28 +34,28 @@
The Qt Installer Framework provides a set of tools and utilities to
create installers once, and deploy them across all the supported desktop
- Qt platforms without rewriting the source code. The installers will have the
- native look and feel on the platform where they are run: Linux, Microsoft
- Windows, and macOS.
+ Qt platforms without rewriting the source code. The installers will have
+ the native look and feel of the platform on which they run: Linux,
+ Microsoft Windows, and macOS.
The Qt Installer Framework tools generate installers with a set of pages
- that guide the users during the installation, update, or uninstallation
- process. You provide the installable content and specify information about
+ that guide the users during the installation, update, or uninstall
+ process. You supply the installable content and specify information about
it, such as the name of the product and the installer and the text for the
license agreement.
You can customize the installers by adding widgets to the predefined pages
- or by adding whole pages to provide users with additional options. You can
+ or by adding whole pages to offer users more options. You can
create scripts to add operations to the installer.
\section1 Choosing Installer Type
- You can provide end users with an \e offline or \e online installer, or
+ You can offer end users an \e offline or \e online installer, or
both, depending on your use cases.
\image ifw-overview.png
- Both installers install a \e {maintenance tool} that can later be used to
+ Both installers install a \e {maintenance tool}, which allows you to later
add, update, and remove components. Offline installers contain all the
installable components and do not require network connections during the
installation. Online installers only install the maintenance tool that then
@@ -63,12 +63,12 @@
Therefore, the size of an online installer binary is smaller and its
download time is shorter than that of an offline installer binary. The total
time spent downloading and running an online installer might also be shorter
- than dowloading and running an offline installer if the end users do not
+ than downloading and running an offline installer if the end users do not
install all the available components.
- End users can use the maintenance tool to install additional components from
+ End users can use the maintenance tool to install more components from
the server after the initial installation, as well as to receive automatic
- updates to content as soon as the updates are published on the server.
+ updates to content as soon as the updates are available on the server.
However, this works for an offline installation only if you specify a
repository address in the offline installer configuration or if end users
specify the repository address themselves in the maintenance tool settings.
@@ -84,7 +84,7 @@
\section1 Promoting Updates
Make online repositories available to promote updates to end users who
- install your product. The easiest way to provide an update is to recreate
+ install your product. The easiest way to offer an update is to recreate
the repository and upload it to the web server. For large repositories, you
can update only the changed components.
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc
index 7917015a9..3387987a7 100644
--- a/doc/installerfw.qdoc
+++ b/doc/installerfw.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -90,7 +90,7 @@
\title Creating Installers
- The following steps are needed to create offline and online installers:
+ To create offline and online installers, do the following:
\list 1
@@ -98,13 +98,13 @@
For more information, see \l{Package Directory}.
\li Create a configuration file called \c config.xml in the \c config
- directory. It contains information about how to build the installer
+ directory. It has information about how to build the installer
binaries and online repositories. For more information about the
file format and available settings, see
\l{Configuration File}.
\li Create a package information file called \c package.xml in the
- \c {packages\{component}\meta} directory. It contains settings for deployment and
+ \c {packages\{component}\meta} directory. It has settings for deployment and
the installation process. For more information, see
\l{Meta Directory}.
@@ -112,7 +112,7 @@
For more information, see \l{Data Directory}.
\li For online installers, use the \c repogen tool to create the
- repository that contains the installable content and upload the
+ repository that has the installable content and upload the
repository to a web server.
\li Use the \c binarycreator tool to create the installer. For more
@@ -949,7 +949,15 @@
\row
\li Script
\li File name of a script being loaded. Optional.
- For more information, see \l{Adding Operations}.
+ Specifying the \c {postLoad="true"} attribute will cause the script
+ to be loaded only to the component that is selected for update or
+ install. With the attribute, the script is loaded right before the
+ component installation starts. This will speed up the installer
+ if there are large amounts of components with install scripts in the
+ repository. Make sure the script does not contain anything that needs
+ to be evaluated before the install tree view is shown.
+ For more information, see \l{Adding Operations} and
+ \l{Using postLoad in component script}.
\row
\li UserInterfaces
\li List of pages to load. To add several pages, add several
@@ -1877,7 +1885,7 @@
\section2 Getting the Maintenance Tool
The maintenance tool for Linux and Windows is the same as the \c installerbase
- executable located in your Qt Installer Framework's \c installation bin folder.
+ executable located in your Qt Installer Framework's installation \c bin folder.
For macOS the maintenance tool app bundle can be created using the \c binarycreator
tool with the command line switch \c --mt or \c --create-maintenancetool. The
name of the macOS app bundle can be configured in config.xml using
diff --git a/doc/noninteractive.qdoc b/doc/noninteractive.qdoc
index 5576f93f3..15c85d28a 100644
--- a/doc/noninteractive.qdoc
+++ b/doc/noninteractive.qdoc
@@ -80,6 +80,21 @@
For more information about the JavaScript global objects that you can use
in control scripts, see \l{Scripting API}.
+ In addition to the predefined global objects, the scripting API supports working
+ with other objects derived from \c QObject. In the above code example the
+ \c{gui.currentPageWidget()} method returns a widget of type \c QWidget.
+
+ The scripting API makes use of Qt's object trees. Widgets derived from \c QObject export
+ their named child objects as properties for the JavaScript object, where the name of the
+ property is the same as the child object's \c{QObject::objectName}. The default properties
+ and their access functions of objects derived from \c QObject can also be used in the scripts.
+
+ For example, in the above code the \c MessageLabel object from class \c QLabel is a child
+ of the \c widget. The \c setText() is the setter access function for its \c QLabel::text property.
+
+ In addition to properties, the signals and public slots of objects derived from \c QObject
+ can be used in both controller and component scripts.
+
\section1 Predefined Installer Pages
The QInstaller JavaScript object provides access to the following predefined
diff --git a/doc/scripting-api/gui.qdoc b/doc/scripting-api/gui.qdoc
index 7b9e22328..149e1bc84 100644
--- a/doc/scripting-api/gui.qdoc
+++ b/doc/scripting-api/gui.qdoc
@@ -58,6 +58,13 @@
*/
/*!
+ \qmlsignal gui::aboutApplicationClicked()
+
+ This signal is emitted when the about application menu and dialog
+ on macOS are shown.
+*/
+
+/*!
\qmlsignal gui::settingsButtonClicked()
This signal is emitted when the \uicontrol Settings button is clicked.
diff --git a/doc/scripting.qdoc b/doc/scripting.qdoc
index bb547a0d6..8854442fa 100644
--- a/doc/scripting.qdoc
+++ b/doc/scripting.qdoc
@@ -375,4 +375,28 @@
\li ApplicationsDirX64
\li \c {C:\Program Files}
\endtable
+
+ \section1 Using postLoad in Component Script
+ By default, component scripts are evaluated before the install tree view
+ is shown. This can have performance cost if there is a huge amount of
+ components with component scripts. The \c postLoad attribute introduces a way
+ to evaluate the component script right before installation starts, only for
+ the components that are selected for installation or update:
+ \code
+ <Script postLoad="true">my_install_script.qs</Script>
+ \endcode
+ Whether \c postLoad can be set to \c true must be considered case by case,
+ depending on the contents of the script. For example, if the script contents
+ affect the install tree view, like setting \c <Default> to \c true, setting
+ new dependencies, or adding new wizard pages, \c postLoad must not be used or
+ it must be set to \c false. If the script contains only methods
+ that are run during the installation, \c postLoad can be set to \c true. For
+ example, all overridden \c operation functions are run during installation.
+ For more information, see \l {Adding Operations to Components}. If you are not
+ sure when to use \c postLoad, then don't use it. The performance cost is
+ huge only when there are thousands of scripts to be evaluated.
+
+ Both \c <Script postLoad="true"> and \c <Script> tags can be used at the same time.
+ This means that one component can have one script that is evaluated when the installation
+ starts and another script that is evaluated before the install tree view is shown.
*/
diff --git a/doc/tutorial.qdoc b/doc/tutorial.qdoc
index 1f5782317..152f392c4 100644
--- a/doc/tutorial.qdoc
+++ b/doc/tutorial.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,37 +37,37 @@
\image ifw-introduction-page.png "Introduction page"
- This section describes the following tasks that you must accomplish to
+ This section describes the following tasks that you must carry out to
create the installer:
\list 1
- \li Create a \e {package directory} that will contain all the
+ \li Create a \e {package directory} that will have all the
configuration files and installable packages.
- \li Create a \e {configuration file} that contains information about how
+ \li Create a \e {configuration file} that has information about how
to build the installer binaries and online repositories.
- \li Create a \e {package information file} that contains information
+ \li Create a \e {package information file} that has information
about the installable components.
\li Create installer content and copy it to the package directory.
\li Use the \c binarycreator tool to create the \e installer.
- The installer pages are created by using the information you provide
- in the configuration and package information file.
+ To create the installer pages, use the information set in the
+ configuration and package information file.
\endlist
- The example files are located in the \c{examples\tutorial} directory in the
+ You can find the example files in the \c{examples\tutorial} directory in the
Qt Installer Framework repository.
\section1 Creating a Package Directory
Create a directory structure that reflects the design of the installer and
- allows the installer to be extended in the future. The directory must
- contain subdirectories called \c config and \c packages.
+ allows for future extension. The directory must contain subdirectories
+ called \c config and \c packages.
\image ifw-tutorial-files.png
@@ -80,37 +80,37 @@
\quotefile ../examples/tutorial/config/config.xml
- The configuration file specifies the following information that is
- displayed on the introduction page:
+ The configuration file includes the following information for the
+ introduction page:
\list
- \li The \c <Title> element specifies the installer name displayed on the
- title bar (1).
+ \li The \c <Title> element sets the installer name and displays it on
+ the title bar (1).
- \li The \c <Name> element specifies the application name that is added to
- the page name and introduction text (2).
+ \li The \c <Name> element sets the application name and adds it to
+ the page number and introduction text (2).
\endlist
\image ifw-tutorial-introduction-page.png "Introduction page"
- The other elements are used to customize the behavior of the installer:
+ The other elements customize the behavior of the installer:
\list
- \li The \c <Version> element specifies the application version number.
+ \li The \c <Version> element sets the application version number.
- \li The \c <Publisher> element specifies the publisher of the software
+ \li The \c <Publisher> element sets the publisher of the software
(as shown in the Windows Control Panel, for example).
- \li The \c <StartMenuDir> element specifies the name of the default
+ \li The \c <StartMenuDir> element sets the name of the default
program group for the product in the Windows \gui Start menu.
- \li The \c <TargetDir> element specifies that the default target
- directory displayed to users is \c InstallationDirectory in the home
- directory of the current user (because the predefined variable
- \c @HomeDir@ is used as a part of the value). For more information,
+ \li The \c <TargetDir> element sets and displays \c InstallationDirectory
+ in the home directory of the current user as the default target
+ directory displayed to users (because it uses the predefined
+ variable \c @HomeDir@ as part of the value). For more information,
see \l{Predefined Variables}.
\endlist
@@ -120,30 +120,28 @@
\section1 Creating a Package Information File
- In this easy scenario, the installer handles only one component that is
- called \c{com.vendor.product}. To provide the installer with information
+ In this easy scenario, the installer handles only one component,
+ \c{com.vendor.product}. To give the installer information
about the component, create a file called \c package.xml with the
following contents and place it in the \c meta directory:
\quotefile ../examples/tutorial/packages/com.vendor.product/meta/package.xml
- The elements in the example file are described in more detail below.
-
+ Below is a more detailed description of the elements in the example file.
For more information about the package information file, see
\l{Package Information File Syntax}.
\section2 Specifying Component Information
- The information from the following elements is displayed on the component
- selection page:
+ The component selection page displays information from the following elements:
\list
- \li The \c <DisplayName> element specifies the name of the component in
- the list of components (1).
+ \li The \c <DisplayName> element sets the name of the component in the
+ component list (1).
- \li The \c <Description> element specifies the text that is displayed when
- the component is selected (2).
+ \li The \c <Description> element sets and displays text based on the
+ selected component (2).
\endlist
@@ -151,29 +149,31 @@
\section2 Specifying Installer Version
- The \c <Version> element enables you to promote updates to users when they
- become available.
+ The \c <Version> element offers updates to users when they become
+ available.
\section2 Adding Licenses
- The \c <License> element specifies the name of the file that contains the text
- for the license agreement (1) that is displayed on the license check page:
+ The \c <License> element sets the name of the file, which has the
+ license agreement text (1). The license check page displays this license
+ text.
\image ifw-tutorial-license-check.png "License check page"
\section2 Selecting Default Contents
- The \c <Default> element specifies whether the component is selected by
- default. The value \c true sets the component as selected. In this example,
- we use the value \c script to resolve the value during runtime. The
- name of the JavaScript script file, installscript.qs, is specified in the
- \c <Script> element.
+ The \c <Default> element specifies whether the selected component
+ is a default value. The value \c true sets the component as selected. This
+ example uses the value \c script to resolve the value during runtime. The
+ \c <Script> element sets the name of the JavaScript script file,
+ installscript.qs.
\section1 Creating Installer Content
- Content to be installed is stored in the \c data directory of a component.
+ The \c data directory of a component can store content available for
+ installation.
As there is only one component, place the data in the
- \c{packages/com.vendor.product/data} directory. The example already contains
+ \c{packages/com.vendor.product/data} directory. The example already has
a file for testing purposes, but you can place basically any files in the
directory.
@@ -184,7 +184,7 @@
You are now ready to create your first installer. Switch to the
\c examples\tutorial directory on the command line. To create an installer called
- YourInstaller.exe that contains the packages identified by
+ YourInstaller.exe that has the packages identified by
com.vendor.product, enter the following command:
\list
@@ -201,15 +201,13 @@
\endlist
- The installer is created in the current directory and you can deliver it to
- end users.
+ \IFW creates the installer in the current directory and you can deliver it
+ to end users.
For more information about using the \c binarycreator tool, see
\l{binarycreator}.
- \note If an error message is displayed when you run the tutorial installer,
+ \note If an error message appears when you run the tutorial installer,
check that you used a statically built Qt to create the installer. For more
information, see \l{Configuring Qt}.
*/
-
-
diff --git a/examples/doc/changeuserinterface.qdoc b/examples/doc/changeuserinterface.qdoc
index 8de6bcadd..012f6bf43 100644
--- a/examples/doc/changeuserinterface.qdoc
+++ b/examples/doc/changeuserinterface.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,14 +30,14 @@
\ingroup qtifwexamples
\title Change Installer UI Example
- \brief Using a component script to modify the installer UI.
+ \brief Using a component script to change the installer UI.
\image qtifw-examples-changeuserinterface.png
\e {Change Installer UI} demonstrates how to use the \c Component() function
- to modify the default text for the check box label on the
+ to change the default text for the check box label on the
\l{License Agreement Page}{license check page}. This example does not install
- any components, but we specify a dummy component in the package information
+ any components, but it specifies a dummy component in the package information
file, because installers without components are not allowed.
\include installerfw-examples-configuring.qdocinc
@@ -49,26 +49,26 @@
\list
\li The \c <Default> element specifies whether the component is
preselected for installation in the user interface by default.
- \li The \c <Script> element specifies the file name of the JavaScript
- file that is loaded to perform operations.
- \li The \c <Licenses> element specifies the \c name of the license
- agreement to be accepted by the end user and the filename of the
- \c file that contains the license.
+ \li The \c <Script> element sets the file name of the loaded JavaScript
+ file.
+ \li The \c <Licenses> element sets the \c name of the license
+ agreement that the end user accepts, as well as the filename of the
+ \c file that has the license.
\endlist
\quotefile changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/package.xml
- \section1 Modifying UI Text
+ \section1 Updating UI Text
- In installscript.qs, we call the \c Component() function to add the license
- check page and to connect to the \c changeLicenseLabels signal when end
+ In installscript.qs, the \c Component() function adds the license
+ check page and connects to the \c changeLicenseLabels signal when end
users enter the page:
\quotefromfile changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/installscript.qs
\skipto Component()
\printuntil }
- We use the \c changeLicenseLabels function to change the text label for the
+ The \c changeLicenseLabels function changes the text label for the
accept license check box on the page:
\printuntil }
diff --git a/installerfw.pri b/installerfw.pri
index 0c8504060..5bba5cd9e 100644
--- a/installerfw.pri
+++ b/installerfw.pri
@@ -3,9 +3,9 @@
}
IFW_PRI_INCLUDED = 1
-IFW_VERSION_STR = 4.5.2
-IFW_VERSION = 0x040502
-IFW_VERSION_WIN32 = 4,5,2,0
+IFW_VERSION_STR = 4.6.0
+IFW_VERSION = 0x040600
+IFW_VERSION_WIN32 = 4,6,0,0
IFW_VERSION_STR_WIN32 = $$IFW_VERSION_STR\0
@@ -136,8 +136,9 @@ macx:LIBS += -framework Carbon -framework Security
QT += uitools core-private
CONFIG(static, static|shared) {
- win32:QT += winextras
+ win32:lessThan(QT_MAJOR_VERSION, 6):QT += winextras
QT += concurrent network qml xml
+ greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat
}
CONFIG += depend_includepath no_private_qt_headers_warning c++11
win32:CONFIG += console
diff --git a/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
index 37f4fe991..e101db855 100644
--- a/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
@@ -37,7 +37,7 @@ void SplitCommandLine(const UString &s, UStringVector &parts)
parts.Clear();
const QString cmdLine = QString::fromStdWString(static_cast<const wchar_t*>(s));
- const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts);
+ const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts);
foreach (const QString &arg, args)
parts.Add(arg.toStdWString().c_str());
}
diff --git a/src/libs/3rdparty/libarchive/archive.h b/src/libs/3rdparty/libarchive/archive.h
index 46041eb08..217ac1989 100644
--- a/src/libs/3rdparty/libarchive/archive.h
+++ b/src/libs/3rdparty/libarchive/archive.h
@@ -36,7 +36,7 @@
* assert that ARCHIVE_VERSION_NUMBER >= 2012108.
*/
/* Note: Compiler will complain if this does not match archive_entry.h! */
-#define ARCHIVE_VERSION_NUMBER 3006001
+#define ARCHIVE_VERSION_NUMBER 3006002
#include <sys/stat.h>
#include <stddef.h> /* for wchar_t */
@@ -120,6 +120,8 @@ typedef ssize_t la_ssize_t;
# define __LA_DECL __declspec(dllimport)
# endif
# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
#else
/* Static libraries or non-Windows needs no special declaration. */
# define __LA_DECL
@@ -155,7 +157,7 @@ __LA_DECL int archive_version_number(void);
/*
* Textual name/version of the library, useful for version displays.
*/
-#define ARCHIVE_VERSION_ONLY_STRING "3.6.1"
+#define ARCHIVE_VERSION_ONLY_STRING "3.6.2"
#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
__LA_DECL const char * archive_version_string(void);
diff --git a/src/libs/3rdparty/libarchive/archive_digest.c b/src/libs/3rdparty/libarchive/archive_digest.c
index a7bd5f028..3361b19ad 100644
--- a/src/libs/3rdparty/libarchive/archive_digest.c
+++ b/src/libs/3rdparty/libarchive/archive_digest.c
@@ -49,16 +49,16 @@
* Initialize a Message digest.
*/
static int
-win_crypto_init(Digest_CTX *ctx, ALG_ID algId)
+win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId)
{
ctx->valid = 0;
if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ prov, CRYPT_VERIFYCONTEXT)) {
if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
return (ARCHIVE_FAILED);
if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
- PROV_RSA_FULL, CRYPT_NEWKEYSET))
+ prov, CRYPT_NEWKEYSET))
return (ARCHIVE_FAILED);
}
@@ -276,7 +276,7 @@ __archive_md5final(archive_md5_ctx *ctx, void *md)
static int
__archive_md5init(archive_md5_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_MD5));
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5));
}
static int
@@ -659,7 +659,7 @@ __archive_sha1final(archive_sha1_ctx *ctx, void *md)
static int
__archive_sha1init(archive_sha1_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA1));
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1));
}
static int
@@ -919,7 +919,7 @@ __archive_sha256final(archive_sha256_ctx *ctx, void *md)
static int
__archive_sha256init(archive_sha256_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA_256));
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256));
}
static int
@@ -1155,7 +1155,7 @@ __archive_sha384final(archive_sha384_ctx *ctx, void *md)
static int
__archive_sha384init(archive_sha384_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA_384));
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384));
}
static int
@@ -1415,7 +1415,7 @@ __archive_sha512final(archive_sha512_ctx *ctx, void *md)
static int
__archive_sha512init(archive_sha512_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA_512));
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512));
}
static int
diff --git a/src/libs/3rdparty/libarchive/archive_entry.c b/src/libs/3rdparty/libarchive/archive_entry.c
index ca7a4bdb5..ae6dc3336 100644
--- a/src/libs/3rdparty/libarchive/archive_entry.c
+++ b/src/libs/3rdparty/libarchive/archive_entry.c
@@ -568,6 +568,13 @@ archive_entry_nlink(struct archive_entry *entry)
return (entry->ae_stat.aest_nlink);
}
+/* Instead, our caller could have chosen a specific encoding
+ * (archive_mstring_get_mbs, archive_mstring_get_utf8,
+ * archive_mstring_get_wcs). So we should try multiple
+ * encodings. Try mbs first because of history, even though
+ * utf8 might be better for pathname portability.
+ * Also omit wcs because of type mismatch (char * versus wchar *)
+ */
const char *
archive_entry_pathname(struct archive_entry *entry)
{
@@ -575,6 +582,13 @@ archive_entry_pathname(struct archive_entry *entry)
if (archive_mstring_get_mbs(
entry->archive, &entry->ae_pathname, &p) == 0)
return (p);
+#if HAVE_EILSEQ /*{*/
+ if (errno == EILSEQ) {
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ }
+#endif /*}*/
if (errno == ENOMEM)
__archive_errx(1, "No memory");
return (NULL);
diff --git a/src/libs/3rdparty/libarchive/archive_entry.h b/src/libs/3rdparty/libarchive/archive_entry.h
index d5cb30de7..e579e9f33 100644
--- a/src/libs/3rdparty/libarchive/archive_entry.h
+++ b/src/libs/3rdparty/libarchive/archive_entry.h
@@ -30,7 +30,7 @@
#define ARCHIVE_ENTRY_H_INCLUDED
/* Note: Compiler will complain if this does not match archive.h! */
-#define ARCHIVE_VERSION_NUMBER 3006001
+#define ARCHIVE_VERSION_NUMBER 3006002
/*
* Note: archive_entry.h is for use outside of libarchive; the
@@ -122,6 +122,8 @@ typedef ssize_t la_ssize_t;
# define __LA_DECL __declspec(dllimport)
# endif
# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
#else
/* Static libraries on all platforms and shared libraries on non-Windows. */
# define __LA_DECL
diff --git a/src/libs/3rdparty/libarchive/archive_hmac.c b/src/libs/3rdparty/libarchive/archive_hmac.c
index 2a9d04c8d..012fe1596 100644
--- a/src/libs/3rdparty/libarchive/archive_hmac.c
+++ b/src/libs/3rdparty/libarchive/archive_hmac.c
@@ -230,10 +230,23 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
static int
__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[2];
+
+ EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ *ctx = EVP_MAC_CTX_new(mac);
+ if (*ctx == NULL)
+ return -1;
+ EVP_MAC_free(mac);
+ params[0] = OSSL_PARAM_construct_utf8_string("digest", "SHA1", 0);
+ params[1] = OSSL_PARAM_construct_end();
+ EVP_MAC_init(*ctx, key, key_len, params);
+#else
*ctx = HMAC_CTX_new();
if (*ctx == NULL)
return -1;
HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);
+#endif
return 0;
}
@@ -241,22 +254,38 @@ static void
__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
size_t data_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_update(*ctx, data, data_len);
+#else
HMAC_Update(*ctx, data, data_len);
+#endif
}
static void
__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ size_t len = *out_len;
+#else
unsigned int len = (unsigned int)*out_len;
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_final(*ctx, out, &len, *out_len);
+#else
HMAC_Final(*ctx, out, &len);
+#endif
*out_len = len;
}
static void
__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_CTX_free(*ctx);
+#else
HMAC_CTX_free(*ctx);
+#endif
*ctx = NULL;
}
diff --git a/src/libs/3rdparty/libarchive/archive_hmac_private.h b/src/libs/3rdparty/libarchive/archive_hmac_private.h
index 13a67d495..50044a045 100644
--- a/src/libs/3rdparty/libarchive/archive_hmac_private.h
+++ b/src/libs/3rdparty/libarchive/archive_hmac_private.h
@@ -74,9 +74,16 @@ typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx;
#elif defined(HAVE_LIBCRYPTO)
+#include <openssl/opensslv.h>
+#include <openssl/hmac.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+typedef EVP_MAC_CTX *archive_hmac_sha1_ctx;
+
+#else
#include "archive_openssl_hmac_private.h"
typedef HMAC_CTX* archive_hmac_sha1_ctx;
+#endif
#else
diff --git a/src/libs/3rdparty/libarchive/archive_platform.h b/src/libs/3rdparty/libarchive/archive_platform.h
index 3426975de..1038932ac 100644
--- a/src/libs/3rdparty/libarchive/archive_platform.h
+++ b/src/libs/3rdparty/libarchive/archive_platform.h
@@ -195,8 +195,9 @@
/*
* glibc 2.24 deprecates readdir_r
+ * bionic c deprecates readdir_r too
*/
-#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))
+#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24)) && (!defined(__ANDROID__))
#define USE_READDIR_R 1
#else
#undef USE_READDIR_R
diff --git a/src/libs/3rdparty/libarchive/archive_read_disk_posix.c b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
index 2b39e672b..5a94ec5d4 100644
--- a/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
+++ b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
@@ -34,9 +34,6 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
-#ifdef HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
@@ -54,6 +51,8 @@ __FBSDID("$FreeBSD$");
#endif
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
+#elif HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
#endif
/*
* Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
@@ -2103,6 +2102,8 @@ tree_push(struct tree *t, const char *path, int filesystem_id,
struct tree_entry *te;
te = calloc(1, sizeof(*te));
+ if (te == NULL)
+ __archive_errx(1, "Out of memory");
te->next = t->stack;
te->parent = t->current;
if (te->parent)
diff --git a/src/libs/3rdparty/libarchive/archive_read_disk_windows.c b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
index ea32e2aac..f9d139557 100644
--- a/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
+++ b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
@@ -418,8 +418,9 @@ la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)
FILE_FLAG_OPEN_REPARSE_POINT;
int ret;
- h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag,
- NULL);
+ h = CreateFileW(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
return (-1);
@@ -1073,7 +1074,9 @@ next_entry(struct archive_read_disk *a, struct tree *t,
else
flags |= FILE_FLAG_SEQUENTIAL_SCAN;
t->entry_fh = CreateFileW(tree_current_access_path(t),
- GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL);
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, flags, NULL);
if (t->entry_fh == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
archive_set_error(&a->archive, errno,
@@ -2046,7 +2049,8 @@ tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st,
if (sim_lstat && tree_current_is_physical_link(t))
flag |= FILE_FLAG_OPEN_REPARSE_POINT;
- h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL,
+ h = CreateFileW(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, flag, NULL);
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
@@ -2275,7 +2279,8 @@ archive_read_disk_entry_from_file(struct archive *_a,
} else
desiredAccess = GENERIC_READ;
- h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL,
+ h = CreateFileW(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, flag, NULL);
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
@@ -2337,7 +2342,8 @@ archive_read_disk_entry_from_file(struct archive *_a,
if (fd >= 0) {
h = (HANDLE)_get_osfhandle(fd);
} else {
- h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
+ h = CreateFileW(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
index ae0b08003..1e99542d7 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
@@ -450,7 +450,9 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
chsum = (chsum >> 8) & 0xff;
chsum_verifier = read_buf[descriptor_bytes-1] & 0xff;
if (chsum != chsum_verifier)
+#ifndef DONT_FAIL_ON_CRC_ERROR
goto malformed_error;
+#endif
__archive_read_filter_consume(self->upstream, descriptor_bytes);
@@ -521,7 +523,9 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
unsigned int chsum_block =
archive_le32dec(read_buf + 4 + compressed_size);
if (chsum != chsum_block)
+#ifndef DONT_FAIL_ON_CRC_ERROR
goto malformed_error;
+#endif
}
@@ -652,10 +656,12 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
state->xxh32_state);
state->xxh32_state = NULL;
if (checksum != checksum_stream) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
"lz4 stream checksum error");
return (ARCHIVE_FATAL);
+#endif
}
} else if (ret > 0)
__archive_xxhash.XXH32_update(state->xxh32_state,
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
index afd2d4d0c..4ebdd3bf3 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
@@ -283,7 +283,9 @@ consume_header(struct archive_read_filter *self)
else
checksum = adler32(adler32(0, NULL, 0), p, len);
if (archive_be32dec(p + len) != checksum)
+#ifndef DONT_FAIL_ON_CRC_ERROR
goto corrupted;
+#endif
__archive_read_filter_consume(self->upstream, len + 4);
if (flags & EXTRA_FIELD) {
/* Skip extra field */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
index 32ae0be92..e313d39c0 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
@@ -612,9 +612,11 @@ lzip_tail(struct archive_read_filter *self)
/* Check the crc32 value of the uncompressed data of the current
* member */
if (state->crc32 != archive_le32dec(f)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
"Lzip: CRC32 error");
return (ARCHIVE_FAILED);
+#endif
}
/* Check the uncompressed size of the current member */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
index 564ba514a..0ba4bee35 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
@@ -776,7 +776,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
}
/* Set up a more descriptive format name. */
- sprintf(zip->format_name, "7-Zip");
+ snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip");
a->archive.archive_format_name = zip->format_name;
return (ret);
@@ -2857,8 +2857,10 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
/* CRC check. */
if (crc32(0, (const unsigned char *)p + 12, 20)
!= archive_le32dec(p + 8)) {
+#ifdef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, -1, "Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
next_header_offset = archive_le64dec(p + 12);
@@ -2908,8 +2910,10 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
/* Check the EncodedHeader CRC.*/
if (r == 0 && zip->header_crc32 != next_header_crc) {
archive_set_error(&a->archive, -1,
+#ifndef DONT_FAIL_ON_CRC_ERROR
"Damaged 7-Zip archive");
r = -1;
+#endif
}
if (r == 0) {
if (zip->si.ci.folders[0].digest_defined)
@@ -2960,9 +2964,11 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
/* Check the Header CRC.*/
if (check_header_crc && zip->header_crc32 != next_header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, -1,
"Malformed 7-Zip archive");
return (ARCHIVE_FATAL);
+#endif
}
break;
default:
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
index 950f3d254..4d5029b1b 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
@@ -996,7 +996,7 @@ archive_read_format_cab_read_header(struct archive_read *a,
cab->end_of_entry_cleanup = cab->end_of_entry = 1;
/* Set up a more descriptive format name. */
- sprintf(cab->format_name, "CAB %d.%d (%s)",
+ snprintf(cab->format_name, sizeof(cab->format_name), "CAB %d.%d (%s)",
hd->major, hd->minor, cab->entry_cffolder->compname);
a->archive.archive_format_name = cab->format_name;
@@ -1134,7 +1134,7 @@ cab_checksum_update(struct archive_read *a, size_t bytes)
}
if (sumbytes) {
int odd = sumbytes & 3;
- if (sumbytes - odd > 0)
+ if ((int)(sumbytes - odd) > 0)
cfdata->sum_calculated = cab_checksum_cfdata_4(
p, sumbytes - odd, cfdata->sum_calculated);
if (odd)
@@ -1171,12 +1171,14 @@ cab_checksum_finish(struct archive_read *a)
cfdata->sum_calculated = cab_checksum_cfdata(
cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
if (cfdata->sum_calculated != cfdata->sum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes",
cab->entry_cffolder->cfdata_index -1,
cfdata->sum, cfdata->sum_calculated,
cfdata->compressed_size);
return (ARCHIVE_FAILED);
+#endif
}
return (ARCHIVE_OK);
}
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
index cd7f92f46..33bf330cb 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
@@ -1757,7 +1757,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
size_t name_len;
const unsigned char *rr_start, *rr_end;
const unsigned char *p;
- size_t dr_len;
+ size_t dr_len = 0;
uint64_t fsize, offset;
int32_t location;
int flags;
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
index bff0f01f4..bcfd42e1d 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
@@ -739,7 +739,7 @@ archive_read_format_lha_read_header(struct archive_read *a,
if (lha->directory || lha->compsize == 0)
lha->end_of_entry = 1;
- sprintf(lha->format_name, "lha -%c%c%c-",
+ snprintf(lha->format_name, sizeof(lha->format_name), "lha -%c%c%c-",
lha->method[0], lha->method[1], lha->method[2]);
a->archive.archive_format_name = lha->format_name;
@@ -1039,9 +1039,11 @@ lha_read_file_header_2(struct archive_read *a, struct lha *lha)
}
if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"LHa header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
return (err);
}
@@ -1107,9 +1109,11 @@ lha_read_file_header_3(struct archive_read *a, struct lha *lha)
return (err);
if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"LHa header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
return (err);
invalid:
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
index 4a2816325..2bc3ba066 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
@@ -994,9 +994,11 @@ process_add_entry(struct archive_read *a, struct mtree *mtree,
struct mtree_entry *alt;
alt = (struct mtree_entry *)__archive_rb_tree_find_node(
&mtree->rbtree, entry->name);
- while (alt->next_dup)
- alt = alt->next_dup;
- alt->next_dup = entry;
+ if (alt != NULL) {
+ while (alt->next_dup)
+ alt = alt->next_dup;
+ alt->next_dup = entry;
+ }
}
}
@@ -1071,7 +1073,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
continue;
/* Non-printable characters are not allowed */
for (s = p;s < p + len - 1; s++) {
- if (!isprint((unsigned char)*s)) {
+ if (!isprint((unsigned char)*s) && *s != '\t') {
r = ARCHIVE_FATAL;
break;
}
@@ -1250,9 +1252,17 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
archive_entry_filetype(entry) == AE_IFDIR) {
mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
__archive_ensure_cloexec_flag(mtree->fd);
- if (mtree->fd == -1 &&
- (errno != ENOENT ||
- archive_strlen(&mtree->contents_name) > 0)) {
+ if (mtree->fd == -1 && (
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * On Windows, attempting to open a file with an
+ * invalid name result in EINVAL (Error 22)
+ */
+ (errno != ENOENT && errno != EINVAL)
+#else
+ errno != ENOENT
+#endif
+ || archive_strlen(&mtree->contents_name) > 0)) {
archive_set_error(&a->archive, errno,
"Can't open %s", path);
r = ARCHIVE_WARN;
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
index f9cbe2a88..793e8e985 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
@@ -1007,9 +1007,11 @@ archive_read_format_rar_read_header(struct archive_read *a,
crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);
if ((crc32_val & 0xffff) != archive_le16dec(p)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
__archive_read_consume(a, skip);
break;
@@ -1065,9 +1067,11 @@ archive_read_format_rar_read_header(struct archive_read *a,
skip -= to_read;
}
if ((crc32_val & 0xffff) != crc32_expected) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
if (head_type == ENDARC_HEAD)
return (ARCHIVE_EOF);
@@ -1432,9 +1436,11 @@ read_header(struct archive_read *a, struct archive_entry *entry,
/* File Header CRC check. */
crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));
if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
/* If no CRC error, Go on parsing File Header. */
p = h;
@@ -1952,9 +1958,11 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size,
*size = 0;
*offset = rar->offset;
if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"File CRC error");
return (ARCHIVE_FATAL);
+#endif
}
rar->entry_eof = 1;
return (ARCHIVE_EOF);
@@ -2045,9 +2053,11 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
*size = 0;
*offset = rar->offset;
if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"File CRC error");
return (ARCHIVE_FATAL);
+#endif
}
rar->entry_eof = 1;
return (ARCHIVE_EOF);
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
index a3cfa72e7..38979cbe9 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
@@ -2821,11 +2821,13 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p,
^ (uint8_t) (*block_size >> 16);
if(calculated_cksum != hdr->block_cksum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Block checksum error: got 0x%x, expected 0x%x",
hdr->block_cksum, calculated_cksum);
return ARCHIVE_FATAL;
+#endif
}
return ARCHIVE_OK;
@@ -3911,6 +3913,13 @@ static int do_unpack(struct archive_read* a, struct rar5* rar,
case GOOD:
/* fallthrough */
case BEST:
+ /* No data is returned here. But because a sparse-file aware
+ * caller (like archive_read_data_into_fd) may treat zero-size
+ * as a sparse file block, we need to update the offset
+ * accordingly. At this point the decoder doesn't have any
+ * pending uncompressed data blocks, so the current position in
+ * the output file should be last_write_ptr. */
+ if (offset) *offset = rar->cstate.last_write_ptr;
return uncompress_file(a);
default:
archive_set_error(&a->archive,
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
index bfdad7f87..93c3fd585 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
@@ -407,14 +407,13 @@ archive_read_format_tar_bid(struct archive_read *a, int best_bid)
/*
* Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields.
*/
- if (bid > 0 && (
- validate_number_field(header->mode, sizeof(header->mode)) == 0
+ if (validate_number_field(header->mode, sizeof(header->mode)) == 0
|| validate_number_field(header->uid, sizeof(header->uid)) == 0
|| validate_number_field(header->gid, sizeof(header->gid)) == 0
|| validate_number_field(header->mtime, sizeof(header->mtime)) == 0
|| validate_number_field(header->size, sizeof(header->size)) == 0
|| validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0
- || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) {
+ || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0) {
bid = 0;
}
@@ -2108,6 +2107,21 @@ pax_attribute(struct archive_read *a, struct tar *tar,
/* "size" is the size of the data in the entry. */
tar->entry_bytes_remaining
= tar_atol10(value, strlen(value));
+ if (tar->entry_bytes_remaining < 0) {
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute is negative");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->entry_bytes_remaining == INT64_MAX) {
+ /* Note: tar_atol returns INT64_MAX on overflow */
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute overflow");
+ return (ARCHIVE_FATAL);
+ }
/*
* The "size" pax header keyword always overrides the
* "size" field in the tar header.
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
index 503ff58b9..ec5b06eda 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
@@ -624,7 +624,9 @@ read_toc(struct archive_read *a)
__archive_read_consume(a, xar->toc_chksum_size);
xar->offset += xar->toc_chksum_size;
if (r != ARCHIVE_OK)
+#ifndef DONT_FAIL_ON_CRC_ERROR
return (ARCHIVE_FATAL);
+#endif
}
/*
@@ -827,10 +829,12 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
xattr->a_sum.val, xattr->a_sum.len,
xattr->e_sum.val, xattr->e_sum.len);
if (r != ARCHIVE_OK) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"Xattr checksum error");
r = ARCHIVE_WARN;
break;
+#endif
}
if (xattr->name.s == NULL) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
diff --git a/src/libs/3rdparty/libarchive/archive_string.c b/src/libs/3rdparty/libarchive/archive_string.c
index d7f2c46b2..69458e1a1 100644
--- a/src/libs/3rdparty/libarchive/archive_string.c
+++ b/src/libs/3rdparty/libarchive/archive_string.c
@@ -3988,10 +3988,10 @@ int
archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
const char **p, size_t *length, struct archive_string_conv *sc)
{
- int r, ret = 0;
-
- (void)r; /* UNUSED */
+ int ret = 0;
#if defined(_WIN32) && !defined(__CYGWIN__)
+ int r;
+
/*
* Internationalization programming on Windows must use Wide
* characters because Windows platform cannot make locale UTF-8.
diff --git a/src/libs/3rdparty/libarchive/archive_write.c b/src/libs/3rdparty/libarchive/archive_write.c
index 66592e826..27626b541 100644
--- a/src/libs/3rdparty/libarchive/archive_write.c
+++ b/src/libs/3rdparty/libarchive/archive_write.c
@@ -201,6 +201,10 @@ __archive_write_allocate_filter(struct archive *_a)
struct archive_write_filter *f;
f = calloc(1, sizeof(*f));
+
+ if (f == NULL)
+ return (NULL);
+
f->archive = _a;
f->state = ARCHIVE_WRITE_FILTER_STATE_NEW;
if (a->filter_first == NULL)
@@ -548,6 +552,10 @@ archive_write_open2(struct archive *_a, void *client_data,
a->client_data = client_data;
client_filter = __archive_write_allocate_filter(_a);
+
+ if (client_filter == NULL)
+ return (ARCHIVE_FATAL);
+
client_filter->open = archive_write_client_open;
client_filter->write = archive_write_client_write;
client_filter->close = archive_write_client_close;
diff --git a/src/libs/3rdparty/libarchive/archive_write_disk_posix.c b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
index dd7eb9a5e..09a5eef03 100644
--- a/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
+++ b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
@@ -1996,6 +1996,8 @@ archive_write_disk_new(void)
free(a);
return (NULL);
}
+ a->path_safe.s[0] = 0;
+
#ifdef HAVE_ZLIB_H
a->decmpfs_compression_level = 5;
#endif
@@ -2793,7 +2795,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
char *tail;
char *head;
int last;
- char c;
+ char c = '\0';
int r;
struct stat st;
int chdir_fd;
diff --git a/src/libs/3rdparty/libarchive/archive_write_disk_windows.c b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
index 1b12a299c..88df3ce02 100644
--- a/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
+++ b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
@@ -1370,6 +1370,7 @@ archive_write_disk_new(void)
free(a);
return (NULL);
}
+ a->path_safe.s[0] = 0;
return (&a->archive);
}
@@ -2154,6 +2155,8 @@ check_symlinks(struct archive_write_disk *a)
return (ARCHIVE_FAILED);
}
}
+ if (!c)
+ break;
pn[0] = c;
pn++;
}
@@ -2258,6 +2261,9 @@ cleanup_pathname(struct archive_write_disk *a, wchar_t *name)
return (ARCHIVE_FAILED);
} else
p += 4;
+ /* Network drive path like "\\<server-name>\<share-name>\file" */
+ } else if (p[0] == L'\\' && p[1] == L'\\') {
+ p += 2;
}
/* Skip leading drive letter from archives created
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
index 52911491f..cf1f4770e 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
@@ -1717,7 +1717,7 @@ build_pax_attribute_name(char *dest, const char *src)
* to having clients override it.
*/
#if HAVE_GETPID && 0 /* Disable this for now; see above comment. */
- sprintf(buff, "PaxHeader.%d", getpid());
+ snprintf(buff, sizeof(buff), "PaxHeader.%d", getpid());
#else
/* If the platform can't fetch the pid, don't include it. */
strcpy(buff, "PaxHeader");
diff --git a/src/libs/3rdparty/libarchive/filter_fork_posix.c b/src/libs/3rdparty/libarchive/filter_fork_posix.c
index ac255c4f8..62085a709 100644
--- a/src/libs/3rdparty/libarchive/filter_fork_posix.c
+++ b/src/libs/3rdparty/libarchive/filter_fork_posix.c
@@ -76,7 +76,7 @@ int
__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
pid_t *out_child)
{
- pid_t child;
+ pid_t child = -1;
int stdin_pipe[2], stdout_pipe[2], tmp;
#if HAVE_POSIX_SPAWNP
posix_spawn_file_actions_t actions;
diff --git a/src/libs/3rdparty/libarchive/qt_attribution.json b/src/libs/3rdparty/libarchive/qt_attribution.json
index 336051d47..df03d1702 100644
--- a/src/libs/3rdparty/libarchive/qt_attribution.json
+++ b/src/libs/3rdparty/libarchive/qt_attribution.json
@@ -5,7 +5,7 @@
"Description": "Multi-format archive and compression library.",
"QtUsage": "Used for reading and writing archive files in Qt Installer Framework",
"Homepage": "https://www.libarchive.org",
- "Version": "3.6.1",
+ "Version": "3.6.2",
"License": "BSD 2-clause \"Simplified\" License",
"LicenseId": "BSD-2-Clause",
"LicenseFile": "COPYING",
diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp
index d7adc2a52..52603cad5 100644
--- a/src/libs/ifwtools/binarycreator.cpp
+++ b/src/libs/ifwtools/binarycreator.cpp
@@ -43,7 +43,7 @@
#include <QDirIterator>
#include <QDomDocument>
#include <QProcess>
-#include <QRegExp>
+#include <QRegularExpression>
#include <QSettings>
#include <QTemporaryFile>
#include <QTemporaryDir>
@@ -637,8 +637,8 @@ void QInstallerTools::copyConfigData(const QString &configFile, const QString &t
continue;
}
- QString newName = domElement.text().replace(QRegExp(QLatin1String("\\\\|/|\\.|:")),
- QLatin1String("_"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:"));
+ QString newName = domElement.text().replace(regex, QLatin1String("_"));
QString targetFile;
QFileInfo elementFileInfo;
@@ -704,13 +704,13 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
// Begin check arguments
foreach (const QString &packageDir, args.packagesDirectories) {
- if (!QFileInfo(packageDir).exists()) {
+ if (!QFileInfo::exists(packageDir)) {
argumentError = QString::fromLatin1("Error: Package directory not found at the specified location.");
return EXIT_FAILURE;
}
}
foreach (const QString &repositoryDir, args.repositoryDirectories) {
- if (!QFileInfo(repositoryDir).exists()) {
+ if (!QFileInfo::exists(repositoryDir)) {
argumentError = QString::fromLatin1("Error: Only local filesystem repositories now supported.");
return EXIT_FAILURE;
}
@@ -721,12 +721,12 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
"contain any components apart from the root component.");
return EXIT_FAILURE;
}
- if (!QFileInfo(args.templateBinary).exists()) {
+ if (!QFileInfo::exists(args.templateBinary)) {
#ifdef Q_OS_WIN
if (!args.templateBinary.endsWith(suffix))
args.templateBinary = args.templateBinary + suffix;
// Try again with added executable suffix
- if (!QFileInfo(args.templateBinary).exists()) {
+ if (!QFileInfo::exists(args.templateBinary)) {
argumentError = QString::fromLatin1("Error: Template base binary not found at the specified location.");
return EXIT_FAILURE;
}
diff --git a/src/libs/ifwtools/binarycreator.h b/src/libs/ifwtools/binarycreator.h
index 779725660..387195742 100644
--- a/src/libs/ifwtools/binarycreator.h
+++ b/src/libs/ifwtools/binarycreator.h
@@ -77,7 +77,7 @@ public:
explicit BundleBackup(const QString &bundle = QString())
: bundle(bundle)
{
- if (!bundle.isEmpty() && QFileInfo(bundle).exists()) {
+ if (!bundle.isEmpty() && QFileInfo::exists(bundle)) {
backup = QInstaller::generateTemporaryFileName(bundle);
QFile::rename(bundle, backup);
}
diff --git a/src/libs/ifwtools/rcc/rcc.cpp b/src/libs/ifwtools/rcc/rcc.cpp
index 16203e2cd..caef84433 100644
--- a/src/libs/ifwtools/rcc/rcc.cpp
+++ b/src/libs/ifwtools/rcc/rcc.cpp
@@ -36,7 +36,7 @@
#include <QtCore/QFile>
#include <QtCore/QIODevice>
#include <QtCore/QLocale>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QStack>
#include <QXmlStreamReader>
@@ -664,7 +664,7 @@ QStringList RCCResourceLibrary::dataFiles() const
pending.push(m_root);
while (!pending.isEmpty()) {
RCCFileInfo *file = pending.pop();
- for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
it != file->m_children.end(); ++it) {
RCCFileInfo *child = it.value();
if (child->m_flags & RCCFileInfo::Directory)
@@ -678,7 +678,7 @@ QStringList RCCResourceLibrary::dataFiles() const
// Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
{
- typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
+ typedef QMultiHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
const QChar slash = QLatin1Char('/');
const ChildConstIterator cend = m_root->m_children.constEnd();
for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) {
@@ -815,7 +815,7 @@ bool RCCResourceLibrary::writeDataBlobs()
QString errorMessage;
while (!pending.isEmpty()) {
RCCFileInfo *file = pending.pop();
- for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
it != file->m_children.end(); ++it) {
RCCFileInfo *child = it.value();
if (child->m_flags & RCCFileInfo::Directory)
@@ -851,7 +851,7 @@ bool RCCResourceLibrary::writeDataNames()
qint64 offset = 0;
while (!pending.isEmpty()) {
RCCFileInfo *file = pending.pop();
- for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
it != file->m_children.end(); ++it) {
RCCFileInfo *child = it.value();
if (child->m_flags & RCCFileInfo::Directory)
@@ -958,7 +958,7 @@ bool RCCResourceLibrary::writeInitializer()
QString initName = m_initName;
if (!initName.isEmpty()) {
initName.prepend(QLatin1Char('_'));
- initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
+ initName.replace(QRegularExpression(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
}
//init
diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp
index 5bd9fd334..c54fb846b 100644
--- a/src/libs/ifwtools/repositorygen.cpp
+++ b/src/libs/ifwtools/repositorygen.cpp
@@ -43,7 +43,7 @@
#include "updater.h"
#include <QtCore/QDirIterator>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtXml/QDomDocument>
#include <QTemporaryDir>
@@ -188,7 +188,7 @@ static bool findMetaFile(const QString &repositoryDir, const QDomElement &packag
const QDomNodeList c1 = packageUpdate.childNodes();
for (int i = 0; i < c1.count(); ++i) {
const QDomElement e1 = c1.at(i).toElement();
- for (const QString &meta : *scMetaElements) {
+ for (const QString &meta : qAsConst(*scMetaElements)) {
if (e1.tagName() == meta) {
metaElementFound = true;
break;
@@ -367,39 +367,8 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
root.appendChild(update);
- // copy script file
- const QString script = package.firstChildElement(QLatin1String("Script")).text();
- if (!script.isEmpty()) {
- QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script));
- if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".")
- .arg(QDir::toNativeSeparators(scriptFile.fileName())));
- }
-
- const QString scriptContent = QLatin1String("(function() {")
- + QString::fromUtf8(scriptFile.readAll())
- + QLatin1String(";"
- " if (typeof Component == \"undefined\")"
- " throw \"Missing Component constructor. Please check your script.\";"
- "})();");
-
- // if the user isn't aware of the downloadable archives value we will add it automatically later
- foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive"))
- || scriptContent.contains(QLatin1String("removeDownloadableArchive"));
-
- static QInstaller::ScriptEngine testScriptEngine;
- const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName());
- if (value.isError()) {
- throw QInstaller::Error(QString::fromLatin1("Exception while loading component "
- "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()),
- value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") :
- value.toString() + QStringLiteral(" on line number: ") +
- value.property(QStringLiteral("lineNumber")).toString()));
- }
-
- const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script));
- copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript);
- }
+ // copy script files
+ copyScriptFiles(childNodes, info, foundDownloadableArchives, targetDir);
// write DownloadableArchives tag if that is missed by the user
if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) {
@@ -600,8 +569,8 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
info.version = packageElement.firstChildElement(QLatin1String("Version")).text();
// Version cannot start with comparison characters, be an empty string
// or have whitespaces at the beginning or at the end
- if (!QRegExp(QLatin1String("(?![<=>\\s]+)(.+)")).exactMatch(info.version) ||
- (info.version != info.version.trimmed())) {
+ static const QRegularExpression regex(QLatin1String("^(?![<=>\\s]+)(.+)$"));
+ if (!regex.match(info.version).hasMatch() || (info.version != info.version.trimmed())) {
if (ignoreInvalidPackages)
continue;
throw QInstaller::Error(QString::fromLatin1("Component version for \"%1\" is invalid! <Version>%2</Version>")
@@ -977,6 +946,50 @@ void QInstallerTools::splitMetadata(const QStringList &entryList, const QString
}
}
+void QInstallerTools::copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir)
+{
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomNode node = childNodes.at(i);
+ const QString key = node.nodeName();
+
+ if (key != QLatin1String("Script"))
+ continue;
+ const QString script = node.toElement().text();
+ if (script.isEmpty())
+ continue;
+
+ QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script));
+ if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".")
+ .arg(QDir::toNativeSeparators(scriptFile.fileName())));
+ }
+
+ const QString scriptContent = QLatin1String("(function() {")
+ + QString::fromUtf8(scriptFile.readAll())
+ + QLatin1String(";"
+ " if (typeof Component == \"undefined\")"
+ " throw \"Missing Component constructor. Please check your script.\";"
+ "})();");
+
+ // if the user isn't aware of the downloadable archives value we will add it automatically later
+ foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive"))
+ || scriptContent.contains(QLatin1String("removeDownloadableArchive"));
+
+ static QInstaller::ScriptEngine testScriptEngine;
+ const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName());
+ if (value.isError()) {
+ throw QInstaller::Error(QString::fromLatin1("Exception while loading component "
+ "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()),
+ value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") :
+ value.toString() + QStringLiteral(" on line number: ") +
+ value.property(QStringLiteral("lineNumber")).toString()));
+ }
+
+ const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script));
+ copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript);
+ }
+}
+
void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QString &repoDir,
PackageInfoVector *const infos, const QString &archiveSuffix, Compression compression)
{
diff --git a/src/libs/ifwtools/repositorygen.h b/src/libs/ifwtools/repositorygen.h
index 054c023f4..7ad3dd073 100644
--- a/src/libs/ifwtools/repositorygen.h
+++ b/src/libs/ifwtools/repositorygen.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -88,6 +88,7 @@ void IFWTOOLS_EXPORT compressMetaDirectories(const QString &repoDir, const QStri
QStringList unifyMetadata(const QString &repoDir, const QString &existingRepoDir, QDomDocument doc);
void splitMetadata(const QStringList &entryList, const QString &repoDir, QDomDocument doc,
const QHash<QString, QString> &versionMapping);
+void copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir);
void IFWTOOLS_EXPORT copyMetaData(const QString &outDir, const QString &dataDir, const PackageInfoVector &packages,
const QString &appName, const QString& appVersion, const QStringList &uniteMetadatas);
diff --git a/src/libs/installer/abstracttask.h b/src/libs/installer/abstracttask.h
index 7daff848f..5ee23bef5 100644
--- a/src/libs/installer/abstracttask.h
+++ b/src/libs/installer/abstracttask.h
@@ -31,7 +31,9 @@
#include "runextensions.h"
+#include <QException>
#include <QFutureInterface>
+#include <QVariant>
namespace QInstaller {
diff --git a/src/libs/installer/binaryformatengine.cpp b/src/libs/installer/binaryformatengine.cpp
index ec6926031..681e6db79 100644
--- a/src/libs/installer/binaryformatengine.cpp
+++ b/src/libs/installer/binaryformatengine.cpp
@@ -28,7 +28,7 @@
#include "binaryformatengine.h"
-#include <QRegExp>
+#include <QRegularExpression>
namespace {
@@ -263,15 +263,17 @@ QStringList BinaryFormatEngine::entryList(QDir::Filters filters, const QStringLi
if (filterNames.isEmpty())
return result;
- QList<QRegExp> regexps;
- foreach (const QString &i, filterNames)
- regexps.append(QRegExp(i, Qt::CaseInsensitive, QRegExp::Wildcard));
+ QList<QRegularExpression> regexps;
+ for (const QString &i : filterNames) {
+ regexps.append(QRegularExpression(QRegularExpression::wildcardToRegularExpression(i),
+ QRegularExpression::CaseInsensitiveOption));
+ }
QStringList entries;
- foreach (const QString &i, result) {
+ for (const QString &i : qAsConst(result)) {
bool matched = false;
- foreach (const QRegExp &reg, regexps) {
- matched = reg.exactMatch(i);
+ for (const QRegularExpression &reg : qAsConst(regexps)) {
+ matched = reg.match(i).hasMatch();
if (matched)
break;
}
diff --git a/src/libs/installer/calculatorbase.cpp b/src/libs/installer/calculatorbase.cpp
new file mode 100644
index 000000000..4f1732677
--- /dev/null
+++ b/src/libs/installer/calculatorbase.cpp
@@ -0,0 +1,76 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 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 "calculatorbase.h"
+
+#include "component.h"
+
+namespace QInstaller {
+
+CalculatorBase::CalculatorBase(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+CalculatorBase::~CalculatorBase()
+{
+}
+
+void CalculatorBase::insertResolution(Component *component, const Resolution resolutionType
+ , const QString &referencedComponent)
+{
+ // Keep the first reason
+ if (m_componentNameResolutionHash.contains(component->name()))
+ return;
+
+ m_componentNameResolutionHash.insert(component->name(),
+ QPair<Resolution, QString>(resolutionType, referencedComponent));
+}
+
+QList<Component *> CalculatorBase::resolvedComponents() const
+{
+ return m_resolvedComponents;
+}
+
+CalculatorBase::Resolution CalculatorBase::resolutionType(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).first;
+}
+
+QString CalculatorBase::error() const
+{
+ return m_errorString;
+}
+
+QString CalculatorBase::referencedComponent(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).second;
+}
+
+} // namespace QInstaller
+
diff --git a/src/libs/installer/calculatorbase.h b/src/libs/installer/calculatorbase.h
new file mode 100644
index 000000000..baf51c5b4
--- /dev/null
+++ b/src/libs/installer/calculatorbase.h
@@ -0,0 +1,84 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 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$
+**
+**************************************************************************/
+
+#ifndef CALCULATORBASE_H
+#define CALCULATORBASE_H
+
+#include "installer_global.h"
+
+#include <QList>
+#include <QString>
+#include <QMetaEnum>
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCore;
+
+class INSTALLER_EXPORT CalculatorBase
+{
+public:
+ enum class Resolution {
+ Selected = 0, // "Selected Component(s) without Dependencies" / "Deselected Component(s)"
+ Replaced, // "Component(s) replaced by other components"
+ VirtualDependent, // "No dependencies to virtual component"
+ Dependent, // "Added as dependency for %1." / "Removed as dependency component is removed"
+ Automatic, // "Component(s) added as automatic dependencies" / "Removed as autodependency component is removed"
+ Resolved // "Component(s) that have resolved Dependencies"
+ };
+
+ CalculatorBase(PackageManagerCore *core);
+ virtual ~CalculatorBase() = 0;
+
+ virtual bool solve(const QList<Component *> &components) = 0;
+ void insertResolution(Component *component, const Resolution resolutionType,
+ const QString &referencedComponent = QString());
+
+ QList<Component *> resolvedComponents() const;
+ virtual QString resolutionText(Component *component) const = 0;
+ Resolution resolutionType(Component *component) const;
+
+ QString error() const;
+
+protected:
+ virtual bool solveComponent(Component *component, const QString &version = QString()) = 0;
+ QString referencedComponent(Component *component) const;
+
+protected:
+ PackageManagerCore *m_core;
+ QString m_errorString;
+
+ QList<Component *> m_resolvedComponents;
+ QHash<QString, QPair<Resolution, QString> > m_componentNameResolutionHash;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::CalculatorBase::Resolution)
+
+#endif // CALCULATORBASE_H
diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp
index 937c966ba..a91ab0128 100644
--- a/src/libs/installer/commandlineparser.cpp
+++ b/src/libs/installer/commandlineparser.cpp
@@ -201,7 +201,7 @@ CommandLineParser::CommandLineParser()
QLatin1String("[CLI] Automatically sets the QFileDialog values getExistingDirectory() or getOpenFileName() "
"requested by install script. "
"Several identifier=value pairs can be given separated with comma, "
- "for example --file-query filedialog.id=C:\Temp,filedialog.id2=C:\Temp2"),
+ "for example --file-query filedialog.id=C:/Temp,filedialog.id2=C:/Temp2"),
QLatin1String("identifier=value")), CommandLineOnly);
addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scConfirmCommandShort
<< CommandLineOptions::scConfirmCommandLong, QLatin1String("[CLI] Confirms starting of "
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index 77a4775c4..edd03a191 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -37,13 +37,13 @@
#include "remoteclient.h"
#include "settings.h"
#include "utils.h"
+#include "constants.h"
#include "updateoperationfactory.h"
#include <productkeycheck.h>
#include <QtCore/QDirIterator>
-#include <QtCore/QRegExp>
#include <QtCore/QTranslator>
#include <QtCore/QRegularExpression>
@@ -64,17 +64,6 @@
using namespace QInstaller;
-static const QLatin1String scScriptTag("Script");
-static const QLatin1String scVirtual("Virtual");
-static const QLatin1String scInstalled("Installed");
-static const QLatin1String scUpdateText("UpdateText");
-static const QLatin1String scUninstalled("Uninstalled");
-static const QLatin1String scCurrentState("CurrentState");
-static const QLatin1String scForcedInstallation("ForcedInstallation");
-static const QLatin1String scCheckable("Checkable");
-static const QLatin1String scExpandedByDefault("ExpandedByDefault");
-static const QLatin1String scUnstable("Unstable");
-
/*!
\enum QInstaller::Component::UnstableError
@@ -253,7 +242,7 @@ static const QLatin1String scUnstable("Unstable");
*/
Component::Component(PackageManagerCore *core)
: d(new ComponentPrivate(core, this))
- , m_defaultArchivePath(QLatin1String("@TargetDir@"))
+ , m_defaultArchivePath(scTargetDirPlaceholder)
{
setPrivate(d);
@@ -294,11 +283,11 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package)
setValue(scVersion, package.version);
setValue(scInheritVersion, package.inheritVersionFrom);
setValue(scInstalledVersion, package.version);
- setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString());
- setValue(QLatin1String("InstallDate"), package.installDate.toString());
+ setValue(scLastUpdateDate, package.lastUpdateDate.toString());
+ setValue(scInstallDate, package.installDate.toString());
setValue(scUncompressedSize, QString::number(package.uncompressedSize));
- setValue(scDependencies, package.dependencies.join(QLatin1String(",")));
- setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(",")));
+ setValue(scDependencies, package.dependencies.join(scCommaWithSpace));
+ setValue(scAutoDependOn, package.autoDependencies.join(scCommaWithSpace));
setValue(scSortingPriority, QString::number(package.sortingPriority));
setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse);
@@ -345,8 +334,7 @@ void Component::loadDataFromPackage(const Package &package)
setValue(scUpdateText, package.data(scUpdateText).toString());
setValue(scNewComponent, package.data(scNewComponent).toString());
setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString());
-
- setValue(scScriptTag, package.data(scScriptTag).toString());
+ d->m_scriptHash = package.data(scScriptTag).toHash();
setValue(scReplaces, package.data(scReplaces).toString());
setValue(scReleaseDate, package.data(scReleaseDate).toString());
setValue(scCheckable, package.data(scCheckable).toString());
@@ -357,8 +345,9 @@ void Component::loadDataFromPackage(const Package &package)
forced = scFalse;
setValue(scForcedInstallation, forced);
setValue(scContentSha1, package.data(scContentSha1).toString());
+ setValue(scCheckSha1CheckSum, package.data(scCheckSha1CheckSum, scTrue).toString().toLower());
- const auto treeNamePair = package.data(QLatin1String(scTreeName)).value<QPair<QString, bool>>();
+ const auto treeNamePair = package.data(scTreeName).value<QPair<QString, bool>>();
setValue(scTreeName, treeNamePair.first);
d->m_treeNameMoveChildren = treeNamePair.second;
@@ -366,20 +355,20 @@ void Component::loadDataFromPackage(const Package &package)
return;
setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url));
- const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
- if (!uis.isEmpty())
- loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis);
+
+ const QStringList uiList = QInstaller::splitStringWithComma(package.data(scUserInterfaces).toString());
+ if (!uiList.isEmpty())
+ loadUserInterfaces(QDir(scTwoArgs.arg(localTempPath(), name())), uiList);
+
#ifndef IFW_DISABLE_TRANSLATIONS
- const QStringList qms = package.data(QLatin1String("Translations")).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ const QStringList qms = QInstaller::splitStringWithComma(package.data(scTranslations).toString());
if (!qms.isEmpty())
- loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms);
+ loadTranslations(QDir(scTwoArgs.arg(localTempPath(), name())), qms);
#endif
- QHash<QString, QVariant> licenseHash = package.data(QLatin1String("Licenses")).toHash();
+ QHash<QString, QVariant> licenseHash = package.data(scLicenses).toHash();
if (!licenseHash.isEmpty())
- loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash);
- QVariant operationsVariant = package.data(QLatin1String("Operations"));
+ loadLicenses(scTwoArgs.arg(localTempPath(), name()), licenseHash);
+ QVariant operationsVariant = package.data(scOperations);
if (operationsVariant.canConvert<QList<QPair<QString, QVariant>>>())
m_operationsList = operationsVariant.value<QList<QPair<QString, QVariant>>>();
}
@@ -599,38 +588,45 @@ bool Component::treeNameMoveChildren() const
}
/*!
- Loads the component script into the script engine.
+ Loads the component script into the script engine. Call this method with
+ \a postLoad \c true to a list of components that are updated or installed
+ to improve performance if the amount of components is huge and there are no script
+ functions that need to be called before the installation starts.
*/
-void Component::loadComponentScript()
+void Component::loadComponentScript(const bool postLoad)
{
- const QString script = d->m_vars.value(scScriptTag);
- if (!localTempPath().isEmpty() && !script.isEmpty())
- loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script));
+ const QString installScript(!postLoad ? d->m_scriptHash.value(scInstallScript).toString()
+ : d->m_scriptHash.value(scPostLoadScript).toString());
+
+ if (!localTempPath().isEmpty() && !installScript.isEmpty()) {
+ evaluateComponentScript(scThreeArgs.arg(localTempPath(), name()
+ , installScript), postLoad);
+ }
}
/*!
- Loads the script at \a fileName into the script engine. The installer and all its
- components as well as other useful things are being exported into the script.
- For more information, see \l{Component Scripting}.
-
- Throws an error when either the script at \a fileName could not be opened, or QScriptEngine
- could not evaluate the script.
+ \internal
*/
-void Component::loadComponentScript(const QString &fileName)
+void Component::evaluateComponentScript(const QString &fileName, const bool postScriptContent)
{
// introduce the component object as javascript value and call the name to check that it
// was successful
try {
- d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
- } catch (const Error &error) {
- if (packageManagerCore()->settings().allowUnstableComponents()) {
- setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
- qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ if (postScriptContent) {
+ d->m_postScriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
} else {
- throw error;
+ d->m_scriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
}
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
+ // evaluateComponentScript is called with postScriptContent after we have selected components
+ // and are about to install. Do not allow install if unstable components are allowed
+ // as we then end up installing a component which has invalid script.
+ if (!packageManagerCore()->settings().allowUnstableComponents() || postScriptContent)
+ throw error;
}
emit loaded();
@@ -644,7 +640,7 @@ void Component::loadComponentScript(const QString &fileName)
*/
void Component::languageChanged()
{
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("retranslateUi"));
+ callScriptMethod(scRetranslateUi);
}
/*!
@@ -656,7 +652,7 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
{
QDirIterator it(directory.path(), qms, QDir::Files);
const QStringList translations = d->m_core->settings().translations();
- const QString uiLanguage = QLocale().uiLanguages().value(0, QLatin1String("en"));
+ const QString uiLanguage = QLocale().uiLanguages().value(0, scEn);
while (it.hasNext()) {
const QString filename = it.next();
const QString basename = QFileInfo(filename).baseName();
@@ -664,7 +660,7 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
if (!translations.isEmpty()) {
bool found = false;
foreach (const QString &translation, translations)
- found |= translation.startsWith(QLatin1String("ifw_") + basename, Qt::CaseInsensitive);
+ found |= translation.startsWith(scIfw_ + basename, Qt::CaseInsensitive);
if (!found) // don't load the file if it does match the UI language but is not allowed to be used
continue;
} else if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive)) {
@@ -693,8 +689,8 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
while (it.hasNext()) {
QFile file(it.next());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg(
- it.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
static QUiLoader loader;
@@ -702,8 +698,8 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
loader.setLanguageChangeEnabled(true);
QWidget *const widget = loader.load(&file, 0);
if (!widget) {
- throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg(
- it.fileName(), loader.errorString()));
+ throw Error(tr("Cannot load the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), loader.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
d->scriptEngine()->newQObject(widget);
d->m_userInterfaces.insert(widget->objectName(), widget);
@@ -719,7 +715,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QHash<QString, QVariant>::const_iterator it;
for (it = licenseHash.begin(); it != licenseHash.end(); ++it) {
QVariantMap license = it.value().toMap();
- const QString &fileName = license.value(QLatin1String("file")).toString();
+ const QString &fileName = license.value(scFile).toString();
if (!ProductKeyCheck::instance()->isValidLicenseTextFile(fileName))
continue;
@@ -731,7 +727,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QList<QFileInfo> fileCandidates;
foreach (const QString &locale, QInstaller::localeCandidates(lang)) {
- fileCandidates << QFileInfo(QString::fromLatin1("%1%2_%3.%4").arg(
+ fileCandidates << QFileInfo(scLocalesArgs.arg(
directory, fileInfo.baseName(), locale,
fileInfo.completeSuffix()));
}
@@ -748,12 +744,12 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QFile file(fileInfo.filePath());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested license file \"%1\": %2").arg(
- file.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested license file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ file.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
QTextStream stream(&file);
stream.setCodec("UTF-8");
- license.insert(QLatin1String("content"), stream.readAll());
+ license.insert(scContent, stream.readAll());
d->m_licenses.insert(it.key(), license);
}
}
@@ -764,8 +760,8 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
*/
void Component::loadXMLOperations()
{
- for (auto operation: m_operationsList) {
- if (operation.first != QLatin1String("Extract"))
+ for (auto operation: qAsConst(m_operationsList)) {
+ if (operation.first != scExtract)
addOperation(operation.first, operation.second.toStringList());
}
}
@@ -776,14 +772,14 @@ void Component::loadXMLOperations()
*/
void Component::loadXMLExtractOperations()
{
- for (auto operation: m_operationsList) {
- if (operation.first == QLatin1String("Extract")) {
+ for (auto &operation: qAsConst(m_operationsList)) {
+ if (operation.first == scExtract) {
// Create hash for Extract operations. Operation has a mandatory extract folder as
// first argument and optional archive name as second argument.
const QStringList &operationArgs = operation.second.toStringList();
if (operationArgs.count() == 2) {
const QString archiveName = value(scVersion) + operationArgs.at(1);
- const QString archivePath = QString::fromLatin1("installer://%1/%2").arg(name()).arg(archiveName);
+ const QString archivePath = scInstallerPrefixWithTwoArgs.arg(name()).arg(archiveName);
m_archivesHash.insert(archivePath, operationArgs.at(0));
} else if (operationArgs.count() == 1) {
m_defaultArchivePath = operationArgs.at(0);
@@ -841,25 +837,23 @@ void Component::createOperationsForPath(const QString &path)
const QFileInfo fi(path);
// don't copy over a checksum file
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(scCreateOperationsForPath, QJSValueList() << path).isUndefined())
+ return;
QString target;
- static const QString prefix = QString::fromLatin1("installer://");
- target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length()));
+ static const QString prefix = scInstallerPrefix;
+ target = scTargetDirPlaceholderWithArg.arg(path.mid(prefix.length() + name().length()));
if (fi.isFile()) {
- static const QString copy = QString::fromLatin1("Copy");
+ static const QString copy = scCopy;
addOperation(copy, QStringList() << fi.filePath() << target);
} else if (fi.isDir()) {
qApp->processEvents();
- static const QString mkdir = QString::fromLatin1("Mkdir");
+ static const QString mkdir = scMkdir;
addOperation(mkdir, QStringList(target));
QDirIterator it(fi.filePath());
@@ -887,14 +881,12 @@ void Component::createOperationsForArchive(const QString &archive)
const QFileInfo fi(archive);
// don't do anything with sha1 files
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(scCreateOperationsForArchive, QJSValueList() << archive).isUndefined())
+ return;
QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive));
const bool isZip = (archiveFile && archiveFile->open(QIODevice::ReadOnly) && archiveFile->isSupported());
@@ -902,9 +894,9 @@ void Component::createOperationsForArchive(const QString &archive)
if (isZip) {
// component.xml can override this value
if (m_archivesHash.contains(archive))
- addOperation(QLatin1String("Extract"), QStringList() << archive << m_archivesHash.value(archive));
+ addOperation(scExtract, QStringList() << archive << m_archivesHash.value(archive));
else
- addOperation(QLatin1String("Extract"), QStringList() << archive << m_defaultArchivePath);
+ addOperation(scExtract, QStringList() << archive << m_defaultArchivePath);
} else {
createOperationsForPath(archive);
}
@@ -916,7 +908,7 @@ void Component::createOperationsForArchive(const QString &archive)
void Component::beginInstallation()
{
// the script can override this method
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("beginInstallation"));
+ callScriptMethod(scBeginInstallation);
}
/*!
@@ -926,10 +918,9 @@ void Component::beginInstallation()
void Component::createOperations()
{
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperations"))
- .isUndefined()) {
- d->m_operationsCreated = true;
- return;
+ if (!callScriptMethod(scCreateOperations).isUndefined()) {
+ d->m_operationsCreated = true;
+ return;
}
loadXMLExtractOperations();
foreach (const QString &archive, archives())
@@ -966,10 +957,11 @@ QList<QPair<QString, bool> > Component::pathsForUninstallation() const
*/
QStringList Component::archives() const
{
- QString pathString = QString::fromLatin1("installer://%1/").arg(name());
+ static const QRegularExpression regExp(scCaretSymbol);
+ QString pathString = scInstallerPrefixWithOneArgs.arg(name());
QStringList archivesNameList = QDir(pathString).entryList();
//RegExp "^" means line beginning
- archivesNameList.replaceInStrings(QRegExp(QLatin1String("^")), pathString);
+ archivesNameList.replaceInStrings(regExp, pathString);
return archivesNameList;
}
@@ -989,6 +981,15 @@ void Component::addDownloadableArchive(const QString &path)
}
/*!
+ \internal
+*/
+void Component::addDownloadableArchives(const QString& archives)
+{
+ Q_ASSERT(isFromOnlineRepository());
+ d->m_downloadableArchivesVariable = archives;
+}
+
+/*!
Removes the archive \a path previously added via addDownloadableArchive() from this component.
This can only be called if this component was downloaded from an online repository.
@@ -1003,9 +1004,15 @@ void Component::removeDownloadableArchive(const QString &path)
/*!
Returns the archives to be downloaded from the online repository before installation.
+ Should be called only once when the installation starts.
*/
-QStringList Component::downloadableArchives() const
+QStringList Component::downloadableArchives()
{
+ const QStringList downloadableArchives = d->m_downloadableArchivesVariable
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ foreach (const QString downloadableArchive, downloadableArchives)
+ addDownloadableArchive(downloadableArchive);
+
return d->m_downloadableArchives;
}
@@ -1066,23 +1073,23 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons
if (!d->m_minimumProgressOperation) {
d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("MinimumProgress"), d->m_core);
- d->m_minimumProgressOperation->setValue(QLatin1String("component"), name());
+ .create(scMinimumProgress, d->m_core);
+ d->m_minimumProgressOperation->setValue(scComponentSmall, name());
d->m_operations.append(d->m_minimumProgressOperation);
}
if (!d->m_licenses.isEmpty()) {
d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("License"), d->m_core);
- d->m_licenseOperation->setValue(QLatin1String("component"), name());
+ .create(scLicense, d->m_core);
+ d->m_licenseOperation->setValue(scComponentSmall, name());
QVariantMap licenses;
const QList<QVariantMap> values = d->m_licenses.values();
for (int i = 0; i < values.count(); ++i) {
- licenses.insert(values.at(i).value(QLatin1String("file")).toString(),
- values.at(i).value(QLatin1String("content")));
+ licenses.insert(values.at(i).value(scFile).toString(),
+ values.at(i).value(scContent));
}
- d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses);
+ d->m_licenseOperation->setValue(scLicenses, licenses);
d->m_operations.append(d->m_licenseOperation);
}
}
@@ -1102,7 +1109,7 @@ void Component::addOperation(Operation *operation)
{
d->m_operations.append(operation);
if (RemoteClient::instance().isActive())
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1112,7 +1119,7 @@ void Component::addOperation(Operation *operation)
void Component::addElevatedOperation(Operation *operation)
{
addOperation(operation);
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1167,8 +1174,8 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
return operation;
}
- if (operation->name() == QLatin1String("Delete"))
- operation->setValue(QLatin1String("performUndo"), false);
+ if (operation->name() == scDelete)
+ operation->setValue(scPerformUndo, false);
// Operation can contain variables which are resolved when performing the operation
if (operation->requiresUnreplacedVariables())
@@ -1177,7 +1184,7 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
operation->setArguments(d->m_core->replaceVariables(parameters));
- operation->setValue(QLatin1String("component"), name());
+ operation->setValue(scComponentSmall, name());
return operation;
}
@@ -1196,6 +1203,21 @@ void Component::markComponentUnstable(Component::UnstableError error, const QStr
setValue(scUnstable, scTrue);
QMetaEnum metaEnum = QMetaEnum::fromType<Component::UnstableError>();
emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name());
+
+ // Update the description and tooltip texts to contain
+ // information about the unstable error.
+ updateModelData(scDescription, QString());
+}
+
+QJSValue Component::callScriptMethod(const QString &methodName, const QJSValueList &arguments) const
+{
+ QJSValue scriptContext;
+ if (!d->m_postScriptContext.isUndefined() && d->m_postScriptContext.property(methodName).isCallable())
+ scriptContext = d->m_postScriptContext;
+ else
+ scriptContext = d->m_scriptContext;
+ return d->scriptEngine()->callScriptMethod(scriptContext,
+ methodName, arguments);
}
namespace {
@@ -1355,7 +1377,7 @@ void Component::setValidatorCallbackName(const QString &name)
bool Component::validatePage()
{
if (!validatorCallbackName.isEmpty())
- return d->scriptEngine()->callScriptMethod(d->m_scriptContext, validatorCallbackName).toBool();
+ return callScriptMethod(validatorCallbackName).toBool();
return true;
}
@@ -1374,7 +1396,7 @@ void Component::addDependency(const QString &newDependency)
if (oldDependencies.isEmpty())
setValue(scDependencies, newDependency);
else
- setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency);
+ setValue(scDependencies, oldDependencies + scCommaWithSpace + newDependency);
}
/*!
@@ -1382,7 +1404,7 @@ void Component::addDependency(const QString &newDependency)
*/
QStringList Component::dependencies() const
{
- return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scDependencies));
}
/*!
@@ -1390,7 +1412,7 @@ QStringList Component::dependencies() const
*/
QStringList Component::localDependencies() const
{
- return d->m_vars.value(scLocalDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scLocalDependencies));
}
/*!
@@ -1408,7 +1430,7 @@ void Component::addAutoDependOn(const QString &newDependOn)
if (oldDependOn.isEmpty())
setValue(scAutoDependOn, newDependOn);
else
- setValue(scAutoDependOn, oldDependOn + QLatin1String(", ") + newDependOn);
+ setValue(scAutoDependOn, oldDependOn + scCommaWithSpace + newDependOn);
}
QStringList Component::autoDependencies() const
@@ -1500,8 +1522,7 @@ bool Component::isDefault() const
if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) {
QJSValue valueFromScript;
try {
- valueFromScript = d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("isDefault"));
+ valueFromScript = callScriptMethod(scIsDefault);
} catch (const Error &error) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()),
@@ -1735,23 +1756,26 @@ void Component::updateModelData(const QString &key, const QString &data)
setData(humanReadableSize(size), UncompressedSize);
}
- QString tooltipText;
- const QString &updateInfo = d->m_vars.value(scUpdateText);
- if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
- tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
- } else {
- tooltipText = d->m_vars.value(scDescription) + QLatin1String("<br><br>")
- + tr("Update Info: ") + updateInfo;
- }
- if (isUnstable()) {
- tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
+ if (key == scUpdateText || key == scDescription) {
+ QString tooltipText;
+ const QString &updateInfo = d->m_vars.value(scUpdateText);
+ if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
+ tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
+ } else {
+ tooltipText = d->m_vars.value(scDescription) + scBr + scBr
+ + tr("Update Info: ") + updateInfo;
+ }
+ if (isUnstable()) {
+ tooltipText += scBr + tr("There was an error loading the selected component. "
"This component cannot be installed.");
- }
- // replace {external-link}='' fields in component description with proper link tags
- tooltipText.replace(QRegularExpression(QLatin1String("{external-link}='(.*?)'")),
- QLatin1String("<a href=\"\\1\">\\1</a>"));
+ }
+ static const QRegularExpression externalLinkRegexp(QLatin1String("{external-link}='(.*?)'"));
+ static const QLatin1String externalLinkElement(QLatin1String("<a href=\"\\1\">\\1</a>"));
+ // replace {external-link}='' fields in component description with proper link tags
+ tooltipText.replace(externalLinkRegexp, externalLinkElement);
- setData(tooltipText, Qt::ToolTipRole);
+ setData(tooltipText, Qt::ToolTipRole);
+ }
}
/*!
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
index 35b0ea0b2..8f17b2d98 100644
--- a/src/libs/installer/component.h
+++ b/src/libs/installer/component.h
@@ -32,7 +32,6 @@
#include "constants.h"
#include "component_p.h"
#include "qinstallerglobal.h"
-#include "packagemanagercore.h"
#include <QtCore/QDir>
#include <QtCore/QMetaType>
@@ -44,6 +43,8 @@ QT_FORWARD_DECLARE_CLASS(QQmlV4Function)
namespace QInstaller {
+class PackageManagerCore;
+
class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper
{
Q_OBJECT
@@ -118,10 +119,9 @@ public:
void removeComponent(Component *component);
QList<Component*> descendantComponents() const;
- void loadComponentScript();
+ void loadComponentScript(const bool postLoad = false);
+ void evaluateComponentScript(const QString &fileName, const bool postScriptContext = false);
- //move this to private
- void loadComponentScript(const QString &fileName);
void loadTranslations(const QDir &directory, const QStringList &qms);
void loadUserInterfaces(const QDir &directory, const QStringList &uis);
void loadLicenses(const QString &directory, const QHash<QString, QVariant> &hash);
@@ -150,9 +150,10 @@ public:
Q_INVOKABLE bool addElevatedOperation(QQmlV4Function *args);
bool addElevatedOperation(const QString &operation, const QStringList &parameters);
- QStringList downloadableArchives() const;
+ QStringList downloadableArchives();
Q_INVOKABLE void addDownloadableArchive(const QString &path);
Q_INVOKABLE void removeDownloadableArchive(const QString &path);
+ void addDownloadableArchives(const QString& archives);
QStringList stopProcessForUpdateRequests() const;
Q_INVOKABLE void addStopProcessForUpdateRequest(const QString &process);
@@ -237,6 +238,8 @@ private:
Operation *createOperation(const QString &operationName, const QStringList &parameters);
void markComponentUnstable(const Component::UnstableError error, const QString &errorMessage);
+ QJSValue callScriptMethod(const QString &methodName, const QJSValueList &arguments = QJSValueList()) const;
+
private:
QString validatorCallbackName;
ComponentPrivate *d;
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 4030d266e..7cf47c925 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,6 +55,9 @@ ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq)
, m_operationsCreatedSuccessfully(true)
, m_updateIsAvailable(false)
, m_treeNameMoveChildren(false)
+ , m_postLoadScript(false)
+ , m_scriptContext(QJSValue::UndefinedValue)
+ , m_postScriptContext(QJSValue::UndefinedValue)
{
}
@@ -150,7 +153,7 @@ void ComponentModelHelper::setEnabled(bool enabled)
*/
bool ComponentModelHelper::isTristate() const
{
- return (flags() & Qt::ItemIsTristate) != 0;
+ return (flags() & Qt::ItemIsAutoTristate) != 0;
}
/*!
@@ -161,7 +164,7 @@ bool ComponentModelHelper::isTristate() const
*/
void ComponentModelHelper::setTristate(bool tristate)
{
- changeFlags(tristate, Qt::ItemIsTristate);
+ changeFlags(tristate, Qt::ItemIsAutoTristate);
}
/*!
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
index bdc67898d..dea2d4935 100644
--- a/src/libs/installer/component_p.h
+++ b/src/libs/installer/component_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -64,17 +64,21 @@ public:
bool m_operationsCreatedSuccessfully;
bool m_updateIsAvailable;
bool m_treeNameMoveChildren;
+ bool m_postLoadScript;
QString m_componentName;
QUrl m_repositoryUrl;
QString m_localTempPath;
QJSValue m_scriptContext;
+ QJSValue m_postScriptContext;
QHash<QString, QString> m_vars;
QList<Component*> m_childComponents;
QList<Component*> m_allChildComponents;
QStringList m_downloadableArchives;
+ QString m_downloadableArchivesVariable;
QStringList m_stopProcessForUpdateRequests;
QHash<QString, QPointer<QWidget> > m_userInterfaces;
+ QHash<QString, QVariant> m_scriptHash;
// < display name, < file name, file content > >
QHash<QString, QVariantMap> m_licenses;
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 23e8680f6..dbb80f2f6 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -47,6 +47,8 @@ namespace QInstaller {
This enum value holds the checked state of the components available for
installation.
+ \value Empty
+ The model does not contain any components.
\value AllChecked
All components are checked.
\value AllUnchecked
@@ -392,7 +394,7 @@ void ComponentModel::reset(QList<Component *> rootComponents)
m_uncheckable.clear();
m_indexByNameCache.clear();
m_rootComponentList.clear();
- m_modelState = DefaultChecked;
+ m_modelState = !rootComponents.isEmpty() ? DefaultChecked : Empty;
// Initialize these with an empty set for every possible state, cause we compare the hashes later in
// updateAndEmitModelState(). The comparison than might lead to wrong results if one of the checked
@@ -481,6 +483,10 @@ void ComponentModel::postModelReset()
void ComponentModel::updateAndEmitModelState()
{
+ if (m_rootComponentList.isEmpty()) {
+ m_modelState = ComponentModel::Empty;
+ return;
+ }
m_modelState = ComponentModel::DefaultChecked;
if (m_initialCheckedState != m_currentCheckedState)
m_modelState = ComponentModel::PartiallyChecked;
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index 26cd888a3..1e8a2d297 100644
--- a/src/libs/installer/componentmodel.h
+++ b/src/libs/installer/componentmodel.h
@@ -31,6 +31,8 @@
#include "qinstallerglobal.h"
+#include "component.h"
+
#include <QtCore/QAbstractItemModel>
#include <QtCore/QList>
#include <QtCore/QSet>
@@ -38,7 +40,6 @@
namespace QInstaller {
-class Component;
class PackageManagerCore;
class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
@@ -49,6 +50,7 @@ class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
public:
enum ModelStateFlag {
+ Empty = -0x01,
AllChecked = 0x01,
AllUnchecked = 0x02,
DefaultChecked = 0x04,
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index 1cb10336c..7f82ef47d 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -28,6 +28,7 @@
#include "componentselectionpage_p.h"
+#include "globals.h"
#include "packagemanagergui.h"
#include "componentmodel.h"
#include "settings.h"
@@ -48,8 +49,10 @@
#include <QFileDialog>
#include <QStackedLayout>
#include <QStackedWidget>
-#include <QToolBox>
#include <QLineEdit>
+#include <QComboBox>
+#include <QStandardItemModel>
+#include <QStyledItemDelegate>
namespace QInstaller {
@@ -59,6 +62,11 @@ namespace QInstaller {
\internal
*/
+constexpr int scNoCheckSelectionIndex = -1;
+constexpr int scCheckDefaultIndex = 0;
+constexpr int scCheckAllIndex = 1;
+constexpr int scUncheckAllIndex = 2;
+
ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core)
: q(qq)
, m_core(core)
@@ -67,7 +75,7 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
, m_updaterModel(m_core->updaterComponentModel())
, m_currentModel(m_allModel)
, m_allowCompressedRepositoryInstall(false)
- , m_toolBox(nullptr)
+ , m_tabWidget(nullptr)
, m_descriptionBaseWidget(nullptr)
, m_categoryWidget(Q_NULLPTR)
, m_categoryLayoutVisible(false)
@@ -87,8 +95,12 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
descriptionVLayout->setContentsMargins(0, 0, 0, 0);
- m_toolBox = new QToolBox(q);
- m_toolBox->setObjectName(QLatin1String("ToolBox"));
+ m_tabWidget = new QTabWidget(q);
+ m_tabWidget->setObjectName(QLatin1String("ComponentSelectionTabWidget"));
+ m_tabWidget->tabBar()->setObjectName(QLatin1String("ComponentSelectionTabBar"));
+ m_tabWidget->hide();
+
+ m_rightSideVLayout = new QVBoxLayout;
QScrollArea *descriptionScrollArea = new QScrollArea(q);
descriptionScrollArea->setWidgetResizable(true);
@@ -109,46 +121,46 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
descriptionVLayout->addWidget(m_sizeLabel);
- QHBoxLayout *buttonHLayout = new QHBoxLayout;
- m_checkDefault = new QPushButton;
- connect(m_checkDefault, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectDefault);
+ m_qbspPushButton = new QPushButton(q);
+ m_qbspPushButton->setVisible(false);
+ m_qbspPushButton->setText(ComponentSelectionPage::tr("Browse &QBSP files"));
+ m_qbspPushButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_qbspPushButton->setToolTip(
+ ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
+ "additional content that is not directly available from the online repositories."));
+
+ connect(m_qbspPushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::qbspButtonClicked);
+
+ m_rightSideVLayout->addWidget(m_descriptionBaseWidget);
+ m_rightSideVLayout->addWidget(m_qbspPushButton, 0, Qt::AlignRight | Qt::AlignBottom);
+
+ QHBoxLayout *topHLayout = new QHBoxLayout;
+ m_checkStateComboBox = new QComboBox(q);
+ QStyledItemDelegate *delegate = new QStyledItemDelegate(this);
+ m_checkStateComboBox->setItemDelegate(delegate);
+ m_checkStateComboBox->setObjectName(QLatin1String("CheckStateComboBox"));
+ topHLayout->addWidget(m_checkStateComboBox);
+
+ connect(m_checkStateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &ComponentSelectionPagePrivate::updateAllCheckStates);
+
+ m_checkStateComboBox->setPlaceholderText(ComponentSelectionPage::tr("Select"));
if (m_core->isInstaller()) {
- m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A",
- "Select default components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("Def&ault"));
- m_checkDefault->setToolTip(ComponentSelectionPage::tr("Select default components in the tree view."));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Default"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Select default components in the tree view."), Qt::ToolTipRole);
} else {
- m_checkDefault->setEnabled(false);
- m_checkDefault->setObjectName(QLatin1String("ResetComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+R",
- "Reset to already installed components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("&Reset"));
- m_checkDefault->setToolTip(
- ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Reset"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."), Qt::ToolTipRole);
}
- buttonHLayout->addWidget(m_checkDefault);
-
- m_checkAll = new QPushButton;
- connect(m_checkAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectAll);
- m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton"));
- m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S",
- "Select all components")));
- m_checkAll->setText(ComponentSelectionPage::tr("&Select All"));
- m_checkAll->setToolTip(ComponentSelectionPage::tr("Select all components in the tree view."));
- buttonHLayout->addWidget(m_checkAll);
-
- m_uncheckAll = new QPushButton;
- connect(m_uncheckAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::deselectAll);
- m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton"));
- m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D",
- "Deselect all components")));
- m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All"));
- m_uncheckAll->setToolTip(ComponentSelectionPage::tr("Deselect all components in the tree view."));
- buttonHLayout->addWidget(m_uncheckAll);
+ m_checkStateComboBox->insertItem(scCheckAllIndex, ComponentSelectionPage::tr("Select All"));
+ m_checkStateComboBox->setItemData(scCheckAllIndex,
+ ComponentSelectionPage::tr("Select all components in the tree view."), Qt::ToolTipRole);
+ m_checkStateComboBox->insertItem(scUncheckAllIndex, ComponentSelectionPage::tr("Deselect All"));
+ m_checkStateComboBox->setItemData(scUncheckAllIndex,
+ ComponentSelectionPage::tr("Deselect all components in the tree view."), Qt::ToolTipRole);
QWidget *progressStackedWidget = new QWidget();
QVBoxLayout *metaLayout = new QVBoxLayout(progressStackedWidget);
@@ -168,17 +180,24 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
connect(m_searchLineEdit, &QLineEdit::textChanged,
this, &ComponentSelectionPagePrivate::setSearchPattern);
connect(q, &ComponentSelectionPage::entered, m_searchLineEdit, &QLineEdit::clear);
+ topHLayout->addWidget(m_searchLineEdit);
QVBoxLayout *treeViewVLayout = new QVBoxLayout;
treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
treeViewVLayout->addWidget(m_treeView, 3);
- treeViewVLayout->addWidget(m_searchLineEdit);
QWidget *mainStackedWidget = new QWidget();
m_mainGLayout = new QGridLayout(mainStackedWidget);
- m_mainGLayout->addLayout(buttonHLayout, 0, 0);
+ {
+ int left = 0;
+ int top = 0;
+ int bottom = 0;
+ m_mainGLayout->getContentsMargins(&left, &top, nullptr, &bottom);
+ m_mainGLayout->setContentsMargins(left, top, 0, bottom);
+ }
+ m_mainGLayout->addLayout(topHLayout, 0, 0);
m_mainGLayout->addLayout(treeViewVLayout, 1, 0);
- m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_mainGLayout->addLayout(m_rightSideVLayout, 0, 1, 0, -1);
m_mainGLayout->setColumnStretch(0, 3);
m_mainGLayout->setColumnStretch(1, 2);
@@ -214,29 +233,13 @@ void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall()
void ComponentSelectionPagePrivate::showCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && !(wizard->options() & QWizard::HaveCustomButton2) && m_allowCompressedRepositoryInstall) {
- wizard->setOption(QWizard::HaveCustomButton2, true);
- wizard->setButtonText(QWizard::CustomButton2,
- ComponentSelectionPage::tr("&Browse QBSP files"));
- wizard->button(QWizard::CustomButton2)->setToolTip(
- ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
- "additional content that is not directly available from the online repositories."));
- connect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ if (m_allowCompressedRepositoryInstall)
+ m_qbspPushButton->setVisible(true);
}
void ComponentSelectionPagePrivate::hideCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && (wizard->options() & QWizard::HaveCustomButton2)) {
- wizard->setOption(QWizard::HaveCustomButton2, false);
- disconnect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ m_qbspPushButton->setVisible(false);
}
void ComponentSelectionPagePrivate::setupCategoryLayout()
@@ -270,7 +273,7 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
vLayout->addWidget(m_categoryGroupBox);
vLayout->addStretch();
- m_toolBox->insertItem(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
+ m_tabWidget->insertTab(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
}
void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
@@ -283,21 +286,22 @@ void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
setupCategoryLayout();
if (show) {
- m_mainGLayout->removeWidget(m_descriptionBaseWidget);
- m_toolBox->insertItem(0, m_descriptionBaseWidget, tr("Component Information"));
- m_mainGLayout->addWidget(m_toolBox, 1, 1);
+ m_rightSideVLayout->removeWidget(m_descriptionBaseWidget);
+ m_tabWidget->insertTab(0, m_descriptionBaseWidget, tr("Information"));
+ m_rightSideVLayout->insertWidget(m_rightSideVLayout->count() - 1, m_tabWidget);
} else {
- m_toolBox->removeItem(0);
- m_mainGLayout->removeWidget(m_toolBox);
- m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_tabWidget->removeTab(0);
+ m_rightSideVLayout->removeWidget(m_tabWidget);
+ m_rightSideVLayout->insertWidget(m_rightSideVLayout->count() - 1, m_descriptionBaseWidget);
+ m_descriptionBaseWidget->setVisible(true);
}
- m_toolBox->setVisible(show);
+ m_tabWidget->setVisible(show);
m_categoryLayoutVisible = show;
}
void ComponentSelectionPagePrivate::updateTreeView()
{
- m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager());
+ setComboBoxItemEnabled(scCheckDefaultIndex, m_core->isInstaller() || m_core->isPackageManager());
if (m_treeView->selectionModel()) {
disconnect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
@@ -423,6 +427,32 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu
}
}
+/*!
+ Updates the checkstate of the components based on the value of \c which.
+*/
+void ComponentSelectionPagePrivate::updateAllCheckStates(int which)
+{
+ switch (which) {
+ case scNoCheckSelectionIndex:
+ // A 'helper' text index, no selection
+ return;
+ case scCheckDefaultIndex:
+ selectDefault();
+ break;
+ case scCheckAllIndex:
+ selectAll();
+ break;
+ case scUncheckAllIndex:
+ deselectAll();
+ break;
+ default:
+ qCWarning(lcInstallerInstallLog) << "Invalid index for check state selection!";
+ break;
+ }
+ // Reset back to 'helper' text index
+ m_checkStateComboBox->setCurrentIndex(scNoCheckSelectionIndex);
+}
+
void ComponentSelectionPagePrivate::selectAll()
{
m_currentModel->setCheckedState(ComponentModel::AllChecked);
@@ -461,8 +491,12 @@ void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
else
m_stackedLayout->setCurrentIndex(0);
- if (QAbstractButton *bspButton = q->gui()->button(QWizard::CustomButton2))
- bspButton->setEnabled(!show);
+ m_qbspPushButton->setEnabled(!show);
+
+ if (show) {
+ q->gui()->button(QWizard::NextButton)->setEnabled(false);
+ q->gui()->button(QWizard::BackButton)->setEnabled(false);
+ }
// In macOS 10.12 the widgets are not hidden if those are not updated immediately
#ifdef Q_OS_MACOS
@@ -488,31 +522,29 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults();
}
-void ComponentSelectionPagePrivate::customButtonClicked(int which)
+void ComponentSelectionPagePrivate::qbspButtonClicked()
{
- if (QWizard::WizardButton(which) == QWizard::CustomButton2) {
- QString defaultDownloadDirectory =
- QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
- QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
- ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
- QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
-
- QSet<Repository> set;
- foreach (QString fileName, fileNames) {
- Repository repository = Repository::fromUserInput(fileName, true);
- repository.setEnabled(true);
- set.insert(repository);
- }
- if (set.count() > 0) {
- updateWidgetVisibility(true);
- m_core->settings().addTemporaryRepositories(set, false);
- if (!m_core->fetchCompressedPackagesTree()) {
- MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
- QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
- }
+ QString defaultDownloadDirectory =
+ QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
+ ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
+ QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
+
+ QSet<Repository> set;
+ foreach (QString fileName, fileNames) {
+ Repository repository = Repository::fromUserInput(fileName, true);
+ repository.setEnabled(true);
+ set.insert(repository);
+ }
+ if (set.count() > 0) {
+ updateWidgetVisibility(true);
+ m_core->settings().addTemporaryRepositories(set, false);
+ if (!m_core->fetchCompressedPackagesTree()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
}
- updateWidgetVisibility(false);
}
+ updateWidgetVisibility(false);
}
/*!
@@ -546,6 +578,13 @@ void ComponentSelectionPagePrivate::selectDefault()
void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentModel::ModelState state)
{
+ if (state.testFlag(ComponentModel::Empty)) {
+ setComboBoxItemEnabled(scCheckAllIndex, false);
+ setComboBoxItemEnabled(scUncheckAllIndex, false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, false);
+ return;
+ }
+
m_componentsResolved = m_core->recalculateAllComponents();
if (!m_componentsResolved) {
const QString error = !m_core->componentsToInstallError().isEmpty()
@@ -563,9 +602,9 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod
state |= ComponentModel::AllUnchecked;
}
// enable the button if the corresponding flag is not set
- m_checkAll->setEnabled(state.testFlag(ComponentModel::AllChecked) == false);
- m_uncheckAll->setEnabled(state.testFlag(ComponentModel::AllUnchecked) == false);
- m_checkDefault->setEnabled(state.testFlag(ComponentModel::DefaultChecked) == false);
+ setComboBoxItemEnabled(scCheckAllIndex, state.testFlag(ComponentModel::AllChecked) == false);
+ setComboBoxItemEnabled(scUncheckAllIndex, state.testFlag(ComponentModel::AllUnchecked) == false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, state.testFlag(ComponentModel::DefaultChecked) == false);
// update the current selected node (important to reflect possible sub-node changes)
if (m_treeView->selectionModel())
@@ -619,4 +658,20 @@ void ComponentSelectionPagePrivate::restoreHeaderResizeModes()
m_treeView->header()->setSectionResizeMode(i, m_headerResizeModes.value(i));
}
+/*!
+ Sets the enabled state of the combo box item in \a index to \a enabled.
+*/
+void ComponentSelectionPagePrivate::setComboBoxItemEnabled(int index, bool enabled)
+{
+ auto *model = qobject_cast<QStandardItemModel *>(m_checkStateComboBox->model());
+ if (!model)
+ return;
+
+ QStandardItem *item = model->item(index);
+ if (!item)
+ return;
+
+ item->setEnabled(enabled);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index f06d376f4..ed68fafa4 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -48,7 +48,7 @@ class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QStackedLayout;
-class QToolBox;
+class QComboBox;
namespace QInstaller {
@@ -78,12 +78,13 @@ public:
public slots:
void currentSelectedChanged(const QModelIndex &current);
+ void updateAllCheckStates(int which);
void selectAll();
void deselectAll();
void enableRepositoryCategory(const QString &repositoryName, bool enable);
void updateWidgetVisibility(bool show);
void fetchRepositoryCategories();
- void customButtonClicked(int which);
+ void qbspButtonClicked();
void onProgressChanged(int progress);
void setMessage(const QString &msg);
void setTotalProgress(int totalProgress);
@@ -94,23 +95,24 @@ public slots:
private:
void storeHeaderResizeModes();
void restoreHeaderResizeModes();
+ void setComboBoxItemEnabled(int index, bool enabled);
private:
ComponentSelectionPage *q;
PackageManagerCore *m_core;
QTreeView *m_treeView;
- QToolBox *m_toolBox;
+ QTabWidget *m_tabWidget;
QWidget *m_descriptionBaseWidget;
QLabel *m_sizeLabel;
QLabel *m_descriptionLabel;
- QPushButton *m_checkAll;
- QPushButton *m_uncheckAll;
- QPushButton *m_checkDefault;
+ QPushButton *m_qbspPushButton;
+ QComboBox *m_checkStateComboBox;
QWidget *m_categoryWidget;
QGroupBox *m_categoryGroupBox;
QLabel *m_metadataProgressLabel;
QProgressBar *m_progressBar;
QGridLayout *m_mainGLayout;
+ QVBoxLayout *m_rightSideVLayout;
bool m_allowCompressedRepositoryInstall;
bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
diff --git a/src/libs/installer/concurrentoperationrunner.cpp b/src/libs/installer/concurrentoperationrunner.cpp
index dfcae44dd..b0eb5f582 100644
--- a/src/libs/installer/concurrentoperationrunner.cpp
+++ b/src/libs/installer/concurrentoperationrunner.cpp
@@ -156,7 +156,7 @@ QHash<Operation *, bool> ConcurrentOperationRunner::run()
this, &ConcurrentOperationRunner::onOperationfinished);
futureWatcher->setFuture(QtConcurrent::run(m_threadPool,
- this, &ConcurrentOperationRunner::runOperation, operation));
+ [this, operation] { return runOperation(operation); }));
}
if (!m_operationWatchers.isEmpty()) {
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 485982672..ecd85fe11 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -41,6 +41,7 @@ static const QLatin1String scScript("script");
static const QLatin1String scAllUsersStartMenuProgramsPath("AllUsersStartMenuProgramsPath");
static const QLatin1String scUserStartMenuProgramsPath("UserStartMenuProgramsPath");
static const QLatin1String scUILanguage("UILanguage");
+static const QLatin1String scUpdatesXML("Updates.xml");
static const QLatin1String scName("Name");
static const QLatin1String scVersion("Version");
@@ -72,11 +73,66 @@ static const QLatin1String scOfflineBinaryName("OfflineBinaryName");
static const QLatin1String scSHA1("SHA1");
static const QLatin1String scMetadataName("MetadataName");
static const QLatin1String scContentSha1("ContentSha1");
+static const QLatin1String scCheckSha1CheckSum("CheckSha1CheckSum");
+
+static const char *scClearCacheHint = QT_TR_NOOP(
+ "This may be solved by restarting the application after clearing the cache from:");
+
+// symbols
+static const QLatin1String scCaretSymbol("^");
+static const QLatin1String scCommaWithSpace(", ");
+static const QLatin1String scBr("<br>");
// constants used throughout the components class
static const QLatin1String scVirtual("Virtual");
static const QLatin1String scSortingPriority("SortingPriority");
static const QLatin1String scCheckable("Checkable");
+static const QLatin1String scScriptTag("Script");
+static const QLatin1String scInstalled("Installed");
+static const QLatin1String scUpdateText("UpdateText");
+static const QLatin1String scUninstalled("Uninstalled");
+static const QLatin1String scCurrentState("CurrentState");
+static const QLatin1String scForcedInstallation("ForcedInstallation");
+static const QLatin1String scExpandedByDefault("ExpandedByDefault");
+static const QLatin1String scUnstable("Unstable");
+static const QLatin1String scTargetDirPlaceholder("@TargetDir@");
+static const QLatin1String scTargetDirPlaceholderWithArg("@TargetDir@%1");
+static const QLatin1String scLastUpdateDate("LastUpdateDate");
+static const QLatin1String scInstallDate("InstallDate");
+static const QLatin1String scUserInterfaces("UserInterfaces");
+static const QLatin1String scTranslations("Translations");
+static const QLatin1String scLicenses("Licenses");
+static const QLatin1String scLicense("License");
+static const QLatin1String scOperations("Operations");
+static const QLatin1String scInstallScript("installScript");
+static const QLatin1String scPostLoadScript("postLoadScript");
+static const QLatin1String scComponent("Component");
+static const QLatin1String scComponentSmall("component");
+static const QLatin1String scRetranslateUi("retranslateUi");
+static const QLatin1String scEn("en");
+static const QLatin1String scIfw_("ifw_");
+static const QLatin1String scFile("file");
+static const QLatin1String scContent("content");
+static const QLatin1String scExtract("Extract");
+static const QLatin1String scSha1("sha1");
+static const QLatin1String scCreateOperationsForPath("createOperationsForPath");
+static const QLatin1String scCreateOperationsForArchive("createOperationsForArchive");
+static const QLatin1String scCreateOperations("createOperations");
+static const QLatin1String scBeginInstallation("beginInstallation");
+static const QLatin1String scMinimumProgress("MinimumProgress");
+static const QLatin1String scDelete("Delete");
+static const QLatin1String scCopy("Copy");
+static const QLatin1String scMkdir("Mkdir");
+static const QLatin1String scPerformUndo("performUndo");
+static const QLatin1String scIsDefault("isDefault");
+static const QLatin1String scAdmin("admin");
+static const QLatin1String scTwoArgs("%1/%2/");
+static const QLatin1String scThreeArgs("%1/%2/%3");
+static const QLatin1String scComponentScriptTest("var component = installer.componentByName('%1'); component.name;");
+static const QLatin1String scInstallerPrefix("installer://");
+static const QLatin1String scInstallerPrefixWithOneArgs("installer://%1/");
+static const QLatin1String scInstallerPrefixWithTwoArgs("installer://%1/%2");
+static const QLatin1String scLocalesArgs("%1%2_%3.%4");
// constants used throughout the settings and package manager core class
static const QLatin1String scTitle("Title");
diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp
index 6c92cc178..c3988a8ec 100644
--- a/src/libs/installer/createdesktopentryoperation.cpp
+++ b/src/libs/installer/createdesktopentryoperation.cpp
@@ -158,7 +158,7 @@ bool CreateDesktopEntryOperation::performOperation()
// Type=Application\nExec=qtcreator\nPath=...
const QStringList pairs = values.split(QLatin1Char('\n'));
for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it)
- stream << *it << endl;
+ stream << *it << Qt::endl;
return true;
}
diff --git a/src/libs/installer/createlinkoperation.cpp b/src/libs/installer/createlinkoperation.cpp
index 3f29367b6..65ff8fc5d 100644
--- a/src/libs/installer/createlinkoperation.cpp
+++ b/src/libs/installer/createlinkoperation.cpp
@@ -88,7 +88,7 @@ bool CreateLinkOperation::undoOperation()
return false;
}
- return !QFileInfo(linkPath).exists();
+ return !QFileInfo::exists(linkPath);
}
bool CreateLinkOperation::testOperation()
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
index a019d1567..48700bd12 100644
--- a/src/libs/installer/downloadarchivesjob.cpp
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -75,7 +75,7 @@ DownloadArchivesJob::~DownloadArchivesJob()
Sets the \a archives to download. The first value of each pair contains the file name to register
the file in the installer's internal file system, the second one the source url.
*/
-void DownloadArchivesJob::setArchivesToDownload(const QList<QPair<QString, QString> > &archives)
+void DownloadArchivesJob::setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives)
{
m_archivesToDownload = archives;
m_archivesToDownloadCount = archives.count();
@@ -111,17 +111,17 @@ void DownloadArchivesJob::doCancel()
void DownloadArchivesJob::fetchNextArchiveHash()
{
- if (m_core->testChecksum()) {
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_archivesToDownload.first().checkSha1CheckSum) {
if (m_canceled) {
finishWithError(tr("Canceled"));
return;
}
- if (m_archivesToDownload.isEmpty()) {
- emitFinished();
- return;
- }
-
if (m_downloader)
m_downloader->deleteLater();
@@ -281,10 +281,10 @@ void DownloadArchivesJob::registerFile()
{
Q_ASSERT(m_downloader != nullptr);
- if (m_canceled)
+ if (m_canceled || m_archivesToDownload.isEmpty())
return;
- if (m_core->testChecksum() && m_currentHash != m_downloader->sha1Sum().toHex()) {
+ if (m_archivesToDownload.first().checkSha1CheckSum && m_currentHash != m_downloader->sha1Sum().toHex()) {
//TODO: Maybe we should try to download the file again automatically
const QMessageBox::Button res =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
@@ -308,8 +308,8 @@ void DownloadArchivesJob::registerFile()
emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
}
- const QPair<QString, QString> pair = m_archivesToDownload.takeFirst();
- BinaryFormatEngineHandler::instance()->registerResource(pair.first,
+ const PackageManagerCore::DownloadItem item = m_archivesToDownload.takeFirst();
+ BinaryFormatEngineHandler::instance()->registerResource(item.fileName,
m_downloader->downloadedFileName());
emit fileDownloadReady(m_downloader->downloadedFileName());
@@ -330,7 +330,7 @@ void DownloadArchivesJob::downloadFailed(const QString &error)
const QMessageBox::StandardButton b =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2")
- .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel);
+ .arg(m_archivesToDownload.first().sourceUrl, error), QMessageBox::Retry | QMessageBox::Cancel);
// Do not call fetchNextArchiveHash when using command line instance,
// installer tries to download the same archive causing infinite loop
@@ -353,13 +353,13 @@ void DownloadArchivesJob::finishWithError(const QString &error)
KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix, const QString &queryString)
{
KDUpdater::FileDownloader *downloader = nullptr;
- const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
+ const QFileInfo fi = QFileInfo(m_archivesToDownload.first().fileName);
const Component *const component = m_core->componentByName(PackageManagerCore::checkableName(QFileInfo(fi.path()).fileName()));
if (component) {
QString fullQueryString;
if (!queryString.isEmpty())
fullQueryString = QLatin1String("?") + queryString;
- const QUrl url(m_archivesToDownload.first().second + suffix + fullQueryString);
+ const QUrl url(m_archivesToDownload.first().sourceUrl + suffix + fullQueryString);
const QString &scheme = url.scheme();
downloader = FileDownloaderFactory::instance().create(scheme, this);
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
index c75e9307a..c156d0244 100644
--- a/src/libs/installer/downloadarchivesjob.h
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -30,7 +30,7 @@
#define DOWNLOADARCHIVESJOB_H
#include "job.h"
-
+#include "packagemanagercore.h"
#include <QtCore/QPair>
#include <QtCore/QElapsedTimer>
@@ -45,7 +45,6 @@ namespace KDUpdater {
namespace QInstaller {
class MessageBoxHandler;
-class PackageManagerCore;
class DownloadArchivesJob : public Job
{
@@ -56,7 +55,7 @@ public:
~DownloadArchivesJob();
int numberOfDownloads() const { return m_archivesDownloaded; }
- void setArchivesToDownload(const QList<QPair<QString, QString> > &archives);
+ void setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives);
void setExpectedTotalSize(quint64 total);
Q_SIGNALS:
@@ -94,7 +93,7 @@ private:
int m_archivesDownloaded;
int m_archivesToDownloadCount;
- QList<QPair<QString, QString> > m_archivesToDownload;
+ QList<PackageManagerCore::DownloadItem> m_archivesToDownload;
bool m_canceled;
QByteArray m_currentHash;
diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp
index 59f9f22bf..87810211e 100644
--- a/src/libs/installer/elevatedexecuteoperation.cpp
+++ b/src/libs/installer/elevatedexecuteoperation.cpp
@@ -35,7 +35,7 @@
#include <QtCore/QDebug>
#include <QtCore/QProcessEnvironment>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QThread>
using namespace QInstaller;
@@ -138,9 +138,10 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
QList< int > allowedExitCodes;
- QRegExp re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
- if (re.exactMatch(args.first())) {
- const QStringList numbers = re.cap(1).split(QLatin1Char(','));
+ static const QRegularExpression re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
+ const QRegularExpressionMatch match = re.match(args.first());
+ if (match.hasMatch()) {
+ const QStringList numbers = match.captured(1).split(QLatin1Char(','));
for(QStringList::const_iterator it = numbers.constBegin(); it != numbers.constEnd(); ++it)
allowedExitCodes.push_back(it->toInt());
args.pop_front();
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp
index 7b9d568f2..2147a8978 100644
--- a/src/libs/installer/fileutils.cpp
+++ b/src/libs/installer/fileutils.cpp
@@ -89,7 +89,7 @@ TempPathDeleter::~TempPathDeleter()
QStringList TempPathDeleter::paths() const
{
- return m_paths.toList();
+ return m_paths.values();
}
void TempPathDeleter::add(const QString &path)
@@ -371,7 +371,7 @@ bool QInstaller::setDefaultFilePermissions(QFile *file, DefaultFilePermissions p
void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir)
{
Q_ASSERT(QFileInfo(sourceDir).isDir());
- Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ Q_ASSERT(!QFileInfo::exists(targetDir) || QFileInfo(targetDir).isDir());
if (!QDir().mkpath(targetDir)) {
throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".")
.arg(QDir::toNativeSeparators(targetDir)));
@@ -402,7 +402,7 @@ void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &
void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &targetDir)
{
Q_ASSERT(QFileInfo(sourceDir).isDir());
- Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ Q_ASSERT(!QFileInfo::exists(targetDir) || QFileInfo(targetDir).isDir());
if (!QDir().mkpath(targetDir)) {
throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".")
.arg(QDir::toNativeSeparators(targetDir)));
@@ -864,8 +864,8 @@ void QInstaller::copyConfigChildElements(QDomDocument &dom, const QDomNodeList &
// Filename may also contain a path relative to source directory but we
// copy it strictly into target directory without extra paths
- const QString newName = domElement.text()
- .replace(QRegExp(QLatin1String("\\\\|/|\\.|:")), QLatin1String("_"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:"));
+ const QString newName = domElement.text().replace(regex, QLatin1String("_"));
const QString targetFile = targetDir + QDir::separator() + newName;
const QFileInfo elementFileInfo = QFileInfo(sourceDir, domElement.text());
diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp
index 683c945dc..fd264ce63 100644
--- a/src/libs/installer/genericdatacache.cpp
+++ b/src/libs/installer/genericdatacache.cpp
@@ -174,6 +174,7 @@ GenericDataCache<T>::~GenericDataCache()
template<typename T>
void GenericDataCache<T>::setType(const QString &type)
{
+ QMutexLocker _(&m_mutex);
m_type = type;
}
@@ -186,6 +187,7 @@ void GenericDataCache<T>::setType(const QString &type)
template<typename T>
void GenericDataCache<T>::setVersion(const QString &version)
{
+ QMutexLocker _(&m_mutex);
m_version = version;
}
@@ -200,6 +202,7 @@ void GenericDataCache<T>::setVersion(const QString &version)
template <typename T>
bool GenericDataCache<T>::initialize()
{
+ QMutexLocker _(&m_mutex);
Q_ASSERT(m_items.isEmpty());
if (m_path.isEmpty()) {
@@ -247,6 +250,7 @@ bool GenericDataCache<T>::initialize()
template <typename T>
bool GenericDataCache<T>::clear()
{
+ QMutexLocker _(&m_mutex);
if (m_invalidated) {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Cannot clear invalidated cache."));
@@ -286,6 +290,7 @@ bool GenericDataCache<T>::clear()
template<typename T>
bool GenericDataCache<T>::sync()
{
+ QMutexLocker _(&m_mutex);
if (m_invalidated) {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Cannot synchronize invalidated cache."));
@@ -303,6 +308,7 @@ bool GenericDataCache<T>::sync()
template<typename T>
bool GenericDataCache<T>::isValid() const
{
+ QMutexLocker _(&m_mutex);
return !m_invalidated;
}
@@ -314,6 +320,7 @@ bool GenericDataCache<T>::isValid() const
template<typename T>
QString GenericDataCache<T>::errorString() const
{
+ QMutexLocker _(&m_mutex);
return m_error;
}
@@ -325,6 +332,7 @@ QString GenericDataCache<T>::errorString() const
template <typename T>
QString GenericDataCache<T>::path() const
{
+ QMutexLocker _(&m_mutex);
return m_path;
}
@@ -337,6 +345,7 @@ QString GenericDataCache<T>::path() const
template <typename T>
void GenericDataCache<T>::setPath(const QString &path)
{
+ QMutexLocker _(&m_mutex);
if (!m_invalidated)
toDisk();
@@ -352,6 +361,7 @@ void GenericDataCache<T>::setPath(const QString &path)
template <typename T>
QList<T *> GenericDataCache<T>::items() const
{
+ QMutexLocker _(&m_mutex);
if (m_invalidated) {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Cannot retrieve items from invalidated cache."));
@@ -369,6 +379,7 @@ QList<T *> GenericDataCache<T>::items() const
template <typename T>
T *GenericDataCache<T>::itemByChecksum(const QByteArray &checksum) const
{
+ QMutexLocker _(&m_mutex);
if (m_invalidated) {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Cannot retrieve item from invalidated cache."));
@@ -387,6 +398,7 @@ T *GenericDataCache<T>::itemByChecksum(const QByteArray &checksum) const
template <typename T>
T *GenericDataCache<T>::itemByPath(const QString &path) const
{
+ QMutexLocker _(&m_mutex);
auto it = std::find_if(m_items.constBegin(), m_items.constEnd(),
[&](T *item) {
return (QDir::fromNativeSeparators(path) == QDir::fromNativeSeparators(item->path()));
@@ -413,6 +425,7 @@ T *GenericDataCache<T>::itemByPath(const QString &path) const
template <typename T>
bool GenericDataCache<T>::registerItem(T *item, bool replace)
{
+ QMutexLocker _(&m_mutex);
if (m_invalidated) {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Cannot register item to invalidated cache."));
@@ -430,7 +443,7 @@ bool GenericDataCache<T>::registerItem(T *item, bool replace)
}
if (m_items.contains(item->checksum())) {
if (replace) {// replace existing item including contents on disk
- removeItem(item->checksum());
+ remove(item->checksum());
} else {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Cannot register item with checksum %1. An item with the same checksum "
@@ -470,26 +483,8 @@ bool GenericDataCache<T>::registerItem(T *item, bool replace)
template <typename T>
bool GenericDataCache<T>::removeItem(const QByteArray &checksum)
{
- if (m_invalidated) {
- setErrorString(QCoreApplication::translate("GenericDataCache",
- "Cannot remove item from invalidated cache."));
- return false;
- }
- QScopedPointer<T> item(m_items.take(checksum));
- if (!item) {
- setErrorString(QCoreApplication::translate("GenericDataCache",
- "Cannot remove item specified by checksum %1: no such item exists.").arg(QLatin1String(checksum)));
- return false;
- }
-
- try {
- QInstaller::removeDirectory(item->path());
- } catch (const Error &e) {
- setErrorString(QCoreApplication::translate("GenericDataCache",
- "Error while removing directory \"%1\": %2").arg(item->path(), e.message()));
- return false;
- }
- return true;
+ QMutexLocker _(&m_mutex);
+ return remove(checksum);
}
/*!
@@ -500,6 +495,7 @@ bool GenericDataCache<T>::removeItem(const QByteArray &checksum)
template<typename T>
QList<T *> GenericDataCache<T>::obsoleteItems() const
{
+ QMutexLocker _(&m_mutex);
const QList<T *> obsoletes = QtConcurrent::blockingFiltered(m_items.values(),
[&](T *item1) {
if (item1->isActive()) // We can skip the iteration for active entries
@@ -516,7 +512,7 @@ QList<T *> GenericDataCache<T>::obsoleteItems() const
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::invalidate()
+ \internal
Marks the cache invalid and clears all items. The contents
on disk are not deleted. Releases the lock file of the cache.
@@ -536,7 +532,7 @@ void GenericDataCache<T>::invalidate()
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::setErrorString(const QString &error) const
+ \internal
Sets the current error string to \a error and prints it as a warning to the console.
*/
@@ -548,7 +544,7 @@ void GenericDataCache<T>::setErrorString(const QString &error) const
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::fromDisk()
+ \internal
Reads the manifest file of the cache if one exists, and populates the internal
hash from the file contents. Returns \c true if the manifests was read successfully
@@ -602,7 +598,7 @@ bool GenericDataCache<T>::fromDisk()
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::toDisk()
+ \internal
Writes the manifest file with the contents of the internal item hash.
Returns \c true on success, \c false otherwise.
@@ -638,6 +634,34 @@ bool GenericDataCache<T>::toDisk()
return true;
}
+/*!
+ \internal
+*/
+template<typename T>
+bool GenericDataCache<T>::remove(const QByteArray &checksum)
+{
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove item from invalidated cache."));
+ return false;
+ }
+ QScopedPointer<T> item(m_items.take(checksum));
+ if (!item) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove item specified by checksum %1: no such item exists.").arg(QLatin1String(checksum)));
+ return false;
+ }
+
+ try {
+ QInstaller::removeDirectory(item->path());
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while removing directory \"%1\": %2").arg(item->path(), e.message()));
+ return false;
+ }
+ return true;
+}
+
template class GenericDataCache<Metadata>;
} // namespace QInstaller
diff --git a/src/libs/installer/genericdatacache.h b/src/libs/installer/genericdatacache.h
index 5042ed9a4..2bdf6697e 100644
--- a/src/libs/installer/genericdatacache.h
+++ b/src/libs/installer/genericdatacache.h
@@ -33,6 +33,7 @@
#include "lockfile.h"
#include <QHash>
+#include <QMutex>
#include <QScopedPointer>
namespace QInstaller {
@@ -96,8 +97,12 @@ private:
bool fromDisk();
bool toDisk();
+ bool remove(const QByteArray &checksum);
+
private:
QScopedPointer<KDUpdater::LockFile> m_lock;
+ mutable QMutex m_mutex;
+
QHash<QByteArray, T *> m_items;
QString m_path;
QString m_type;
diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp
index adf1d2f6e..5240138c4 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -60,6 +60,12 @@ namespace QInstaller
\internal
*/
+/*!
+ \fn inline QInstaller::splitStringWithComma(const QString &value())
+ Splits \a value into substrings wherever comma occurs, and returns the list of those strings.
+ \internal
+*/
+
Q_LOGGING_CATEGORY(lcServer, IFW_SERVER)
Q_LOGGING_CATEGORY(lcInstallerInstallLog, IFW_INSTALLER_INSTALLLOG)
Q_LOGGING_CATEGORY(lcProgressIndicator, IFW_PROGRESS_INDICATOR)
@@ -77,12 +83,12 @@ QStringList loggingCategories()
}
-Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, staticCommaRegExp, (QLatin1String("(, |,)")));
+Q_GLOBAL_STATIC_WITH_ARGS(QRegularExpression, staticCommaRegExp, (QLatin1String("(, |,)")));
/*!
\internal
*/
-QRegExp commaRegExp()
+QRegularExpression commaRegExp()
{
return *staticCommaRegExp();
}
diff --git a/src/libs/installer/globals.h b/src/libs/installer/globals.h
index b22331e2c..3b3f4e3ab 100644
--- a/src/libs/installer/globals.h
+++ b/src/libs/installer/globals.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,7 +30,7 @@
#include "installer_global.h"
-#include <QRegExp>
+#include <QRegularExpression>
#include <QLoggingCategory>
namespace QInstaller {
@@ -43,11 +43,22 @@ INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcDeveloperBuild)
QStringList INSTALLER_EXPORT loggingCategories();
-QRegExp INSTALLER_EXPORT commaRegExp();
+QRegularExpression INSTALLER_EXPORT commaRegExp();
+inline QStringList splitStringWithComma(const QString &value) {
+ if (!value.isEmpty())
+ return value.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QStringList();
+}
QString htmlToString(const QString &html);
QString enumToString(const QMetaObject& metaObject, const char *enumerator, int key);
+template <typename T, template<typename> typename C>
+QSet<T> toQSet(const C<T> &container)
+{
+ return QSet<T>(container.begin(), container.end());
+}
+
} // QInstaller
#endif // GLOBALS_H
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index 316d2abfd..5a58968ee 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -39,10 +39,14 @@ QT += \
widgets \
core-private \
qml-private
-win32:QT += winextras
+
+win32:lessThan(QT_MAJOR_VERSION, 6):QT += winextras
+
+greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat
HEADERS += packagemanagercore.h \
aspectratiolabel.h \
+ calculatorbase.h \
componentsortfilterproxymodel.h \
concurrentoperationrunner.h \
genericdatacache.h \
@@ -146,6 +150,7 @@ SOURCES += packagemanagercore.cpp \
abstractarchive.cpp \
archivefactory.cpp \
aspectratiolabel.cpp \
+ calculatorbase.cpp \
concurrentoperationrunner.cpp \
directoryguard.cpp \
fileguard.cpp \
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index 4eb65f1ad..105e99fd8 100644
--- a/src/libs/installer/installercalculator.cpp
+++ b/src/libs/installer/installercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,48 +42,38 @@ namespace QInstaller {
*/
InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash)
- : m_core(core)
+ : CalculatorBase(core)
, m_autoDependencyComponentHash(autoDependencyComponentHash)
{
}
-InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(const Component *c) const
+InstallerCalculator::~InstallerCalculator()
{
- return m_toInstallComponentIdReasonHash.value(c->name(),
- qMakePair(InstallerCalculator::Selected, QString())).first;
}
-QString InstallerCalculator::installReason(const Component *component) const
+QString InstallerCalculator::resolutionText(Component *component) const
{
- InstallerCalculator::InstallReasonType reason = installReasonType(component);
+ const Resolution reason = resolutionType(component);
switch (reason) {
- case Automatic:
+ case Resolution::Automatic:
return QCoreApplication::translate("InstallerCalculator",
"Components added as automatic dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("InstallerCalculator", "Components added as "
- "dependency for \"%1\":").arg(installReasonReferencedComponent(component));
- case Resolved:
+ "dependency for \"%1\":").arg(referencedComponent(component));
+ case Resolution::Resolved:
return QCoreApplication::translate("InstallerCalculator",
"Components that have resolved dependencies:");
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("InstallerCalculator",
"Selected components without dependencies:");
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid install resolution detected!");
}
return QString();
}
-QList<Component*> InstallerCalculator::orderedComponentsToInstall() const
-{
- return m_orderedComponentsToInstall;
-}
-
-QString InstallerCalculator::componentsToInstallError() const
-{
- return m_componentsToInstallError;
-}
-
-bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components)
+bool InstallerCalculator::solve(const QList<Component *> &components)
{
if (components.isEmpty())
return true;
@@ -95,54 +85,38 @@ bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &co
if (m_toInstallComponentIds.contains(component->name())) {
const QString errorMessage = recursionError(component);
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
+ m_errorString.append(errorMessage);
Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO,
qPrintable(errorMessage));
return false;
}
if (component->currentDependencies().isEmpty())
- realAppendToInstallComponents(component);
+ addComponentForInstall(component);
else
notAppendedComponents.append(component);
}
for (Component *component : qAsConst(notAppendedComponents)) {
- if (!appendComponentToInstall(component))
+ if (!solveComponent(component))
return false;
}
// All regular dependencies are resolved. Now we are looking for auto depend on components.
QSet<Component *> foundAutoDependOnList = autodependencyComponents();
-
if (!foundAutoDependOnList.isEmpty())
- return appendComponentsToInstall(foundAutoDependOnList.values());
- return true;
-}
+ return solve(foundAutoDependOnList.values());
-QString InstallerCalculator::installReasonReferencedComponent(const Component *component) const
-{
- return m_toInstallComponentIdReasonHash.value(component->name(),
- qMakePair(InstallerCalculator::Selected, QString())).second;
-}
-
-void InstallerCalculator::insertInstallReason(Component *component,
- InstallReasonType installReason, const QString &referencedComponentName)
-{
- // keep the first reason
- if (m_toInstallComponentIdReasonHash.contains(component->name()))
- return;
- m_toInstallComponentIdReasonHash.insert(component->name(),
- qMakePair(installReason, referencedComponentName));
+ return true;
}
-void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version)
+void InstallerCalculator::addComponentForInstall(Component *component, const QString &version)
{
if (!m_componentsForAutodepencencyCheck.contains(component))
m_componentsForAutodepencencyCheck.append(component);
if (!component->isInstalled(version) || (m_core->isUpdater() && component->isUpdateAvailable())) {
- m_orderedComponentsToInstall.append(component);
+ m_resolvedComponents.append(component);
m_toInstallComponentIds.insert(component->name());
}
}
@@ -150,10 +124,10 @@ void InstallerCalculator::realAppendToInstallComponents(Component *component, co
QString InstallerCalculator::recursionError(Component *component) const
{
return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
- "already added with reason: \"%2\"").arg(component->name(), installReason(component));
+ "already added with reason: \"%2\"").arg(component->name(), resolutionText(component));
}
-bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version)
+bool InstallerCalculator::solveComponent(Component *component, const QString &version)
{
const QStringList dependenciesList = component->currentDependencies();
QString requiredDependencyVersion = version;
@@ -166,7 +140,7 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
"Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName,
component->name());
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
+ m_errorString.append(errorMessage);
if (component->packageManagerCore()->settings().allowUnstableComponents()) {
component->setUnstable(Component::UnstableError::MissingDependency, errorMessage);
continue;
@@ -181,11 +155,13 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
PackageManagerCore::parseNameAndVersion(dependencyComponentName, &requiredName, &requiredVersion);
if (!requiredVersion.isEmpty() &&
!dependencyComponent->value(scInstalledVersion).isEmpty()) {
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ?
- compEx.cap(2) : dependencyComponent->value(scInstalledVersion);
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ QRegularExpressionMatch match = compEx.match(dependencyComponent->value(scInstalledVersion));
+ const QString installedVersion = match.hasMatch()
+ ? match.captured(2) : dependencyComponent->value(scInstalledVersion);
- requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion;
+ match = compEx.match(requiredVersion);
+ requiredVersion = match.hasMatch() ? match.captured(2) : requiredVersion;
if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) {
isUpdateRequired = true;
@@ -202,24 +178,24 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
if (m_visitedComponents.value(component).contains(dependencyComponent)) {
const QString errorMessage = recursionError(component);
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError = errorMessage;
+ m_errorString = errorMessage;
Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent),
Q_FUNC_INFO, qPrintable(errorMessage));
return false;
}
m_visitedComponents[component].insert(dependencyComponent);
// add needed dependency components to the next run
- insertInstallReason(dependencyComponent, InstallerCalculator::Dependent,
+ insertResolution(dependencyComponent, Resolution::Dependent,
component->name());
- if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion))
+ if (!solveComponent(dependencyComponent, requiredDependencyVersion))
return false;
}
}
if (!m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion);
- insertInstallReason(component, InstallerCalculator::Resolved);
+ addComponentForInstall(component, requiredDependencyVersion);
+ insertResolution(component, Resolution::Resolved);
}
return true;
}
@@ -251,7 +227,7 @@ QSet<Component *> InstallerCalculator::autodependencyComponents()
// are other autodependencies as well
if (autoDependComponent->isAutoDependOn(m_toInstallComponentIds)) {
foundAutoDependOnList.insert(autoDependComponent);
- insertInstallReason(autoDependComponent, InstallerCalculator::Automatic);
+ insertResolution(autoDependComponent, Resolution::Automatic);
}
}
}
diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h
index 422de439c..339dbeffd 100644
--- a/src/libs/installer/installercalculator.h
+++ b/src/libs/installer/installercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "installer_global.h"
#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -41,51 +42,30 @@ namespace QInstaller {
class Component;
class PackageManagerCore;
-class INSTALLER_EXPORT InstallerCalculator
+class INSTALLER_EXPORT InstallerCalculator : public CalculatorBase
{
public:
InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash);
+ ~InstallerCalculator();
- enum InstallReasonType
- {
- Selected, // "Selected Component(s) without Dependencies"
- Automatic, // "Component(s) added as automatic dependencies"
- Dependent, // "Added as dependency for %1."
- Resolved // "Component(s) that have resolved Dependencies"
- };
-
- InstallReasonType installReasonType(const Component *component) const;
- QString installReason(const Component *component) const;
- QList<Component*> orderedComponentsToInstall() const;
- QString componentsToInstallError() const;
- bool appendComponentsToInstall(const QList<Component*> &components);
+ bool solve(const QList<Component *> &components) override;
+ QString resolutionText(Component *component) const override;
private:
- QString installReasonReferencedComponent(const Component *component) const;
- void insertInstallReason(Component *component,
- InstallReasonType installReasonType,
- const QString &referencedComponentName = QString());
- void realAppendToInstallComponents(Component *component, const QString &version = QString());
- bool appendComponentToInstall(Component *components, const QString &version = QString());
+ bool solveComponent(Component *component, const QString &version = QString()) override;
+
+ void addComponentForInstall(Component *component, const QString &version = QString());
QSet<Component *> autodependencyComponents();
QString recursionError(Component *component) const;
private:
- PackageManagerCore *m_core;
QHash<Component*, QSet<Component*> > m_visitedComponents;
QList<const Component*> m_componentsForAutodepencencyCheck;
QSet<QString> m_toInstallComponentIds; //for faster lookups
- QString m_componentsToInstallError;
- //calculate installation order variables
- QList<Component*> m_orderedComponentsToInstall;
- //we can't use this reason hash as component id hash, because some reasons are ready before
- //the component is added
- QHash<QString, QPair<InstallReasonType, QString> > m_toInstallComponentIdReasonHash;
//Helper hash for quicker search for autodependency components
AutoDependencyHash m_autoDependencyComponentHash;
};
-}
-
+} // namespace QInstaller
#endif // INSTALLERCALCULATOR_H
diff --git a/src/libs/installer/lib7z_create.h b/src/libs/installer/lib7z_create.h
index 9e920aa55..b908b028f 100644
--- a/src/libs/installer/lib7z_create.h
+++ b/src/libs/installer/lib7z_create.h
@@ -35,9 +35,10 @@
#include <Common/MyCom.h>
#include <7zip/UI/Common/Update.h>
+#include <QStringList>
+
QT_BEGIN_NAMESPACE
class QFileDevice;
-class QStringList;
QT_END_NAMESPACE
namespace Lib7z
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
index bd08c68c5..e0d8a53e4 100644
--- a/src/libs/installer/lib7z_facade.cpp
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -363,7 +363,7 @@ static quint32 getUInt32Property(IInArchive *archive, int index, int propId, qui
static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions)
{
quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0);
- QFile::Permissions permissions = nullptr;
+ QFile::Permissions permissions = QFile::Permissions();
if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
if (hasPermissions != nullptr)
*hasPermissions = true;
diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp
index 4102518f4..233a4c28b 100644
--- a/src/libs/installer/libarchivearchive.cpp
+++ b/src/libs/installer/libarchivearchive.cpp
@@ -832,7 +832,7 @@ QVector<ArchiveEntry> LibArchiveArchive::list()
ArchiveEntry archiveEntry;
archiveEntry.path = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry);
- archiveEntry.utcTime = QDateTime::fromTime_t(archive_entry_mtime(entry));
+ archiveEntry.utcTime = QDateTime::fromSecsSinceEpoch(archive_entry_mtime(entry));
archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR);
archiveEntry.isSymbolicLink = (archive_entry_filetype(entry) == AE_IFLNK);
archiveEntry.uncompressedSize = archive_entry_size(entry);
diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp
index dc863c1da..6cf0c8e3e 100644
--- a/src/libs/installer/licenseoperation.cpp
+++ b/src/libs/installer/licenseoperation.cpp
@@ -56,7 +56,7 @@ void LicenseOperation::backup()
bool LicenseOperation::performOperation()
{
- QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ QVariantMap licenses = value(scLicenses).toMap();
if (licenses.isEmpty()) {
setError(UserDefinedError);
setErrorString(tr("No license files found to copy."));
@@ -96,7 +96,7 @@ bool LicenseOperation::performOperation()
bool LicenseOperation::undoOperation()
{
- const QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ const QVariantMap licenses = value(scLicenses).toMap();
if (licenses.isEmpty()) {
setError(UserDefinedError);
setErrorString(tr("No license files found to delete."));
diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp
index ff9f49696..027dac232 100644
--- a/src/libs/installer/link.cpp
+++ b/src/libs/installer/link.cpp
@@ -263,7 +263,7 @@ QString Link::targetPath() const
#ifdef Q_OS_WIN
return readWindowsSymLink(m_path);
#else
- return QFileInfo(m_path).readLink();
+ return QFileInfo(m_path).symLinkTarget();
#endif
}
diff --git a/src/libs/installer/loggingutils.cpp b/src/libs/installer/loggingutils.cpp
index 8c542a1d1..42bbc0117 100644
--- a/src/libs/installer/loggingutils.cpp
+++ b/src/libs/installer/loggingutils.cpp
@@ -31,6 +31,7 @@
#include "component.h"
#include "globals.h"
#include "fileutils.h"
+#include "packagemanagercore.h"
#include "remoteclient.h"
#include "remotefileengine.h"
@@ -110,6 +111,7 @@ public:
*/
LoggingHandler::LoggingHandler()
: m_verbLevel(VerbosityLevel::Silent)
+ , m_outputRedirected(false)
{
#if defined(Q_OS_UNIX)
m_outputRedirected = !isatty(fileno(stdout));
@@ -251,7 +253,7 @@ bool LoggingHandler::outputRedirected() const
/*!
Prints update information from \a components.
*/
-void LoggingHandler::printUpdateInformation(const QList<Component *> components) const
+void LoggingHandler::printUpdateInformation(const QList<Component *> &components) const
{
QString output;
QXmlStreamWriter stream(&output);
@@ -441,7 +443,7 @@ VerboseWriter *VerboseWriter::instance()
*/
void VerboseWriter::appendLine(const QString &msg)
{
- m_stream << msg << endl;
+ m_stream << msg << Qt::endl;
}
/*!
diff --git a/src/libs/installer/loggingutils.h b/src/libs/installer/loggingutils.h
index f41a18663..8bd4217ee 100644
--- a/src/libs/installer/loggingutils.h
+++ b/src/libs/installer/loggingutils.h
@@ -64,7 +64,7 @@ public:
VerbosityLevel verboseLevel() const;
bool outputRedirected() const;
- void printUpdateInformation(const QList<Component *> components) const;
+ void printUpdateInformation(const QList<Component *> &components) const;
void printLocalPackageInformation(const QList<KDUpdater::LocalPackage> &packages) const;
void printPackageInformation(const PackagesList &matchedPackages, const LocalPackagesMap &installedPackages) const;
diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp
index e2d461ba8..78abc88fa 100644
--- a/src/libs/installer/messageboxhandler.cpp
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -368,6 +368,7 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton defaultButton)
{
QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
+ msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox *>();
Q_ASSERT(buttonBox != nullptr);
diff --git a/src/libs/installer/metadata.cpp b/src/libs/installer/metadata.cpp
index b74ffc3f1..9ae817127 100644
--- a/src/libs/installer/metadata.cpp
+++ b/src/libs/installer/metadata.cpp
@@ -36,6 +36,7 @@
#include <QDir>
#include <QDomDocument>
#include <QFile>
+#include <QByteArrayMatcher>
namespace QInstaller {
@@ -122,15 +123,14 @@ QDomDocument Metadata::updatesDocument() const
/*!
Returns \c true if the \c Updates.xml document of this metadata
- exists and can be opened for reading, \c false otherwise.
+ exists, \c false otherwise.
*/
bool Metadata::isValid() const
{
- QFile updateFile(path() + QLatin1String("/Updates.xml"));
- if (!updateFile.open(QIODevice::ReadOnly)) {
+ const QString updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!QFileInfo::exists(updateFile)) {
qCWarning(QInstaller::lcInstallerInstallLog)
- << "Cannot open" << updateFile.fileName()
- << "for reading:" << updateFile.errorString();
+ << "File" << updateFile << "does not exist.";
return false;
}
return true;
@@ -189,7 +189,7 @@ Repository Metadata::repository() const
is already set, the new one will override the previous one. The metadata becomes
associated with the set repository even if it was fetched from another one.
*/
-void Metadata::setRepository(Repository repository)
+void Metadata::setRepository(const Repository &repository)
{
m_repository = repository;
}
@@ -254,4 +254,32 @@ QString Metadata::persistentRepositoryPath()
return m_persistentRepositoryPath;
}
+/*!
+ Returns true if the updates document of this metadata contains the repository
+ update element, which can include actions to \c add, \c remove, and \c replace
+ repositories.
+
+ \note This function does not check that the repository updates are actually
+ valid, only that the updates document contains the \c RepositoryUpdate element.
+*/
+bool Metadata::containsRepositoryUpdates() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return false;
+ }
+
+ static const auto matcher = qMakeStaticByteArrayMatcher("<RepositoryUpdate>");
+ while (!updateFile.atEnd()) {
+ const QByteArray line = updateFile.readLine().simplified();
+ if (matcher.indexIn(line) != -1)
+ return true;
+ }
+
+ return false;
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/metadata.h b/src/libs/installer/metadata.h
index c2afe7df6..3063be829 100644
--- a/src/libs/installer/metadata.h
+++ b/src/libs/installer/metadata.h
@@ -53,7 +53,7 @@ public:
bool obsoletes(CacheableItem *other) override;
Repository repository() const;
- void setRepository(Repository repository);
+ void setRepository(const Repository &repository);
bool isAvailableFromDefaultRepository() const;
void setAvailableFromDefaultRepository(bool defaultRepository);
@@ -61,6 +61,8 @@ public:
void setPersistentRepositoryPath(const QUrl &url);
QString persistentRepositoryPath();
+ bool containsRepositoryUpdates() const;
+
private:
Repository m_repository;
QString m_persistentRepositoryPath;
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index 08cc70d0c..ff15d7b0d 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.cpp
@@ -100,6 +100,7 @@ MetadataJob::MetadataJob(QObject *parent)
connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished);
connect(&m_metadataTask, &QFutureWatcherBase::finished, this, &MetadataJob::metadataTaskFinished);
connect(&m_metadataTask, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged);
+ connect(&m_updateCacheTask, &QFutureWatcherBase::finished, this, &MetadataJob::updateCacheTaskFinished);
}
MetadataJob::~MetadataJob()
@@ -224,6 +225,8 @@ void MetadataJob::doStart()
setError(Job::NoError);
setErrorString(QString());
m_metadataResult.clear();
+ setProgressTotalAmount(100);
+
if (!m_core) {
emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine."));
return; // We can't do anything here without core, so avoid tons of !m_core checks.
@@ -238,28 +241,63 @@ void MetadataJob::doStart()
emit infoMessage(this, tr("Fetching latest update information..."));
const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly();
if (onlineInstaller || m_core->isMaintainer()) {
+ static const QString updateFilePath(QLatin1Char('/') + scUpdatesXML + QLatin1Char('?'));
+ static const QString randomQueryString = QString::number(QRandomGenerator::global()->generate());
+
QList<FileTaskItem> items;
QSet<Repository> repositories = getRepositories();
+ quint64 cachedCount = 0;
foreach (const Repository &repo, repositories) {
+ // For not blocking the UI
+ qApp->processEvents();
+
if (repo.isEnabled() &&
productKeyCheck->isValidRepository(repo)) {
QAuthenticator authenticator;
authenticator.setUser(repo.username());
authenticator.setPassword(repo.password());
- if (!repo.isCompressed()) {
- QString url = repo.url().toString() + QLatin1String("/Updates.xml?");
- if (!m_core->value(scUrlQueryString).isEmpty())
- url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ if (repo.isCompressed())
+ continue;
+
+ QString url;
+ url = repo.url().toString() + updateFilePath;
+ if (!m_core->value(scUrlQueryString).isEmpty())
+ url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ // also append a random string to avoid proxy caches
+ url.append(randomQueryString);
+
+ // Check if we can skip downloading already cached repositories
+ const Status foundStatus = findCachedUpdatesFile(repo, url);
+ if (foundStatus == XmlDownloadSuccess) {
+ // Found existing Updates.xml
+ ++cachedCount;
+ continue;
+ } else if (foundStatus == XmlDownloadRetry) {
+ // Repositories changed, restart with the new repositories
+ QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
+ return;
+ }
- // also append a random string to avoid proxy caches
- FileTaskItem item(url.append(QString::number(QRandomGenerator::global()->generate())));
- item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
- items.append(item);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
}
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+ item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
+ item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
+ items.append(item);
}
}
+ const quint64 totalCount = repositories.count();
+ if (cachedCount > 0) {
+ qCDebug(lcInstallerInstallLog).nospace() << "Loaded from cache "
+ << cachedCount << "/" << totalCount << ". Downloading remaining "
+ << items.count() << "/" << totalCount <<".";
+ }
if (items.count() > 0) {
startXMLTask(items);
} else {
@@ -304,7 +342,6 @@ void MetadataJob::startXMLTask(const QList<FileTaskItem> &items)
{
DownloadFileTask *const xmlTask = new DownloadFileTask(items);
xmlTask->setProxyFactory(m_core->proxyFactory());
- setProgressTotalAmount(100);
connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this,
&MetadataJob::progressChanged);
m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
@@ -337,47 +374,15 @@ void MetadataJob::startUnzipRepositoryTask(const Repository &repo)
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
-bool MetadataJob::updateCache()
+void MetadataJob::startUpdateCacheTask()
{
const int toRegisterCount = m_fetchedMetadata.count();
if (toRegisterCount > 0)
emit infoMessage(this, tr("Updating local cache with %n new items...",
nullptr, toRegisterCount));
- // Register items from current run to cache
- QStringList registeredKeys;
- bool success = true;
- for (auto *meta : qAsConst(m_fetchedMetadata)) {
- if (!m_metaFromCache.registerItem(meta, true)) {
- success = false;
- break;
- }
- meta->setPersistentRepositoryPath(meta->repository().url());
- registeredKeys.append(m_fetchedMetadata.key(meta));
- }
- // Remove items whose ownership was transferred to cache
- for (auto &key : qAsConst(registeredKeys))
- m_fetchedMetadata.remove(key);
-
- // Bail out if there was error while registering items
- if (!success) {
- emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() + u' '
- + tr("Clearing the cache directory and restarting the application may solve this."));
- m_metaFromCache.sync();
- return false;
- }
-
- // ...and clean up obsolete cached items
- const QList<Metadata *> obsolete = m_metaFromCache.obsoleteItems();
- for (auto *meta : obsolete)
- m_metaFromCache.removeItem(meta->checksum());
-
- if (!m_metaFromCache.sync()) {
- emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() + u' '
- + tr("Clearing the cache directory and restarting the application may solve this."));
- return false;
- }
- return true;
+ UpdateCacheTask *task = new UpdateCacheTask(m_metaFromCache, m_fetchedMetadata);
+ m_updateCacheTask.setFuture(QtConcurrent::run(&UpdateCacheTask::doTask, task));
}
/*
@@ -416,7 +421,15 @@ void MetadataJob::unzipRepositoryTaskFinished()
error = testJob.error();
errorString = testJob.errorString();
if (error == Job::NoError) {
- FileTaskItem item(url);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
+ }
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+
item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
m_unzipRepositoryitems.append(item);
} else {
@@ -499,7 +512,7 @@ void MetadataJob::xmlTaskFinished()
temporaries.insert(replacement);
s.addTemporaryRepositories(temporaries, true);
} else {
- QHash<QString, QPair<Repository, Repository> > update;
+ QMultiHash<QString, QPair<Repository, Repository> > update;
update.insert(QLatin1String("replace"), qMakePair(original, replacement));
if (s.updateRepositoryCategories(update) == Settings::UpdatesApplied)
@@ -543,9 +556,8 @@ void MetadataJob::xmlTaskFinished()
if (!fetchMetaDataPackages()) {
// No new metadata packages to fetch, still need to update the cache
// for refreshed repositories.
- if (updateCache())
- emitFinished();
- }
+ startUpdateCacheTask();
+ }
} else if (status == XmlDownloadRetry) {
QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
} else {
@@ -574,13 +586,8 @@ void MetadataJob::unzipTaskFinished()
m_unzipTasks.remove(watcher);
delete watcher;
- if (m_unzipTasks.isEmpty()) {
- if (!updateCache())
- return;
-
- setProcessedAmount(100);
- emitFinished();
- }
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
}
void MetadataJob::progressChanged(int progress)
@@ -612,6 +619,13 @@ void MetadataJob::metadataTaskFinished()
} else {
throw QInstaller::TaskException(mismatchMessage);
}
+ QFileInfo fi(result.target());
+ QString targetPath = fi.absolutePath();
+ if (m_fetchedMetadata.contains(targetPath)) {
+ delete m_fetchedMetadata.value(targetPath);
+ m_fetchedMetadata.remove(targetPath);
+ }
+ continue;
}
UnzipArchiveTask *task = new UnzipArchiveTask(result.target(),
item.value(TaskRole::UserRole).toString());
@@ -622,9 +636,11 @@ void MetadataJob::metadataTaskFinished()
connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished);
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
+
} else {
- if (updateCache())
- emitFinished();
+ startUpdateCacheTask();
}
}
} catch (const TaskException &e) {
@@ -639,6 +655,25 @@ void MetadataJob::metadataTaskFinished()
}
}
+void MetadataJob::updateCacheTaskFinished()
+{
+ try {
+ m_updateCacheTask.waitForFinished();
+ } catch (const CacheTaskException &e) {
+ emitFinishedWithError(QInstaller::CacheError, e.message());
+ } catch (const QUnhandledException &e) {
+ emitFinishedWithError(QInstaller::CacheError, QLatin1String(e.what()));
+ } catch (...) {
+ emitFinishedWithError(QInstaller::CacheError, tr("Unknown exception during updating cache."));
+ }
+
+ if (error() != Job::NoError)
+ return;
+
+ setProcessedAmount(100);
+ emitFinished();
+}
+
// -- private
@@ -725,23 +760,10 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
if (result.target().isEmpty()) {
continue;
}
- QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
- if (!tmp.isValid()) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
- return XmlDownloadFailure;
- }
-
- tmp.setAutoRemove(false);
- QScopedPointer<Metadata> metadata(new Metadata(tmp.path()));
- m_tempDirDeleter.add(metadata->path());
+ QFileInfo fileInfo(result.target());
+ QScopedPointer<Metadata> metadata(new Metadata(fileInfo.absolutePath()));
QFile file(result.target());
- if (!file.rename(metadata->path() + QLatin1String("/Updates.xml"))) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot rename target to Updates.xml:"
- << file.errorString();
- return XmlDownloadFailure;
- }
-
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open Updates.xml for reading:"
<< file.errorString();
@@ -754,43 +776,14 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
hash.addData(&file);
const QByteArray updatesChecksum = hash.result().toHex();
- Metadata *cachedMetadata = m_metaFromCache.itemByChecksum(updatesChecksum);
- if (cachedMetadata) {
- const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
- if (cachedMetadata->isValid() && !repository.isCompressed()) {
- // Refresh repository information to cache. Same repository may appear in multiple
- // categories and the metadata may be available from default repositories simultaneously.
- cachedMetadata->setRepository(repository);
- if (!repository.categoryname().isEmpty())
- m_fetchedCategorizedRepositories.insert(repository); // For faster lookups
- else
- cachedMetadata->setAvailableFromDefaultRepository(true);
-
- // Refresh also persistent information, the url of the repository may have changed
- // from the last fetch.
- cachedMetadata->setPersistentRepositoryPath(repository.url());
-
- // search for additional repositories that we might need to check
- QDomDocument doc = cachedMetadata->updatesDocument();
- const MetadataJob::Status status
- = parseRepositoryUpdates(doc.documentElement(), result, cachedMetadata);
- if (status == XmlDownloadRetry) {
- // The repository update may have removed or replaced current repositories,
- // clear repository information from cached items and refresh on next fetch run.
- resetCacheRepositories();
- return status;
- }
+ bool refreshed;
+ Status status = refreshCacheItem(result, updatesChecksum, &refreshed);
+ if (status != XmlDownloadSuccess)
+ return status;
+
+ if (refreshed) // Found existing metadata
+ continue;
- continue;
- }
- // Missing or corrupted files, or compressed repository which takes priority
- // over remote repository. We will re-download and uncompress
- // the metadata. Remove broken item from the cache.
- if (!m_metaFromCache.removeItem(updatesChecksum)) {
- qCWarning(lcInstallerInstallLog) << m_metaFromCache.errorString();
- return XmlDownloadFailure;
- }
- }
metadata->setChecksum(updatesChecksum);
file.seek(0);
@@ -865,7 +858,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
m_fetchedMetadata.insert(metadataPath, metadata.take());
// search for additional repositories that we might need to check
- const MetadataJob::Status status = parseRepositoryUpdates(root, result, metadataPtr);
+ status = parseRepositoryUpdates(root, result, metadataPtr);
if (status == XmlDownloadRetry) {
// The repository update may have removed or replaced current repositories,
// clear repository information from cached items and refresh on next fetch run.
@@ -880,13 +873,87 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
return XmlDownloadSuccess;
}
+MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result,
+ const QByteArray &checksum, bool *refreshed)
+{
+ Q_ASSERT(refreshed);
+ *refreshed = false;
+
+ Metadata *cachedMetadata = m_metaFromCache.itemByChecksum(checksum);
+ if (!cachedMetadata)
+ return XmlDownloadSuccess;
+
+ const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
+ const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
+ if (cachedMetadata->isValid() && !repository.isCompressed()) {
+ // Refresh repository information to cache. Same repository may appear in multiple
+ // categories and the metadata may be available from default repositories simultaneously.
+ cachedMetadata->setRepository(repository);
+ if (!repository.categoryname().isEmpty())
+ m_fetchedCategorizedRepositories.insert(repository); // For faster lookups
+ else
+ cachedMetadata->setAvailableFromDefaultRepository(true);
+
+ // Refresh also persistent information, the url of the repository may have changed
+ // from the last fetch.
+ cachedMetadata->setPersistentRepositoryPath(repository.url());
+
+ // search for additional repositories that we might need to check
+ if (cachedMetadata->containsRepositoryUpdates()) {
+ QDomDocument doc = cachedMetadata->updatesDocument();
+ const Status status = parseRepositoryUpdates(doc.documentElement(), result, cachedMetadata);
+ if (status == XmlDownloadRetry) {
+ // The repository update may have removed or replaced current repositories,
+ // clear repository information from cached items and refresh on next fetch run.
+ resetCacheRepositories();
+ return status;
+ }
+ }
+ *refreshed = true;
+ return XmlDownloadSuccess;
+ }
+ // Missing or corrupted files, or compressed repository which takes priority
+ // over remote repository. We will re-download and uncompress
+ // the metadata. Remove broken item from the cache.
+ if (!m_metaFromCache.removeItem(checksum)) {
+ qCWarning(lcInstallerInstallLog) << m_metaFromCache.errorString();
+ return XmlDownloadFailure;
+ }
+ return XmlDownloadSuccess;
+}
+
+MetadataJob::Status MetadataJob::findCachedUpdatesFile(const Repository &repository, const QString &fileUrl)
+{
+ if (repository.xmlChecksum().isEmpty())
+ return XmlDownloadFailure;
+
+ Metadata *metadata = m_metaFromCache.itemByChecksum(repository.xmlChecksum());
+ if (!metadata)
+ return XmlDownloadFailure;
+
+ const QString targetPath = metadata->path() + QLatin1Char('/') + scUpdatesXML;
+
+ FileTaskItem cachedMetaTaskItem(fileUrl, targetPath);
+ cachedMetaTaskItem.insert(TaskRole::UserRole, QVariant::fromValue(repository));
+ const FileTaskResult cachedMetaTaskResult(targetPath, repository.xmlChecksum(), cachedMetaTaskItem, false);
+
+ bool isCached = false;
+ const Status status = refreshCacheItem(cachedMetaTaskResult, repository.xmlChecksum(), &isCached);
+ if (isCached)
+ return XmlDownloadSuccess;
+ else if (status == XmlDownloadRetry)
+ return XmlDownloadRetry;
+ else
+ return XmlDownloadFailure;
+}
+
MetadataJob::Status MetadataJob::parseRepositoryUpdates(const QDomElement &root,
const FileTaskResult &result, Metadata *metadata)
{
MetadataJob::Status status = XmlDownloadSuccess;
const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
if (!repositoryUpdate.isNull()) {
- const QHash<QString, QPair<Repository, Repository> > repositoryUpdates
+ const QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates
= searchAdditionalRepositories(repositoryUpdate, result, *metadata);
if (!repositoryUpdates.isEmpty())
status = setAdditionalRepositories(repositoryUpdates, result, *metadata);
@@ -959,7 +1026,7 @@ bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageNam
return metaFound;
}
-QHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
+QMultiHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
(const QDomNode &repositoryUpdate, const FileTaskResult &result, const Metadata &metadata)
{
QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates;
@@ -1008,7 +1075,7 @@ QHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepo
return repositoryUpdates;
}
-MetadataJob::Status MetadataJob::setAdditionalRepositories(QHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+MetadataJob::Status MetadataJob::setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
const FileTaskResult &result, const Metadata& metadata)
{
MetadataJob::Status status = XmlDownloadSuccess;
diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h
index 0a059e412..37d9367e5 100644
--- a/src/libs/installer/metadatajob.h
+++ b/src/libs/installer/metadatajob.h
@@ -82,6 +82,7 @@ private slots:
void xmlTaskFinished();
void unzipTaskFinished();
void metadataTaskFinished();
+ void updateCacheTaskFinished();
void progressChanged(int progress);
void setProgressTotalAmount(int maximum);
void unzipRepositoryTaskFinished();
@@ -90,20 +91,22 @@ private slots:
private:
bool fetchMetaDataPackages();
void startUnzipRepositoryTask(const Repository &repo);
- bool updateCache();
+ void startUpdateCacheTask();
void resetCacheRepositories();
void reset();
void resetCompressedFetch();
Status parseUpdatesXml(const QList<FileTaskResult> &results);
+ Status refreshCacheItem(const FileTaskResult &result, const QByteArray &checksum, bool *refreshed);
+ Status findCachedUpdatesFile(const Repository &repository, const QString &fileUrl);
Status parseRepositoryUpdates(const QDomElement &root, const FileTaskResult &result, Metadata *metadata);
QSet<Repository> getRepositories();
void addFileTaskItem(const QString &source, const QString &target, Metadata *metadata,
const QString &sha1, const QString &packageName);
static bool parsePackageUpdate(const QDomNodeList &c2, QString &packageName, QString &packageVersion,
QString &packageHash, bool online, bool testCheckSum);
- QHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
+ QMultiHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
const FileTaskResult &result, const Metadata &metadata);
- MetadataJob::Status setAdditionalRepositories(QHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+ MetadataJob::Status setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
const FileTaskResult &result, const Metadata& metadata);
private:
@@ -116,6 +119,7 @@ private:
TempPathDeleter m_tempDirDeleter;
QFutureWatcher<FileTaskResult> m_xmlTask;
QFutureWatcher<FileTaskResult> m_metadataTask;
+ QFutureWatcher<void> m_updateCacheTask;
QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks;
QHash<QFutureWatcher<void> *, QObject*> m_unzipRepositoryTasks;
DownloadType m_downloadType;
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index ef18b2982..ef2729dbb 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -103,6 +103,81 @@ private:
bool m_removeArchive;
};
+class CacheTaskException : public QException
+{
+public:
+ CacheTaskException() {}
+ explicit CacheTaskException(const QString &message)
+ : m_message(message)
+ {}
+ ~CacheTaskException() {}
+
+ void raise() const override { throw *this; }
+ QString message() const { return m_message; }
+ CacheTaskException *clone() const override { return new CacheTaskException(*this); }
+
+private:
+ QString m_message;
+};
+
+class UpdateCacheTask : public AbstractTask<void>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(UpdateCacheTask)
+
+public:
+ UpdateCacheTask(GenericDataCache<Metadata> &cache, QHash<QString, Metadata *> &updates)
+ : m_cache(&cache)
+ , m_updates(&updates)
+ {}
+
+ void doTask(QFutureInterface<void> &fi) override
+ {
+ fi.reportStarted();
+ fi.setExpectedResultCount(1);
+
+ // Register items from current run to cache
+ QStringList registeredKeys;
+ bool success = true;
+ for (auto *meta : qAsConst(*m_updates)) {
+ if (!m_cache->registerItem(meta, true)) {
+ success = false;
+ break;
+ }
+ meta->setPersistentRepositoryPath(meta->repository().url());
+ registeredKeys.append(m_updates->key(meta));
+ }
+ // Remove items whose ownership was transferred to cache
+ for (auto &key : qAsConst(registeredKeys))
+ m_updates->remove(key);
+
+ // Bail out if there was error while registering items
+ if (!success) {
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
+ m_cache->sync();
+ fi.reportFinished();
+ return;
+ }
+
+ // ...and clean up obsolete cached items
+ const QList<Metadata *> obsolete = m_cache->obsoleteItems();
+ for (auto *meta : obsolete)
+ m_cache->removeItem(meta->checksum());
+
+ if (!m_cache->sync()) {
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
+ }
+
+ fi.reportFinished();
+ }
+
+private:
+ GenericDataCache<Metadata> *const m_cache;
+ QHash<QString, Metadata *> *const m_updates;
+};
+
} // namespace QInstaller
#endif // METADATAJOB_P_H
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 85beeb727..b7d7d2a26 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -54,7 +54,6 @@
#include <QtConcurrentRun>
#include <QtCore/QMutex>
-#include <QtCore/QRegExp>
#include <QtCore/QSettings>
#include <QtCore/QTemporaryFile>
#include <QtCore/QTextCodec>
@@ -601,6 +600,18 @@ bool PackageManagerCore::clearLocalCache(QString *error)
}
/*!
+ \internal
+ */
+template <typename T>
+bool PackageManagerCore::loadComponentScripts(const T &components, const bool postScript)
+{
+ return d->loadComponentScripts(components, postScript);
+}
+
+template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
+
+/*!
\deprecated [4.5] Use recalculateAllComponents() instead.
\sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
@@ -799,16 +810,19 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
{
Q_ASSERT(partProgressSize >= 0 && partProgressSize <= 1);
- QList<QPair<QString, QString> > archivesToDownload;
+ QList<DownloadItem> archivesToDownload;
quint64 archivesToDownloadTotalSize = 0;
QList<Component*> neededComponents = orderedComponentsToInstall();
foreach (Component *component, neededComponents) {
// collect all archives to be downloaded
const QStringList toDownload = component->downloadableArchives();
+ bool checkSha1CheckSum = (component->value(scCheckSha1CheckSum).toLower() == scTrue);
foreach (const QString &versionFreeString, toDownload) {
- archivesToDownload.push_back(qMakePair(QString::fromLatin1("installer://%1/%2")
- .arg(component->name(), versionFreeString), QString::fromLatin1("%1/%2/%3")
- .arg(component->repositoryUrl().toString(), component->name(), versionFreeString)));
+ DownloadItem item;
+ item.checkSha1CheckSum = checkSha1CheckSum;
+ item.fileName = scInstallerPrefixWithTwoArgs.arg(component->name(), versionFreeString);
+ item.sourceUrl = scThreeArgs.arg(component->repositoryUrl().toString(), component->name(), versionFreeString);
+ archivesToDownload.push_back(item);
}
archivesToDownloadTotalSize += component->value(scCompressedSize).toULongLong();
}
@@ -987,7 +1001,7 @@ bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) con
*/
bool PackageManagerCore::fileExists(const QString &filePath) const
{
- return QFileInfo(filePath).exists();
+ return QFileInfo::exists(filePath);
}
/*!
@@ -1254,6 +1268,10 @@ PackageManagerCore::PackageManagerCore()
Creates and initializes a remote client. Requests administrator's rights for
QFile, QSettings, and QProcess operations. Calls \c init() with \a socketName, \a key,
and \a mode to set the server side authorization key.
+
+ The \a datFileName contains the corresponding .dat file name for the running
+ \c maintenance tool binary. \a datFileName can be empty if \c maintenance tool
+ fails to find it or if \c installer is run instead of \c maintenance tool.
*/
PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &operations,
const QString &datFileName,
@@ -1290,8 +1308,8 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB
QSet<QString> packagesWithoutOperation = installedPackages - operationPackages;
QSet<QString> orphanedOperations = operationPackages - installedPackages;
if (!packagesWithoutOperation.isEmpty() || !orphanedOperations.isEmpty()) {
- qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.toList();
- qCritical() << "Orphaned operations" << orphanedOperations.toList();
+ qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.values();
+ qCritical() << "Orphaned operations" << orphanedOperations.values();
qCritical() << "Your installation seems to be corrupted. Please consider re-installing from scratch, "
"remove the packages from components.xml which operations are missing, "
"or reinstall the packages.";
@@ -1634,7 +1652,7 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true, true)) {
+ if (!d->addUpdateResourcesFromRepositories(true)) {
return false;
}
@@ -1673,7 +1691,7 @@ bool PackageManagerCore::fetchRemotePackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true))
+ if (!d->addUpdateResourcesFromRepositories())
return false;
const PackagesList &packages = d->remotePackages();
@@ -1928,24 +1946,6 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie
}
/*!
- Checks whether the downloader should try to download SHA-1 checksums for
- archives and returns the checksums.
-*/
-bool PackageManagerCore::testChecksum() const
-{
- return d->m_testChecksum;
-}
-
-/*!
- The \a test argument determines whether the downloader should try to
- download SHA-1 checksums for archives.
-*/
-void PackageManagerCore::setTestChecksum(bool test)
-{
- d->m_testChecksum = test;
-}
-
-/*!
Returns the script engine that prepares and runs the component scripts.
\sa {Component Scripting}
@@ -2042,7 +2042,7 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask, const QSt
QRegularExpression re(regexp);
QList<Component*>::iterator iter = components.begin();
while (iter != components.end()) {
- if (!re.match(iter.i->t()->name()).hasMatch())
+ if (!re.match((*iter)->name()).hasMatch())
iter = components.erase(iter);
else
iter++;
@@ -2182,7 +2182,7 @@ bool PackageManagerCore::calculateComponentsToInstall() const
const QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
const bool componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall);
+ d->installerCalculator()->solve(selectedComponentsToInstall);
d->updateComponentInstallActions();
@@ -2195,7 +2195,7 @@ bool PackageManagerCore::calculateComponentsToInstall() const
*/
QList<Component*> PackageManagerCore::orderedComponentsToInstall() const
{
- return d->installerCalculator()->orderedComponentsToInstall();
+ return d->installerCalculator()->resolvedComponents();
}
/*!
@@ -2272,15 +2272,15 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
emit aboutCalculateComponentsToUninstall();
d->clearUninstallerCalculator();
- const QList<Component *> componentsToInstallList = d->installerCalculator()->orderedComponentsToInstall();
+ const QList<Component *> componentsToInstallList = d->installerCalculator()->resolvedComponents();
QList<Component *> selectedComponentsToUninstall;
foreach (Component* component, components(PackageManagerCore::ComponentType::Replacements)) {
// Uninstall the component if replacement is selected for install or update
QPair<Component*, Component*> comp = d->componentsToReplace().value(component->name());
- if (comp.first && d->m_installerCalculator->orderedComponentsToInstall().contains(comp.first)) {
- d->uninstallerCalculator()->insertUninstallReason(component,
- UninstallerCalculator::Replaced, comp.first->name());
+ if (comp.first && d->m_installerCalculator->resolvedComponents().contains(comp.first)) {
+ d->uninstallerCalculator()->insertResolution(component,
+ CalculatorBase::Resolution::Replaced, comp.first->name());
selectedComponentsToUninstall.append(comp.second);
}
}
@@ -2289,7 +2289,7 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
selectedComponentsToUninstall.append(component);
}
const bool componentsToUninstallCalculated =
- d->uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall);
+ d->uninstallerCalculator()->solve(selectedComponentsToUninstall);
d->updateComponentInstallActions();
@@ -2306,7 +2306,7 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
*/
QList<Component *> PackageManagerCore::componentsToUninstall() const
{
- return d->uninstallerCalculator()->componentsToUninstall().toList();
+ return d->uninstallerCalculator()->resolvedComponents();
}
/*!
@@ -2314,7 +2314,7 @@ QList<Component *> PackageManagerCore::componentsToUninstall() const
*/
QString PackageManagerCore::componentsToInstallError() const
{
- return d->installerCalculator()->componentsToInstallError();
+ return d->installerCalculator()->error();
}
/*!
@@ -2322,7 +2322,7 @@ QString PackageManagerCore::componentsToInstallError() const
*/
QString PackageManagerCore::componentsToUninstallError() const
{
- return d->uninstallerCalculator()->componentsToUninstallError();
+ return d->uninstallerCalculator()->error();
}
/*!
@@ -2336,7 +2336,7 @@ QString PackageManagerCore::componentsToUninstallError() const
*/
QString PackageManagerCore::installReason(Component *component) const
{
- return d->installerCalculator()->installReason(component);
+ return d->installerCalculator()->resolutionText(component);
}
/*!
@@ -2352,7 +2352,7 @@ QString PackageManagerCore::installReason(Component *component) const
*/
QString PackageManagerCore::uninstallReason(Component *component) const
{
- return d->uninstallerCalculator()->uninstallReason(component);
+ return d->uninstallerCalculator()->resolutionText(component);
}
/*!
@@ -2499,7 +2499,7 @@ void PackageManagerCore::listAvailablePackages(const QString &regexp, const QHas
ComponentModel *model = defaultComponentModel();
d->fetchMetaInformationFromRepositories();
- d->addUpdateResourcesFromRepositories(true);
+ d->addUpdateResourcesFromRepositories();
QRegularExpression re(regexp);
re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
const PackagesList &packages = d->remotePackages();
@@ -3378,9 +3378,10 @@ bool PackageManagerCore::performOperation(const QString &name, const QStringList
*/
bool PackageManagerCore::versionMatches(const QString &version, const QString &requirement)
{
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString comparator = compEx.exactMatch(requirement) ? compEx.cap(1) : QLatin1String("=");
- const QString ver = compEx.exactMatch(requirement) ? compEx.cap(2) : requirement;
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ const QRegularExpressionMatch match = compEx.match(requirement);
+ const QString comparator = match.hasMatch() ? match.captured(1) : QLatin1String("=");
+ const QString ver = match.hasMatch() ? match.captured(2) : requirement;
const bool allowEqual = comparator.contains(QLatin1Char('='));
const bool allowLess = comparator.contains(QLatin1Char('<'));
@@ -3980,9 +3981,9 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
if (settings().allowUnstableComponents()) {
// Check if there are sha checksum mismatch. Component will still show in install tree
// but is unselectable.
- foreach (const QString packageName, d->m_metadataJob.shaMismatchPackages()) {
- if (packageName == component->name()) {
- const QString errorString = QLatin1String("SHA mismatch detected for component ") + packageName;
+ foreach (const QString pkgName, d->m_metadataJob.shaMismatchPackages()) {
+ if (pkgName == component->name()) {
+ const QString errorString = QLatin1String("SHA mismatch detected for component ") + pkgName;
d->m_pendingUnstableComponents.insert(component->name(),
QPair<Component::UnstableError, QString>(Component::ShaMismatch, errorString));
}
@@ -4006,18 +4007,10 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
component->setValue(QLatin1String("password"), repo.password());
}
- // add downloadable archive from xml
- const QStringList downloadableArchives = data.package->data(scDownloadableArchives).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
-
- if (component->isFromOnlineRepository()) {
- foreach (const QString downloadableArchive, downloadableArchives)
- component->addDownloadableArchive(downloadableArchive);
- }
-
- const QStringList componentsToReplace = data.package->data(scReplaces).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ if (component->isFromOnlineRepository())
+ component->addDownloadableArchives(data.package->data(scDownloadableArchives).toString());
+ const QStringList componentsToReplace = QInstaller::splitStringWithComma(data.package->data(scReplaces).toString());
if (!componentsToReplace.isEmpty()) {
// Store the component (this is a component that replaces others) and all components that
// this one will replace.
@@ -4354,7 +4347,8 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
}
// store all components that got a replacement, but do not modify the components list
- storeReplacedComponents(localReplaceMes.unite(components), data);
+ localReplaceMes.insert(components);
+ storeReplacedComponents(localReplaceMes, data);
if (!components.isEmpty()) {
// append all components w/o parent to the direct list
@@ -4443,14 +4437,19 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
if (treeNameComponents.isEmpty())
return;
- QHash<QString, Component *> componentsTemp = components;
- for (auto *component : qAsConst(components)) {
+ QHash<QString, Component *> componentsTemp;
+ QMutableHashIterator<QString, Component* > i(components);
+ while (i.hasNext()) {
+ i.next();
+ Component *component = i.value();
if (component->treeName() != component->name()) // already handled
continue;
QString newName;
// Check treename candidates, keep the name closest to a leaf component
- for (auto &name : treeNameComponents.keys()) {
+ QMap<QString, QString>::const_iterator j;
+ for (j = treeNameComponents.begin(); j != treeNameComponents.end(); ++j) {
+ const QString name = j.key();
if (!component->name().startsWith(name))
continue;
@@ -4458,8 +4457,8 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
if (!(parent && parent->treeNameMoveChildren()))
continue; // TreeName only applied to parent
- if (newName.split(QLatin1Char('.'), QString::SkipEmptyParts).count()
- > name.split(QLatin1Char('.'), QString::SkipEmptyParts).count()) {
+ if (newName.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()
+ > name.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()) {
continue;
}
newName = name;
@@ -4485,16 +4484,16 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
d->m_pendingUnstableComponents.insert(component->name(),
QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
} else {
- componentsTemp.remove(componentsTemp.key(component));
+ i.remove();
}
continue;
}
component->setValue(scAutoTreeName, treeName);
- componentsTemp.remove(componentsTemp.key(component));
+ i.remove();
componentsTemp.insert(treeName, component);
}
- components = componentsTemp;
+ components.insert(componentsTemp);
}
void PackageManagerCore::restoreCheckState()
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index fb1eee160..e5236112e 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -29,6 +29,7 @@
#define PACKAGEMANAGERCORE_H
#include "binaryformat.h"
+#include "component.h"
#include "protocol.h"
#include "repository.h"
#include "qinstallerglobal.h"
@@ -38,12 +39,12 @@
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QStringList>
-#include <QtCore/QVector>
+#include <QtCore/QList>
#include <QSettings>
+#include <QModelIndex>
namespace QInstaller {
-class Component;
class ComponentModel;
class ScriptEngine;
class PackageManagerCorePrivate;
@@ -104,6 +105,14 @@ public:
AllNoReplacements = (Root | Descendants | Dependencies),
All = (Root | Descendants | Dependencies | Replacements)
};
+
+ struct DownloadItem
+ {
+ QString fileName;
+ QString sourceUrl;
+ bool checkSha1CheckSum;
+ };
+
Q_DECLARE_FLAGS(ComponentTypes, ComponentType)
static QFont virtualComponentsFont();
@@ -193,9 +202,6 @@ public:
void setOfflineBinaryName(const QString &name);
QString offlineBinaryName() const;
- bool testChecksum() const;
- void setTestChecksum(bool test);
-
Q_INVOKABLE void addUserRepositories(const QStringList &repositories);
Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories,
bool replace = false, bool compressed = false);
@@ -352,6 +358,8 @@ public:
bool resetLocalCache(bool init = false);
bool clearLocalCache(QString *error = nullptr);
+ template <typename T>
+ bool loadComponentScripts(const T &components, const bool postScript = false);
public Q_SLOTS:
bool runInstaller();
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index cce093dc2..259aafeb6 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -401,10 +401,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
component->setCheckState(Qt::Checked);
clearInstallerCalculator();
- if (installerCalculator()->appendComponentsToInstall(components.values()) == false) {
- setStatus(PackageManagerCore::Failure, installerCalculator()->componentsToInstallError());
+ if (installerCalculator()->solve(components.values()) == false) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
- tr("Unresolved dependencies"), installerCalculator()->componentsToInstallError());
+ tr("Unresolved dependencies"), installerCalculator()->error());
return false;
}
@@ -431,7 +431,7 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
}
template <typename T>
-bool PackageManagerCorePrivate::loadComponentScripts(const T &components)
+bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
{
infoMessage(nullptr, tr("Loading component scripts..."));
@@ -440,7 +440,7 @@ bool PackageManagerCorePrivate::loadComponentScripts(const T &components)
if (statusCanceledOrFailed())
return false;
- component->loadComponentScript();
+ component->loadComponentScript(postScript);
++loadedComponents;
const int currentProgress = qRound(double(loadedComponents) / components.count() * 100);
@@ -450,8 +450,8 @@ bool PackageManagerCorePrivate::loadComponentScripts(const T &components)
return true;
}
-template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &);
-template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &);
+template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
void PackageManagerCorePrivate::cleanUpComponentEnvironment()
{
@@ -494,6 +494,7 @@ void PackageManagerCorePrivate::clearAllComponentLists()
m_deletedReplacedComponents.clear();
m_componentsToReplaceAllMode.clear();
+ m_foundEssentialUpdate = false;
qDeleteAll(toDelete);
cleanUpComponentEnvironment();
@@ -501,8 +502,10 @@ void PackageManagerCorePrivate::clearAllComponentLists()
void PackageManagerCorePrivate::clearUpdaterComponentLists()
{
- QSet<Component*> usedComponents =
- QSet<Component*>::fromList(m_updaterComponents + m_updaterComponentsDeps);
+
+ QSet<Component*> usedComponents(m_updaterComponents.begin(), m_updaterComponents.end());
+ usedComponents.unite(QSet<Component*>(m_updaterComponentsDeps.begin(),
+ m_updaterComponentsDeps.end()));
const QList<QPair<Component*, Component*> > list = m_componentsToReplaceUpdaterMode.values();
for (int i = 0; i < list.count(); ++i) {
@@ -518,6 +521,7 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists()
m_updaterDependencyReplacements.clear();
m_componentsToReplaceUpdaterMode.clear();
+ m_foundEssentialUpdate = false;
qDeleteAll(usedComponents);
cleanUpComponentEnvironment();
@@ -957,7 +961,7 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
case QXmlStreamReader::StartElement: {
if (reader.name() == QLatin1String("Network")) {
while (reader.readNextStartElement()) {
- const QStringRef name = reader.name();
+ const QStringView name = reader.name();
if (name == QLatin1String("Ftp")) {
m_data.settings().setFtpProxy(readProxy(reader));
} else if (name == QLatin1String("Http")) {
@@ -1332,6 +1336,8 @@ void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &per
<< (targetAppDirPath + QLatin1String("/../plugins")));
performOperationThreaded(op);
}
+#else
+ Q_UNUSED(performedOperations);
#endif
}
@@ -1384,7 +1390,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
bool newBinaryWritten = false;
QString mtName = maintenanceToolName();
const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced);
- if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) {
+ if (!installerBaseBinary.isEmpty() && QFileInfo::exists(installerBaseBinary)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< installerBaseBinary;
if (QInstaller::isInBundle(installerBaseBinary)) {
@@ -1435,7 +1441,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
}
}
m_installerBaseBinaryUnreplaced.clear();
- } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) {
+ } else if (!installerBaseBinary.isEmpty() && !QFileInfo::exists(installerBaseBinary)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "The current maintenance tool could not be updated."
<< installerBaseBinary << "does not exist. Please fix the \"setInstallerBaseBinary"
"(<temp_installer_base_binary_path>)\" call in your script.";
@@ -1926,7 +1932,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// There is a replacement, but the replacement is not scheduled for update, keep it as well.
if (m_componentsToReplaceUpdaterMode.contains(name)
- && !m_installerCalculator->orderedComponentsToInstall().contains(m_componentsToReplaceUpdaterMode.value(name).first)) {
+ && !m_installerCalculator->resolvedComponents().contains(m_componentsToReplaceUpdaterMode.value(name).first)) {
nonRevertedOperations.append(operation);
continue;
}
@@ -2157,7 +2163,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
m_core->downloadNeededArchives(double(1));
const QString installerBaseReplacement = replaceVariables(m_offlineBaseBinaryUnreplaced);
- if (!installerBaseReplacement.isEmpty() && QFileInfo(installerBaseReplacement).exists()) {
+ if (!installerBaseReplacement.isEmpty() && QFileInfo::exists(installerBaseReplacement)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< offlineBinaryTempName;
@@ -2820,7 +2826,7 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadTyp
return m_repoFetched;
}
-bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository)
+bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compressedRepository)
{
if (!compressedRepository && m_updateSourcesAdded)
return m_updateSourcesAdded;
@@ -2851,34 +2857,6 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe
if (data->path().isEmpty())
continue;
- if (parseChecksum) {
- const QString updatesXmlPath = data->path() + QLatin1String("/Updates.xml");
- QFile updatesFile(updatesXmlPath);
- try {
- QInstaller::openForRead(&updatesFile);
- } catch(const Error &e) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Error opening Updates.xml:"
- << e.message();
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- int line = 0;
- int column = 0;
- QString error;
- QDomDocument doc;
- if (!doc.setContent(&updatesFile, &error, &line, &column)) {
- qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Parse error in file "
- << updatesFile.fileName() << ": " << error << " at line " << line
- << " col " << column;
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum"));
- if (!checksum.isNull())
- m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue);
- }
if (data->repository().isCompressed())
m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2));
else
@@ -2929,9 +2907,9 @@ void PackageManagerCorePrivate::updateComponentInstallActions()
? ComponentModelHelper::KeepInstalled
: ComponentModelHelper::KeepUninstalled);
}
- for (Component *component : uninstallerCalculator()->componentsToUninstall())
+ for (Component *component : uninstallerCalculator()->resolvedComponents())
component->setInstallAction(ComponentModelHelper::Uninstall);
- for (Component *component : installerCalculator()->orderedComponentsToInstall())
+ for (Component *component : installerCalculator()->resolvedComponents())
component->setInstallAction(ComponentModelHelper::Install);
}
@@ -3051,9 +3029,16 @@ QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringLi
bool PackageManagerCorePrivate::calculateComponentsAndRun()
{
bool componentsOk = m_core->recalculateAllComponents();
+
if (statusCanceledOrFailed()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled.";
} else if (componentsOk && acceptLicenseAgreements()) {
+ try {
+ loadComponentScripts(installerCalculator()->resolvedComponents(), true);
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ return false;
+ }
qCDebug(QInstaller::lcInstallerInstallLog).noquote()
<< htmlToString(m_core->componentResolveReasons());
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index f316d0363..74cb0667c 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -108,7 +108,7 @@ public:
bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript);
template <typename T>
- bool loadComponentScripts(const T &components);
+ bool loadComponentScripts(const T &components, const bool postScript = false);
void cleanUpComponentEnvironment();
ScriptEngine *componentScriptEngine() const;
@@ -262,7 +262,7 @@ private:
PackagesList remotePackages();
LocalPackagesMap localInstalledPackages();
bool fetchMetaInformationFromRepositories(DownloadType type = DownloadType::All);
- bool addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository = false);
+ bool addUpdateResourcesFromRepositories(bool compressedRepository = false);
void processFilesForDelayedDeletion();
void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result);
QStringList runningInstallerProcesses(const QStringList &exludeFiles);
@@ -291,7 +291,7 @@ private:
qint64 m_magicBinaryMarker;
int m_magicMarkerSupplement;
- bool m_foundEssentialUpdate;;
+ bool m_foundEssentialUpdate;
mutable ScriptEngine *m_componentScriptEngine;
mutable ScriptEngine *m_controlScriptEngine;
diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp
index fa3440a72..1d5b9f713 100644
--- a/src/libs/installer/packagemanagercoredata.cpp
+++ b/src/libs/installer/packagemanagercoredata.cpp
@@ -34,7 +34,7 @@
#include <QDesktopServices>
#include <QDir>
-#include <QRegExp>
+#include <QRegularExpression>
#include <QSettings>
#ifdef Q_OS_WIN
@@ -250,7 +250,7 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa
#ifdef Q_OS_WIN
if (!m_variables.contains(key)) {
- static const QRegExp regex(QLatin1String("\\\\|/"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/"));
const QString filename = key.section(regex, 0, -2);
const QString regKey = key.section(regex, -1);
const QSettingsWrapper registry(filename, format);
@@ -293,7 +293,7 @@ QString PackageManagerCoreData::replaceVariables(const QString &str) const
QByteArray PackageManagerCoreData::replaceVariables(const QByteArray &ba) const
{
- static const QChar at = QLatin1Char('@');
+ static const char at = '@';
QByteArray res;
int pos = 0;
while (true) {
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index a83c3a094..7395d938f 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -80,13 +80,15 @@
#include <QShowEvent>
#include <QFileDialog>
#include <QGroupBox>
-#include <QDesktopWidget>
+#include <QScreen>
#ifdef Q_OS_WIN
# include <qt_windows.h>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace KDUpdater;
using namespace QInstaller;
@@ -298,6 +300,11 @@ public:
*/
/*!
+ \fn void QInstaller::PackageManagerGui::aboutApplicationClicked()
+ \sa {gui::aboutApplicationClicked}{gui.aboutApplicationClicked}
+*/
+
+/*!
\fn void QInstaller::PackageManagerGui::packageManagerCore() const
Returns the package manager core.
@@ -461,7 +468,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
*/
void PackageManagerGui::setMaxSize()
{
- QSize size = qApp->desktop()->availableGeometry(this).size();
+ QSize size = this->screen()->availableGeometry().size();
int windowFrameHeight = frameGeometry().height() - geometry().height();
int availableHeight = size.height() - windowFrameHeight;
@@ -513,7 +520,7 @@ void PackageManagerGui::updatePageListWidget()
item->setFont(currentItemFont);
// Current item should be always visible on the list
m_pageListWidget->scrollToItem(item);
- } else if (id > d->m_currentId) {
+ } else {
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
}
@@ -1560,6 +1567,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_updateComponents->setEnabled(!m_offlineMaintenanceTool && ProductKeyCheck::instance()->hasValidKey());
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
connect(core, &PackageManagerCore::metaJobProgress,
@@ -1568,6 +1576,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -1613,6 +1622,7 @@ bool IntroductionPage::validatePage()
}
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0 ,0)
if (m_taskButton) {
if (!m_taskButton->window()) {
if (QWidget *widget = QApplication::activeWindow())
@@ -1624,6 +1634,7 @@ bool IntroductionPage::validatePage()
m_taskButton->progress()->setVisible(true);
}
#endif
+#endif
// fetch updater packages
if (core->isUpdater()) {
@@ -1688,9 +1699,11 @@ bool IntroductionPage::validatePage()
gui()->setSettingsButtonEnabled(true);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton)
m_taskButton->progress()->setVisible(!isComplete());
#endif
+#endif
return isComplete();
}
@@ -1811,11 +1824,13 @@ void IntroductionPage::setErrorMessage(const QString &error)
m_errorLabel->setPalette(palette);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
m_taskButton->progress()->stop();
m_taskButton->progress()->setValue(100);
}
#endif
+#endif
}
/*!
@@ -2248,6 +2263,29 @@ void ComponentSelectionPage::showEvent(QShowEvent *event)
}
/*!
+ Called when \c ComponentSelectionPage is validated.
+ Tries to load \c component scripts for components about to be installed.
+ Returns \c true if the script loading succeeded and the next page is shown.
+*/
+bool ComponentSelectionPage::validatePage()
+{
+ PackageManagerCore *core = packageManagerCore();
+ try {
+ core->loadComponentScripts(core->orderedComponentsToInstall(), true);
+ } catch (const Error &error) {
+ // As component script loading failed, there is error in the script and component is
+ // marked as unselected. Recalculate so that unselected component is removed from install.
+ // User is then able to select other components for install.
+ core->clearComponentsToInstallCalculated();
+ core->calculateComponentsToInstall();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
Selects all components in the component tree.
*/
void ComponentSelectionPage::selectAll()
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index 764c31375..ba27c4af6 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.h
@@ -337,6 +337,7 @@ protected:
void entering() override;
void leaving() override;
void showEvent(QShowEvent *event) override;
+ bool validatePage() override;
private Q_SLOTS:
void setModified(bool modified);
diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp
index d8d1e3dcb..0e4c561f6 100644
--- a/src/libs/installer/performinstallationform.cpp
+++ b/src/libs/installer/performinstallationform.cpp
@@ -49,9 +49,11 @@
#include <QtCore/QTimer>
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace QInstaller;
@@ -96,6 +98,7 @@ PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObje
, m_core(core)
{
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
m_taskButton->progress()->setVisible(true);
@@ -103,6 +106,7 @@ PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObje
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -204,12 +208,14 @@ void PerformInstallationForm::updateProgress()
m_progressBar->setValue(progressPercentage);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
if (!m_taskButton->window() && QApplication::activeWindow())
m_taskButton->setWindow(QApplication::activeWindow()->windowHandle());
m_taskButton->progress()->setValue(progressPercentage);
}
#endif
+#endif
static QString lastLabelText;
if (lastLabelText == progressCoordninator->labelText())
diff --git a/src/libs/installer/permissionsettings.cpp b/src/libs/installer/permissionsettings.cpp
index 235c6667b..68e4ab427 100644
--- a/src/libs/installer/permissionsettings.cpp
+++ b/src/libs/installer/permissionsettings.cpp
@@ -40,25 +40,33 @@ using namespace QInstaller;
PermissionSettings::PermissionSettings(const QString &organization, const QString &application, QObject *parent)
: QSettings(organization, application, parent)
{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
setIniCodec("UTF-8"); // to workaround QTBUG-102334
+#endif
}
PermissionSettings::PermissionSettings(Scope scope, const QString &organization, const QString &application, QObject *parent)
: QSettings(scope, organization, application, parent)
{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
setIniCodec("UTF-8"); // QTBUG-102334
+#endif
}
PermissionSettings::PermissionSettings(Format format, Scope scope, const QString &organization, const QString &application, QObject *parent)
: QSettings(format, scope, organization, application, parent)
{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
setIniCodec("UTF-8"); // QTBUG-102334
+#endif
}
PermissionSettings::PermissionSettings(const QString &fileName, Format format, QObject *parent)
: QSettings(fileName, format, parent)
{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
setIniCodec("UTF-8"); // QTBUG-102334
+#endif
}
PermissionSettings::~PermissionSettings()
diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp
index f37d65b29..6f34e36da 100644
--- a/src/libs/installer/qprocesswrapper.cpp
+++ b/src/libs/installer/qprocesswrapper.cpp
@@ -32,6 +32,7 @@
#include "utils.h"
#include <QDir>
+#include <QVariant>
namespace QInstaller {
@@ -356,7 +357,7 @@ void QProcessWrapper::start(const QString &param1, QIODevice::OpenMode param2)
callRemoteMethod(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
m_lock.unlock();
} else {
- process.start(param1, param2);
+ process.start(param1, {}, param2);
}
}
diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp
index f72e67867..a29e99f94 100644
--- a/src/libs/installer/qtpatch.cpp
+++ b/src/libs/installer/qtpatch.cpp
@@ -67,11 +67,11 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
QFileInfo qmake(qmakePath);
if (!qmake.exists()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
if (!qmake.isExecutable()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
@@ -88,7 +88,7 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
, QString::fromLatin1("Standard output: \"%1\".").arg(QLatin1String(output))
, QString::fromLatin1("Error output: \"%1\".").arg(QLatin1String(process.readAllStandardError()))
};
- qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')));
+ qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')).toUtf8());
return qmakeValueHash;
}
qmakeOutput->append(output);
diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h
index e1809e0af..3dec97c95 100644
--- a/src/libs/installer/remoteclient_p.h
+++ b/src/libs/installer/remoteclient_p.h
@@ -40,8 +40,8 @@
#include <QCoreApplication>
#include <QDeadlineTimer>
+#include <QRecursiveMutex>
#include <QTimer>
-#include <QMutex>
#include <QThread>
namespace QInstaller {
@@ -55,7 +55,6 @@ public:
RemoteClientPrivate(RemoteClient *parent)
: RemoteObject(QLatin1String("RemoteClientPrivate"))
, q_ptr(parent)
- , m_mutex(QMutex::Recursive)
, m_startServerAs(Protocol::StartAs::User)
, m_serverStarted(false)
, m_active(false)
@@ -201,7 +200,7 @@ public:
private:
RemoteClient *q_ptr;
- QMutex m_mutex;
+ QRecursiveMutex m_mutex;
QString m_socketName;
Protocol::StartAs m_startServerAs;
bool m_serverStarted;
diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h
index 4dce2a218..c423de943 100644
--- a/src/libs/installer/remoteobject.h
+++ b/src/libs/installer/remoteobject.h
@@ -35,8 +35,10 @@
#include <QCoreApplication>
#include <QDataStream>
-#include <QObject>
#include <QLocalSocket>
+#include <QObject>
+#include <QVariant>
+
namespace QInstaller {
diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp
index 05446ec19..ed3d343fe 100644
--- a/src/libs/installer/remoteserverconnection.cpp
+++ b/src/libs/installer/remoteserverconnection.cpp
@@ -278,7 +278,7 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr
qint64 pid = -1;
bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid);
- reply->send(qMakePair< bool, qint64>(success, pid));
+ reply->send(QPair<bool, qint64>(success, pid));
} else if (command == QLatin1String(Protocol::QProcessStartDetached2)) {
QString program;
QStringList arguments;
@@ -289,7 +289,7 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr
qint64 pid = -1;
bool success = QProcess::startDetached(program, arguments, workingDirectory, &pid);
- reply->send(qMakePair< bool, qint64>(success, pid));
+ reply->send(QPair<bool, qint64>(success, pid));
} else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) {
QString dir;
data >> dir;
@@ -313,7 +313,7 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr
qint32 mode;
data >> program;
data >> mode;
- m_process->start(program, static_cast<QIODevice::OpenMode> (mode));
+ m_process->start(program, {}, static_cast<QIODevice::OpenMode> (mode));
} else if (command == QLatin1String(Protocol::QProcessState)) {
reply->send(static_cast<qint32> (m_process->state()));
} else if (command == QLatin1String(Protocol::QProcessTerminate)) {
@@ -517,13 +517,13 @@ void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->read(byteArray.data(), maxlen);
- reply->send(qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineReadLine)) {
qint64 maxlen;
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->readLine(byteArray.data(), maxlen);
- reply->send(qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRemove)) {
reply->send(m_engine->remove());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRename)) {
diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp
index ddbf3b382..4ef8f9f25 100644
--- a/src/libs/installer/repository.cpp
+++ b/src/libs/installer/repository.cpp
@@ -64,6 +64,7 @@ Repository::Repository(const Repository &other)
, m_displayname(other.m_displayname)
, m_compressed(other.m_compressed)
, m_categoryname(other.m_categoryname)
+ , m_xmlChecksum(other.m_xmlChecksum)
{
}
@@ -221,6 +222,28 @@ void Repository::setCategoryName(const QString &categoryname)
}
/*!
+ Returns the expected checksum of the repository, which is the checksum
+ calculated from the \c Updates.xml document at the root of the repository.
+
+ This value is used as a hint when looking for already fetched repositories
+ from the local cache. If the installer has cached a repository with a matching
+ checksum, it can skip downloading the \c Updates.xml file for that repository again.
+*/
+QByteArray Repository::xmlChecksum() const
+{
+ return m_xmlChecksum;
+}
+
+/*!
+ Sets the expected checksum of the repository to \c checksum. The checksum
+ is calculated from the \c Updates.xml document at the root of the repository.
+*/
+void Repository::setXmlChecksum(const QByteArray &checksum)
+{
+ m_xmlChecksum = checksum;
+}
+
+/*!
Returns true if repository is compressed
*/
bool Repository::isCompressed() const
@@ -235,7 +258,8 @@ bool Repository::isCompressed() const
bool Repository::operator==(const Repository &other) const
{
return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled
- && m_username == other.m_username && m_password == other.m_password && m_displayname == other.m_displayname;
+ && m_username == other.m_username && m_password == other.m_password
+ && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum;
}
/*!
@@ -263,6 +287,7 @@ const Repository &Repository::operator=(const Repository &other)
m_displayname = other.m_displayname;
m_compressed = other.m_compressed;
m_categoryname = other.m_categoryname;
+ m_xmlChecksum = other.m_xmlChecksum;
return *this;
}
@@ -270,7 +295,9 @@ const Repository &Repository::operator=(const Repository &other)
void Repository::registerMetaType()
{
qRegisterMetaType<Repository>("Repository");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<Repository>("Repository");
+#endif
}
/*!
@@ -280,7 +307,7 @@ QDataStream &operator>>(QDataStream &istream, Repository &repository)
{
QByteArray url, username, password, displayname, compressed;
istream >> url >> repository.m_default >> repository.m_enabled >> username >> password
- >> displayname >> repository.m_categoryname;
+ >> displayname >> repository.m_categoryname >> repository.m_xmlChecksum;
repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url)));
repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username)));
repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password)));
@@ -295,7 +322,8 @@ QDataStream &operator<<(QDataStream &ostream, const Repository &repository)
{
return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled
<< repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64()
- << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64();
+ << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64()
+ << repository.m_xmlChecksum.toBase64();
}
}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
index 3f28e4d99..1edb83449 100644
--- a/src/libs/installer/repository.h
+++ b/src/libs/installer/repository.h
@@ -67,6 +67,9 @@ public:
QString categoryname() const;
void setCategoryName(const QString &categoryname);
+ QByteArray xmlChecksum() const;
+ void setXmlChecksum(const QByteArray &checksum);
+
bool isCompressed() const;
bool operator==(const Repository &other) const;
bool operator!=(const Repository &other) const;
@@ -86,6 +89,7 @@ private:
QString m_displayname;
QString m_categoryname;
bool m_compressed;
+ QByteArray m_xmlChecksum;
};
inline uint qHash(const Repository &repository)
diff --git a/src/libs/installer/repositorycategory.cpp b/src/libs/installer/repositorycategory.cpp
index e651b7f0c..6be292330 100644
--- a/src/libs/installer/repositorycategory.cpp
+++ b/src/libs/installer/repositorycategory.cpp
@@ -75,7 +75,9 @@ RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
void RepositoryCategory::registerMetaType()
{
qRegisterMetaType<RepositoryCategory>("RepositoryCategory");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<RepositoryCategory>("RepositoryCategory");
+#endif
}
/*!
diff --git a/src/libs/installer/runextensions.h b/src/libs/installer/runextensions.h
index e39ae6c81..9c7147141 100644
--- a/src/libs/installer/runextensions.h
+++ b/src/libs/installer/runextensions.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,384 +37,70 @@ QT_BEGIN_NAMESPACE
namespace QtConcurrent {
-template <typename T, typename FunctionPointer>
-class StoredInterfaceFunctionCall0 : public QRunnable
+template <typename T, typename FunctionPointer, typename... Args>
+class StoredInterfaceFunctionCall : public QRunnable
{
public:
- StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &))
- : fn(fn) { }
+ StoredInterfaceFunctionCall(void (fn)(QFutureInterface<T> &, Args...), const Args&&... args)
+ : m_fn(fn), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
void run() override
{
- fn(futureInterface);
- futureInterface.reportFinished();
+ fn(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
-
-};
-template <typename T, typename FunctionPointer, typename Class>
-class StoredInterfaceMemberFunctionCall0 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object)
- : fn(fn), object(object) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
-
-};
-
-template <typename T, typename FunctionPointer, typename Arg1>
-class StoredInterfaceFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
- : fn(fn), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1>
-class StoredInterfaceMemberFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1)
- : fn(fn), object(object), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2>
-class StoredInterfaceFunctionCall2 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), arg1(arg1), arg2(arg2) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2;
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2>
-class StoredInterfaceMemberFunctionCall2 : public QRunnable
+template <typename T, typename FunctionPointer, typename Class, typename... Args>
+class StoredInterfaceMemberFunctionCall : public QRunnable
{
public:
- StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), object(object), arg1(arg1), arg2(arg2) { }
+ StoredInterfaceMemberFunctionCall(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, const Args&&... args)
+ : m_fn(fn), m_object(object), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
void run() override
{
- (object->*fn)(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
+ (m_object->*m_fn)(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2;
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ Class *m_object;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceFunctionCall3 : public QRunnable
+template <typename T, typename... Args>
+QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Args...), Args... args)
{
-public:
- StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceMemberFunctionCall3 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceMemberFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceMemberFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-
-template <typename T>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &))
-{
- return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start();
-}
-template <typename T, typename Arg1>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
-{
- return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start();
-}
-template <typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start();
+ return (new StoredInterfaceFunctionCall<T, void (*)(QFutureInterface<T> &, Args...), Args...>(functionPointer, args...))->start();
}
-template <typename Class, typename T>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object)
+template <typename Class, typename T, typename... Args>
+QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, Args... args)
{
- return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start();
+ return (new StoredInterfaceMemberFunctionCall<T, void (Class::*)(QFutureInterface<T> &, Args...), Class, Args...>(fn, object, args...))->start();
}
-template <typename Class, typename T, typename Arg1>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1)
-{
- return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start();
-}
} // namespace QtConcurrent
QT_END_NAMESPACE
diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index 009215909..baf348868 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,7 @@
#include "loggingutils.h"
#include "packagemanagergui.h"
#include "component.h"
+#include "settings.h"
#include <QMetaEnum>
#include <QQmlEngine>
@@ -95,7 +96,7 @@ QJSValue InstallerProxy::components(const QString &regexp) const
QJSValue InstallerProxy::componentByName(const QString &componentName)
{
if (m_core)
- return m_engine->newQObject(m_core->componentByName(componentName));
+ return m_engine->newQObject(m_core->componentByName(componentName), false);
return QJSValue();
}
@@ -377,9 +378,9 @@ QString QFileDialogProxy::getExistingFileOrDirectory(const QString &caption,
/*!
Constructs a script engine with \a core as parent.
*/
-ScriptEngine::ScriptEngine(PackageManagerCore *core) :
- QObject(core),
- m_guiProxy(new GuiProxy(this, this))
+ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core)
+ , m_guiProxy(new GuiProxy(this, this))
+ , m_core(core)
{
m_engine.installExtensions(QJSEngine::TranslationExtension);
QJSValue global = m_engine.globalObject();
@@ -418,9 +419,9 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
/*!
Creates a JavaScript object that wraps the given QObject \a object.
- Signals and slots, properties and children of \a object are
- available as properties of the created QJSValue. In addition some helper methods and properties
- are added:
+ Signals and slots, properties and children of \a object are available as properties
+ of the created QJSValue. If \a qtScriptCompat is set to \c true (default), some helper
+ methods and properties from the legacy \c QtScript module are added:
\list
\li findChild(), findChildren() recursively search for child objects with the given
@@ -429,7 +430,7 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
names.
\endlist
*/
-QJSValue ScriptEngine::newQObject(QObject *object)
+QJSValue ScriptEngine::newQObject(QObject *object, bool qtScriptCompat)
{
QJSValue jsValue = m_engine.newQObject(object);
if (!jsValue.isQObject())
@@ -437,6 +438,9 @@ QJSValue ScriptEngine::newQObject(QObject *object)
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
+ if (!qtScriptCompat) // skip adding the extra properties
+ return jsValue;
+
// add findChild(), findChildren() methods known from QtScript
QJSValue findChild = m_engine.evaluate(
QLatin1String("(function() { return gui.findChild(this, arguments[0]); })"));
@@ -571,14 +575,17 @@ QJSValue ScriptEngine::callScriptMethod(const QJSValue &scriptContext, const QSt
if (!method.isCallable())
return QJSValue(QJSValue::UndefinedValue);
if (method.isError()) {
- throw Error(method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : method.toString());
+ QString errorString = method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : method.toString();
+
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
const QJSValue result = method.call(arguments);
if (result.isError()) {
- throw Error(result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : result.toString());
+ QString errorString = result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : result.toString();
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
stack.removeLast();
@@ -654,7 +661,7 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths)
SETPROPERTY(desktopServices, TempLocation, QStandardPaths)
SETPROPERTY(desktopServices, HomeLocation, QStandardPaths)
- SETPROPERTY(desktopServices, DataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, CacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, RuntimeLocation, QStandardPaths)
diff --git a/src/libs/installer/scriptengine.h b/src/libs/installer/scriptengine.h
index ae3cdd04f..0b43465cb 100644
--- a/src/libs/installer/scriptengine.h
+++ b/src/libs/installer/scriptengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,7 +48,7 @@ public:
explicit ScriptEngine(PackageManagerCore *core = 0);
QJSValue globalObject() const { return m_engine.globalObject(); }
- QJSValue newQObject(QObject *object);
+ QJSValue newQObject(QObject *object, bool qtScriptCompat = true);
QJSValue newArray(uint length = 0);
QJSValue evaluate(const QString &program, const QString &fileName = QString(),
int lineNumber = 1);
@@ -77,6 +77,7 @@ private:
QJSEngine m_engine;
QHash<QString, QStringList> m_callstack;
GuiProxy *m_guiProxy;
+ PackageManagerCore *m_core;
};
}
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
index df7d5f8cc..85ab0dece 100644
--- a/src/libs/installer/settings.cpp
+++ b/src/libs/installer/settings.cpp
@@ -745,7 +745,7 @@ Settings::Update Settings::updateRepositoryCategories(const RepoHash &updates)
return update ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
}
-static bool apply(const RepoHash &updates, QHash<QUrl, Repository> *reposToUpdate)
+static bool apply(const RepoHash &updates, QMultiHash<QUrl, Repository> *reposToUpdate)
{
bool update = false;
QList<QPair<Repository, Repository> > values = updates.values(QLatin1String("replace"));
@@ -783,7 +783,7 @@ Settings::Update Settings::updateDefaultRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> defaultRepos;
+ QMultiHash <QUrl, Repository> defaultRepos;
foreach (const QVariant &variant, d->m_data.values(scRepositories)) {
const Repository repository = variant.value<Repository>();
defaultRepos.insert(repository.url(), repository);
@@ -837,7 +837,7 @@ Settings::Update Settings::updateUserRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> reposToUpdate;
+ QMultiHash <QUrl, Repository> reposToUpdate;
foreach (const QVariant &variant, d->m_data.values(scUserRepositories)) {
const Repository repository = variant.value<Repository>();
reposToUpdate.insert(repository.url(), repository);
@@ -993,7 +993,7 @@ void Settings::setSaveDefaultRepositories(bool save)
QString Settings::repositoryCategoryDisplayName() const
{
QString displayName = d->m_data.value(QLatin1String(scRepositoryCategoryDisplayName)).toString();
- return displayName.isEmpty() ? tr("Select Categories") : displayName;
+ return displayName.isEmpty() ? tr("Categories") : displayName;
}
void Settings::setRepositoryCategoryDisplayName(const QString& name)
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index ebb65a382..f98319110 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -42,7 +42,7 @@
namespace QInstaller {
class Repository;
-typedef QHash<QString, QPair<Repository, Repository> > RepoHash;
+typedef QMultiHash<QString, QPair<Repository, Repository> > RepoHash;
class INSTALLER_EXPORT Settings
{
diff --git a/src/libs/installer/sysinfo_win.cpp b/src/libs/installer/sysinfo_win.cpp
index e6c2a0be8..e74eb1d4e 100644
--- a/src/libs/installer/sysinfo_win.cpp
+++ b/src/libs/installer/sysinfo_win.cpp
@@ -150,7 +150,7 @@ QList<VolumeInfo> mountedVolumes()
bool pathIsOnLocalDevice(const QString &path)
{
- if (!QFileInfo(path).exists())
+ if (!QFileInfo::exists(path))
return false;
if (path.startsWith(QLatin1String("//")))
diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp
index 214e1124a..de753f71a 100644
--- a/src/libs/installer/uninstallercalculator.cpp
+++ b/src/libs/installer/uninstallercalculator.cpp
@@ -44,25 +44,21 @@ UninstallerCalculator::UninstallerCalculator(PackageManagerCore *core
, const AutoDependencyHash &autoDependencyComponentHash
, const LocalDependencyHash &localDependencyComponentHash
, const QStringList &localVirtualComponents)
- : m_core(core)
+ : CalculatorBase(core)
, m_autoDependencyComponentHash(autoDependencyComponentHash)
, m_localDependencyComponentHash(localDependencyComponentHash)
, m_localVirtualComponents(localVirtualComponents)
{
}
-QSet<Component *> UninstallerCalculator::componentsToUninstall() const
+UninstallerCalculator::~UninstallerCalculator()
{
- return m_componentsToUninstall;
}
-QString UninstallerCalculator::componentsToUninstallError() const
+bool UninstallerCalculator::solveComponent(Component *component, const QString &version)
{
- return m_componentsToUninstallError;
-}
+ Q_UNUSED(version)
-bool UninstallerCalculator::appendComponentToUninstall(Component *component)
-{
if (!component)
return true;
@@ -76,7 +72,7 @@ bool UninstallerCalculator::appendComponentToUninstall(Component *component)
if (!depComponent || !depComponent->isInstalled())
continue;
- if (m_componentsToUninstall.contains(depComponent)
+ if (m_resolvedComponents.contains(depComponent)
|| m_core->orderedComponentsToInstall().contains(depComponent)) {
// Component is already selected for uninstall or update
continue;
@@ -86,28 +82,28 @@ bool UninstallerCalculator::appendComponentToUninstall(Component *component)
const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
"Impossible dependency resolution detected. Forced install component \"%1\" would be uninstalled "
"because its dependency \"%2\" is marked for uninstallation with reason: \"%3\".")
- .arg(depComponent->name(), component->name(), uninstallReason(component));
+ .arg(depComponent->name(), component->name(), resolutionText(component));
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToUninstallError.append(errorMessage);
+ m_errorString.append(errorMessage);
return false;
}
// Resolve also all cascading dependencies
- if (!appendComponentToUninstall(depComponent))
+ if (!solveComponent(depComponent))
return false;
- insertUninstallReason(depComponent, UninstallerCalculator::Dependent, component->name());
+ insertResolution(depComponent, Resolution::Dependent, component->name());
}
}
- m_componentsToUninstall.insert(component);
+ m_resolvedComponents.append(component);
return true;
}
-bool UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components)
+bool UninstallerCalculator::solve(const QList<Component*> &components)
{
foreach (Component *component, components) {
- if (!appendComponentToUninstall(component))
+ if (!solveComponent(component))
return false;
}
@@ -122,8 +118,8 @@ bool UninstallerCalculator::appendComponentsToUninstall(const QList<Component*>
Component *autoDepComponent = m_core->componentByName(autoDependencyComponent);
if (autoDepComponent && autoDepComponent->isInstalled()) {
// A component requested auto uninstallation, keep it to resolve their dependencies as well.
- if (!m_componentsToUninstall.contains(autoDepComponent)) {
- insertUninstallReason(autoDepComponent, UninstallerCalculator::AutoDependent, component->name());
+ if (!m_resolvedComponents.contains(autoDepComponent)) {
+ insertResolution(autoDepComponent, Resolution::Automatic, component->name());
autoDepComponent->setInstallAction(ComponentModelHelper::AutodependUninstallation);
autoDependOnList.append(autoDepComponent);
}
@@ -132,56 +128,38 @@ bool UninstallerCalculator::appendComponentsToUninstall(const QList<Component*>
}
if (!autoDependOnList.isEmpty())
- appendComponentsToUninstall(autoDependOnList);
+ solve(autoDependOnList);
else
appendVirtualComponentsToUninstall();
return true;
}
-void UninstallerCalculator::insertUninstallReason(Component *component, const UninstallReasonType uninstallReason,
- const QString &referencedComponentName)
-{
- // keep the first reason
- if (m_toUninstallComponentIdReasonHash.contains(component->name()))
- return;
- m_toUninstallComponentIdReasonHash.insert(component->name(),
- qMakePair(uninstallReason, referencedComponentName));
-}
-
-QString UninstallerCalculator::uninstallReason(Component *component) const
+QString UninstallerCalculator::resolutionText(Component *component) const
{
- UninstallerCalculator::UninstallReasonType reason = uninstallReasonType(component);
+ Resolution reason = resolutionType(component);
switch (reason) {
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("UninstallerCalculator",
"Deselected Components:");
- case Replaced:
+ case Resolution::Replaced:
return QCoreApplication::translate("UninstallerCalculator", "Components replaced "
- "by \"%1\":").arg(uninstallReasonReferencedComponent(component));
- case VirtualDependent:
+ "by \"%1\":").arg(referencedComponent(component));
+ case Resolution::VirtualDependent:
return QCoreApplication::translate("UninstallerCalculator",
"Removing virtual components without existing dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("UninstallerCalculator", "Components "
- "dependency \"%1\" removed:").arg(uninstallReasonReferencedComponent(component));
- case AutoDependent:
+ "dependency \"%1\" removed:").arg(referencedComponent(component));
+ case Resolution::Automatic:
return QCoreApplication::translate("UninstallerCalculator", "Components "
- "autodependency \"%1\" removed:").arg(uninstallReasonReferencedComponent(component));
+ "autodependency \"%1\" removed:").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid uninstall resolution detected!");
}
return QString();
}
-UninstallerCalculator::UninstallReasonType UninstallerCalculator::uninstallReasonType(Component *c) const
-{
- return m_toUninstallComponentIdReasonHash.value(c->name()).first;
-}
-
-QString UninstallerCalculator::uninstallReasonReferencedComponent(Component *component) const
-{
- return m_toUninstallComponentIdReasonHash.value(component->name()).second;
-}
-
void UninstallerCalculator::appendVirtualComponentsToUninstall()
{
QList<Component*> unneededVirtualList;
@@ -191,21 +169,20 @@ void UninstallerCalculator::appendVirtualComponentsToUninstall()
if (!virtualComponent)
continue;
- if (virtualComponent->isInstalled() && !m_componentsToUninstall.contains(virtualComponent)) {
+ if (virtualComponent->isInstalled() && !m_resolvedComponents.contains(virtualComponent)) {
// Components with auto dependencies were handled in the previous step
if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation())
continue;
if (!isRequiredVirtualPackage(virtualComponent)) {
unneededVirtualList.append(virtualComponent);
- m_virtualComponentsForReverse.append(virtualComponent);
- insertUninstallReason(virtualComponent, UninstallerCalculator::VirtualDependent);
+ insertResolution(virtualComponent, Resolution::VirtualDependent);
}
}
}
if (!unneededVirtualList.isEmpty())
- appendComponentsToUninstall(unneededVirtualList);
+ solve(unneededVirtualList);
}
bool UninstallerCalculator::isRequiredVirtualPackage(Component *component)
@@ -213,10 +190,11 @@ bool UninstallerCalculator::isRequiredVirtualPackage(Component *component)
const QStringList localInstallDependents = m_core->localDependenciesToComponent(component);
for (const QString &dependent : localInstallDependents) {
Component *comp = m_core->componentByName(dependent);
- if (!m_componentsToUninstall.contains(comp)) {
+ if (!m_resolvedComponents.contains(comp)) {
return true;
}
}
return m_core->isDependencyForRequestedComponent(component);
}
+
} // namespace QInstaller
diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h
index 6bdd342d2..24979a9bb 100644
--- a/src/libs/installer/uninstallercalculator.h
+++ b/src/libs/installer/uninstallercalculator.h
@@ -30,6 +30,7 @@
#include "installer_global.h"
#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -41,50 +42,30 @@ namespace QInstaller {
class Component;
class PackageManagerCore;
-class INSTALLER_EXPORT UninstallerCalculator
+class INSTALLER_EXPORT UninstallerCalculator : public CalculatorBase
{
public:
- enum UninstallReasonType
- {
- Selected, // "Deselected Component(s)"
- Replaced, // "Component(s) replaced by other components"
- VirtualDependent, // "No dependencies to virtual component"
- Dependent, // "Removed as dependency component is removed"
- AutoDependent // "Removed as autodependency component is removed"
- };
-
UninstallerCalculator(PackageManagerCore *core,
const AutoDependencyHash &autoDependencyComponentHash,
const LocalDependencyHash &localDependencyComponentHash,
const QStringList &localVirtualComponents);
+ ~UninstallerCalculator();
- QSet<Component*> componentsToUninstall() const;
- QString componentsToUninstallError() const;
-
- bool appendComponentsToUninstall(const QList<Component*> &components);
- void insertUninstallReason(Component *component,
- const UninstallReasonType uninstallReason,
- const QString &referencedComponentName = QString());
- QString uninstallReason(Component *component) const;
- UninstallerCalculator::UninstallReasonType uninstallReasonType(Component *c) const;
+ bool solve(const QList<Component*> &components) override;
+ QString resolutionText(Component *component) const override;
private:
- QString uninstallReasonReferencedComponent(Component *component) const;
+ bool solveComponent(Component *component, const QString &version = QString()) override;
+
bool isRequiredVirtualPackage(Component *component);
- bool appendComponentToUninstall(Component *component);
void appendVirtualComponentsToUninstall();
- QString m_componentsToUninstallError;
- QSet<Component *> m_componentsToUninstall;
- PackageManagerCore *m_core;
- QHash<QString, QPair<UninstallReasonType, QString> > m_toUninstallComponentIdReasonHash;
+private:
AutoDependencyHash m_autoDependencyComponentHash;
LocalDependencyHash m_localDependencyComponentHash;
QStringList m_localVirtualComponents;
- QList<Component *> m_virtualComponentsForReverse;
};
-}
-
+} // namespace QInstaller
#endif // UNINSTALLERCALCULATOR_H
diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp
index e9fb45f2a..154a66553 100644
--- a/src/libs/installer/utils.cpp
+++ b/src/libs/installer/utils.cpp
@@ -57,7 +57,7 @@
*/
void QInstaller::uiDetachedWait(int ms)
{
- QTime timer;
+ QElapsedTimer timer;
timer.start();
do {
QCoreApplication::processEvents(QEventLoop::AllEvents, ms);
@@ -139,7 +139,7 @@ QStringList QInstaller::localeCandidates(const QString &locale_)
at least one mutually exclusive pair of options set. Otherwise returns an empty
\c QStringList. The options considered mutual are provided with \a options.
*/
-QStringList QInstaller::checkMutualOptions(CommandLineParser &parser, const QStringList &options)
+QStringList QInstaller::checkMutualOptions(const CommandLineParser &parser, const QStringList &options)
{
QStringList mutual;
foreach (const QString &option, options) {
diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h
index 2bf997835..068490cc2 100644
--- a/src/libs/installer/utils.h
+++ b/src/libs/installer/utils.h
@@ -69,7 +69,7 @@ namespace QInstaller {
QStringList INSTALLER_EXPORT localeCandidates(const QString &locale);
- QStringList INSTALLER_EXPORT checkMutualOptions(CommandLineParser &parser, const QStringList &options);
+ QStringList INSTALLER_EXPORT checkMutualOptions(const CommandLineParser &parser, const QStringList &options);
INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string);
}
diff --git a/src/libs/kdtools/filedownloader.h b/src/libs/kdtools/filedownloader.h
index f02a4cc85..3990d849a 100644
--- a/src/libs/kdtools/filedownloader.h
+++ b/src/libs/kdtools/filedownloader.h
@@ -58,6 +58,8 @@ public:
QByteArray assumedSha1Sum() const;
void setAssumedSha1Sum(const QByteArray &sha1);
+ void setCheckSha1Sum(const bool checkSha1Sum);
+ bool checkSha1Sum() const;
QString scheme() const;
void setScheme(const QString &scheme);
diff --git a/src/libs/kdtools/kdsysinfo_win.cpp b/src/libs/kdtools/kdsysinfo_win.cpp
index 465029252..d423ef01e 100644
--- a/src/libs/kdtools/kdsysinfo_win.cpp
+++ b/src/libs/kdtools/kdsysinfo_win.cpp
@@ -66,7 +66,13 @@ QList<ProcessInfo> runningProcesses()
QStringList deviceList;
const DWORD bufferSize = 1024;
char buffer[bufferSize + 1] = { 0 };
- if (QSysInfo::windowsVersion() <= QSysInfo::WV_5_2) {
+
+ // Qt6 does not support Win before 10
+ bool winVerLessEqual5_2 = false;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ winVerLessEqual5_2 = QSysInfo::windowsVersion() <= QSysInfo::WV_5_2;
+#endif
+ if (winVerLessEqual5_2) {
const DWORD size = GetLogicalDriveStringsA(bufferSize, buffer);
deviceList = QString::fromLatin1(buffer, size).split(QLatin1Char(char(0)), Qt::SkipEmptyParts);
}
@@ -85,7 +91,7 @@ QList<ProcessInfo> runningProcesses()
processStruct.dwSize = sizeof(PROCESSENTRY32);
bool foundProcess = Process32First(snapshot, &processStruct);
while (foundProcess) {
- HANDLE procHandle = OpenProcess(QSysInfo::windowsVersion() > QSysInfo::WV_5_2
+ HANDLE procHandle = OpenProcess(!winVerLessEqual5_2
? KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, false, processStruct
.th32ProcessID);
@@ -93,7 +99,7 @@ QList<ProcessInfo> runningProcesses()
QString executablePath;
DWORD bufferSize = 1024;
- if (QSysInfo::windowsVersion() > QSysInfo::WV_5_2) {
+ if (!winVerLessEqual5_2) {
succ = pQueryFullProcessImageNamePtr(procHandle, 0, buffer, &bufferSize);
executablePath = QString::fromLatin1(buffer);
} else if (pGetProcessImageFileNamePtr) {
diff --git a/src/libs/kdtools/lockfile_win.cpp b/src/libs/kdtools/lockfile_win.cpp
index 20961cc4e..7fc808d39 100644
--- a/src/libs/kdtools/lockfile_win.cpp
+++ b/src/libs/kdtools/lockfile_win.cpp
@@ -45,7 +45,7 @@ bool LockFile::Private::lock()
errorString.clear();
handle = CreateFile(filename.toStdWString().data(), GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, NULL, QFileInfo(filename).exists() ? OPEN_EXISTING : CREATE_NEW,
+ FILE_SHARE_READ, NULL, QFileInfo::exists(filename) ? OPEN_EXISTING : CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle == INVALID_HANDLE_VALUE) {
diff --git a/src/libs/kdtools/sysinfo_x11.cpp b/src/libs/kdtools/sysinfo_x11.cpp
index a59fd150b..24ef099ca 100644
--- a/src/libs/kdtools/sysinfo_x11.cpp
+++ b/src/libs/kdtools/sysinfo_x11.cpp
@@ -40,7 +40,7 @@
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
namespace KDUpdater {
@@ -124,9 +124,9 @@ QList<ProcessInfo> runningProcesses()
QList<ProcessInfo> processes;
QDir procDir(QLatin1String("/proc"));
const QFileInfoList procCont = procDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);
- QRegExp validator(QLatin1String("[0-9]+"));
- Q_FOREACH (const QFileInfo &info, procCont) {
- if (validator.exactMatch(info.fileName())) {
+ static const QRegularExpression validator(QLatin1String("^[0-9]+$"));
+ for (const QFileInfo &info : procCont) {
+ if (validator.match(info.fileName()).hasMatch()) {
const QString linkPath = QDir(info.absoluteFilePath()).absoluteFilePath(QLatin1String("exe"));
const QFileInfo linkInfo(linkPath);
if (linkInfo.exists()) {
diff --git a/src/libs/kdtools/updatefinder.cpp b/src/libs/kdtools/updatefinder.cpp
index b1070c742..120dcb952 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) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,7 +39,8 @@
#include <QCoreApplication>
#include <QFileInfo>
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QFutureWatcher>
using namespace KDUpdater;
using namespace QInstaller;
@@ -55,72 +56,107 @@ using namespace QInstaller;
objects.
*/
-//
-// Private
-//
-class UpdateFinder::Private
+
+static int computeProgressPercentage(int min, int max, int percent)
{
-public:
- enum struct Resolution {
- AddPackage,
- KeepExisting,
- RemoveExisting
- };
-
- explicit Private(UpdateFinder *qq)
- : q(qq)
- , cancel(false)
- , downloadCompleteCount(0)
- , m_downloadsToComplete(0)
- {}
-
- ~Private()
- {
- clear();
- }
+ return min + qint64(max-min) * percent / 100;
+}
- struct Data {
- Data()
- : downloader(0) {}
- explicit Data(const PackageSource &i, FileDownloader *d = 0)
- : info(i), downloader(d) {}
+static int computePercent(int done, int total)
+{
+ return total ? done * Q_INT64_C(100) / total : 0 ;
+}
- PackageSource info;
- FileDownloader *downloader;
- };
- UpdateFinder *q;
- QHash<QString, Update *> updates;
+/*!
+ Constructs an update finder.
+*/
+UpdateFinder::UpdateFinder()
+ : Task(QLatin1String("UpdateFinder"), Stoppable)
+ , m_cancel(false)
+ , m_downloadCompleteCount(0)
+ , m_downloadsToComplete(0)
+ , m_updatesXmlTasks(0)
+ , m_updatesXmlTasksToComplete(0)
+{
+}
- // Temporary structure that notes down information about updates.
- bool cancel;
- int downloadCompleteCount;
- int m_downloadsToComplete;
- QHash<UpdatesInfo *, Data> m_updatesInfoList;
+/*!
+ Destructor
+*/
+UpdateFinder::~UpdateFinder()
+{
+ clear();
+}
- void clear();
- void computeUpdates();
- void cancelComputeUpdates();
- bool downloadUpdateXMLFiles();
- bool computeApplicableUpdates();
+/*!
+ Returns a list of KDUpdater::Update objects.
+*/
+QList<Update *> UpdateFinder::updates() const
+{
+ return m_updates.values();
+}
- QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo);
- void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList);
- Resolution checkPriorityAndVersion(const PackageSource &source, const QVariantHash &data) const;
- void slotDownloadDone();
+/*!
+ Sets the information about installed local packages \a hub.
+*/
+void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub)
+{
+ m_localPackageHub = std::move(hub);
+}
- QSet<PackageSource> packageSources;
- std::weak_ptr<LocalPackageHub> m_localPackageHub;
-};
+/*!
+ Sets the package \a sources information when searching for applicable packages.
+*/
+void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources)
+{
+ m_packageSources = sources;
+}
+/*!
+ \internal
-static int computeProgressPercentage(int min, int max, int percent)
+ Implemented from KDUpdater::Task::doRun().
+*/
+void UpdateFinder::doRun()
{
- return min + qint64(max-min) * percent / 100;
+ computeUpdates();
}
-static int computePercent(int done, int total)
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doStop()
{
- return total ? done * Q_INT64_C(100) / total : 0 ;
+ cancelComputeUpdates();
+
+ // Wait until the cancel has actually happened, and then return.
+ // Thinking of using QMutex for this. Frank/Till any suggestions?
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doPause()
+{
+ // Not a pausable task
+ return false;
+}
+
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doResume()
+{
+ // Not a pausable task, hence it is not resumable as well
+ return false;
}
/*!
@@ -128,20 +164,22 @@ static int computePercent(int done, int total)
Releases all internal resources consumed while downloading and computing updates.
*/
-void UpdateFinder::Private::clear()
+void UpdateFinder::clear()
{
- qDeleteAll(updates);
- updates.clear();
+ qDeleteAll(m_updates);
+ m_updates.clear();
const QList<Data> values = m_updatesInfoList.values();
foreach (const Data &data, values)
delete data.downloader;
- qDeleteAll(m_updatesInfoList.keys());
+ qDeleteAll(m_updatesInfoList.keyBegin(), m_updatesInfoList.keyEnd());
m_updatesInfoList.clear();
- downloadCompleteCount = 0;
+ m_downloadCompleteCount = 0;
m_downloadsToComplete = 0;
+ qDeleteAll(m_xmlFileTasks);
+ m_xmlFileTasks.clear();
}
/*!
@@ -164,51 +202,59 @@ void UpdateFinder::Private::clear()
\note Each time this function is called, all the previously computed updates are discarded
and its resources are freed.
*/
-void UpdateFinder::Private::computeUpdates()
+void UpdateFinder::computeUpdates()
{
// Computing updates is done in two stages
// 1. Downloading Update XML files from all the update sources
- // 2. Matching updates with Package XML and figuring out available updates
-
+ // 2. Parse attributes from Update XML documents to UpdateInfoList
+ // 3. Remove all updates with invalid content
+ // 4. Matching updates with Package XML and figuring out available updates
clear();
- cancel = false;
+ m_cancel = false;
// First do some quick sanity checks on the packages info
std::shared_ptr<LocalPackageHub> packages = m_localPackageHub.lock();
if (!packages) {
- q->reportError(tr("Cannot access the package information of this application."));
+ reportError(tr("Cannot access the package information of this application."));
return;
}
if (!packages->isValid()) {
- q->reportError(packages->errorString());
+ reportError(packages->errorString());
return;
}
// Now do some quick sanity checks on the package sources.
- if (packageSources.count() <= 0) {
- q->reportError(tr("No package sources set for this application."));
+ if (m_packageSources.count() <= 0) {
+ reportError(tr("No package sources set for this application."));
return;
}
// Now we can start...
+ if (!downloadUpdateXMLFiles() || m_cancel) {
+ clear();
+ return;
+ }
- // Step 1: 0 - 49 percent
- if (!downloadUpdateXMLFiles() || cancel) {
+ if (!parseUpdateXMLFiles() || m_cancel) {
clear();
return;
}
- // Step 2: 50 - 100 percent
- if (!computeApplicableUpdates() || cancel) {
+ if (!removeInvalidObjects() || m_cancel) {
+ clear();
+ return;
+ }
+
+ if (!computeApplicableUpdates() || m_cancel) {
clear();
return;
}
// All done
- q->reportProgress(100, tr("%n update(s) found.", "", updates.count()));
- q->reportDone();
+ reportProgress(100, tr("%n update(s) found.", "", m_updates.count()));
+ reportDone();
}
/*!
@@ -218,9 +264,9 @@ void UpdateFinder::Private::computeUpdates()
\sa computeUpdates()
*/
-void UpdateFinder::Private::cancelComputeUpdates()
+void UpdateFinder::cancelComputeUpdates()
{
- cancel = true;
+ m_cancel = true;
}
/*!
@@ -239,22 +285,22 @@ void UpdateFinder::Private::cancelComputeUpdates()
The function gets into an event loop until all the downloads are complete.
*/
-bool UpdateFinder::Private::downloadUpdateXMLFiles()
+bool UpdateFinder::downloadUpdateXMLFiles()
{
// create UpdatesInfo for each update source
- foreach (const PackageSource &info, packageSources) {
+ foreach (const PackageSource &info, m_packageSources) {
const QUrl url = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString());
if (url.scheme() != QLatin1String("resource") && url.scheme() != QLatin1String("file")) {
// create FileDownloader (except for local files and resources)
- FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), q);
+ FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), this);
if (!downloader)
break;
downloader->setUrl(url);
downloader->setAutoRemoveDownloadedFile(true);
- connect(downloader, SIGNAL(downloadCanceled()), q, SLOT(slotDownloadDone()));
- connect(downloader, SIGNAL(downloadCompleted()), q, SLOT(slotDownloadDone()));
- connect(downloader, SIGNAL(downloadAborted(QString)), q, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadCompleted()), this, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(slotDownloadDone()));
m_updatesInfoList.insert(new UpdatesInfo, Data(info, downloader));
} else {
UpdatesInfo *updatesInfo = new UpdatesInfo;
@@ -264,7 +310,7 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
}
// Trigger download of Updates.xml file
- downloadCompleteCount = 0;
+ m_downloadCompleteCount = 0;
m_downloadsToComplete = 0;
foreach (const Data &data, m_updatesInfoList) {
if (data.downloader) {
@@ -274,39 +320,53 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
}
// Wait until all downloaders have completed their downloads.
- while (true) {
- QCoreApplication::processEvents();
- if (cancel)
- return false;
-
- if (downloadCompleteCount == m_downloadsToComplete)
- break;
-
- q->reportProgress(computePercent(downloadCompleteCount, m_downloadsToComplete),
- tr("Downloading Updates.xml from update sources."));
- }
+ return waitForJobToFinish(m_downloadCompleteCount, m_downloadsToComplete);
+}
+/*!
+ \internal
+*/
+bool UpdateFinder::parseUpdateXMLFiles()
+{
// Setup the update info objects with the files from download.
- foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) {
+ m_updatesXmlTasks = 0;
+ m_updatesXmlTasksToComplete = 0;
+ QList<UpdatesInfo *> keys = m_updatesInfoList.keys();
+ for (UpdatesInfo *updatesInfo : qAsConst(keys)) {
const Data data = m_updatesInfoList.value(updatesInfo);
if (data.downloader) {
if (!data.downloader->isDownloaded()) {
- q->reportError(tr("Cannot download package source %1 from \"%2\".").arg(data
- .downloader->url().fileName(), data.info.url.toString()));
+ reportError(tr("Cannot download package source %1 from \"%2\".").arg(data.
+ downloader->url().fileName(), data.info.url.toString()));
} else {
updatesInfo->setFileName(data.downloader->downloadedFileName());
}
}
+ if (!updatesInfo->fileName().isEmpty()) {
+ ParseXmlFilesTask *const task = new ParseXmlFilesTask(updatesInfo);
+ m_xmlFileTasks.append(task);
+ QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
+ m_updatesXmlTasksToComplete++;
+ connect(watcher, &QFutureWatcherBase::finished, this, &UpdateFinder::parseUpdatesXmlTaskFinished);
+ watcher->setFuture(QtConcurrent::run(&ParseXmlFilesTask::doTask, task));
+ }
}
- // Remove all invalid update info objects.
+ // Wait until all updates.xml files are parsed
+ return waitForJobToFinish(m_updatesXmlTasks, m_updatesXmlTasksToComplete);
+}
+/*!
+ \internal
+*/
+bool UpdateFinder::removeInvalidObjects()
+{
QMutableHashIterator<UpdatesInfo *, Data> it(m_updatesInfoList);
while (it.hasNext()) {
UpdatesInfo *info = it.next().key();
if (info->isValid())
continue;
- q->reportError(info->errorString());
+ reportError(info->errorString());
delete info;
it.remove();
}
@@ -314,7 +374,7 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
if (m_updatesInfoList.isEmpty())
return false;
- q->reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
+ reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
return true;
}
@@ -326,36 +386,40 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
KDUpdater::PackagesInfo. Thereby figures out whether an update is applicable for
this application or not.
*/
-bool UpdateFinder::Private::computeApplicableUpdates()
+bool UpdateFinder::computeApplicableUpdates()
{
int i = 0;
- foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) {
+ QList<UpdatesInfo *> keys = m_updatesInfoList.keys();
+ for (UpdatesInfo *updatesInfo : qAsConst(keys)) {
// Fetch updates applicable to this application.
QList<UpdateInfo> updates = applicableUpdates(updatesInfo);
if (!updates.count())
continue;
- if (cancel)
+ if (m_cancel)
return false;
const PackageSource updateSource = m_updatesInfoList.value(updatesInfo).info;
// Create Update objects for updates that have a valid
// UpdateFile
createUpdateObjects(updateSource, updates);
- if (cancel)
+ if (m_cancel)
return false;
// Report progress
- q->reportProgress(computeProgressPercentage(51, 100, computePercent(i,
+ reportProgress(computeProgressPercentage(51, 100, computePercent(i,
m_updatesInfoList.count())), tr("Computing applicable updates."));
++i;
}
- q->reportProgress(99, tr("Application updates computed."));
+ reportProgress(99, tr("Application updates computed."));
return true;
}
-QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesInfo)
+/*!
+ \internal
+*/
+QList<UpdateInfo> UpdateFinder::applicableUpdates(UpdatesInfo *updatesInfo)
{
const QList<UpdateInfo> dummy;
if (!updatesInfo || updatesInfo->updateInfoCount() == 0)
@@ -384,7 +448,10 @@ QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesI
return updatesInfo->updatesInfo();
}
-void UpdateFinder::Private::createUpdateObjects(const PackageSource &source,
+/*!
+ \internal
+*/
+void UpdateFinder::createUpdateObjects(const PackageSource &source,
const QList<UpdateInfo> &updateInfoList)
{
foreach (const UpdateInfo &info, updateInfoList) {
@@ -394,23 +461,24 @@ void UpdateFinder::Private::createUpdateObjects(const PackageSource &source,
const QString name = info.data.value(QLatin1String("Name")).toString();
if (value == Resolution::RemoveExisting)
- delete updates.take(name);
+ delete m_updates.take(name);
// Create and register the update
- updates.insert(name, new Update(source, info));
+ m_updates.insert(name, new Update(source, info));
}
}
-/*
+/*!
+ \internal
If a package of the same name exists, always use the one with the higher
version. If the new package has the same version but a higher
priority, use the new new package, otherwise keep the already existing package.
*/
-UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion(
+UpdateFinder::Resolution UpdateFinder::checkPriorityAndVersion(
const PackageSource &source, const QVariantHash &newPackage) const
{
const QString name = newPackage.value(QLatin1String("Name")).toString();
- if (Update *existingPackage = updates.value(name)) {
+ if (Update *existingPackage = m_updates.value(name)) {
// Bingo, package was previously found elsewhere.
const int match = compareVersion(newPackage.value(QLatin1String("Version")).toString(),
@@ -442,108 +510,51 @@ UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion
return Resolution::AddPackage;
}
-//
-// UpdateFinder
-//
-
-/*!
- Constructs an update finder.
-*/
-UpdateFinder::UpdateFinder()
- : Task(QLatin1String("UpdateFinder"), Stoppable),
- d(new Private(this))
-{
-}
-
-/*!
- Destructor
-*/
-UpdateFinder::~UpdateFinder()
-{
- delete d;
-}
-
-/*!
- Returns a list of KDUpdater::Update objects.
-*/
-QList<Update *> UpdateFinder::updates() const
-{
- return d->updates.values();
-}
-
-/*!
- Sets the information about installed local packages \a hub.
-*/
-void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub)
-{
- d->m_localPackageHub = std::move(hub);
-}
-
-/*!
- Sets the package \a sources information when searching for applicable packages.
-*/
-void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources)
-{
- d->packageSources = sources;
-}
-
-/*!
- \internal
-
- Implemented from KDUpdater::Task::doRun().
-*/
-void UpdateFinder::doRun()
-{
- d->computeUpdates();
-}
-
/*!
\internal
-
- Implemented from KDUpdater::Task::doStop().
*/
-bool UpdateFinder::doStop()
+bool UpdateFinder::waitForJobToFinish(const int &currentCount, const int &totalsCount)
{
- d->cancelComputeUpdates();
+ while (true) {
+ QCoreApplication::processEvents();
+ if (m_cancel)
+ return false;
- // Wait until the cancel has actually happened, and then return.
- // Thinking of using QMutex for this. Frank/Till any suggestions?
+ if (currentCount == totalsCount)
+ break;
+ reportProgress(computePercent(currentCount, totalsCount),
+ tr("Downloading Updates.xml from update sources."));
+ }
return true;
}
-
/*!
\internal
-
- Implemented from KDUpdater::Task::doStop().
*/
-bool UpdateFinder::doPause()
+void UpdateFinder::parseUpdatesXmlTaskFinished()
{
- // Not a pausable task
- return false;
-}
+ ++m_updatesXmlTasks;
-/*!
- \internal
+ int pc = computePercent(m_updatesXmlTasks, m_updatesXmlTasksToComplete);
+ pc = computeProgressPercentage(0, 45, pc);
+ reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
- Implemented from KDUpdater::Task::doStop().
-*/
-bool UpdateFinder::doResume()
-{
- // Not a pausable task, hence it is not resumable as well
- return false;
+ QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender());
+ watcher->waitForFinished();
+ watcher->deleteLater();
}
+
/*!
\internal
*/
-void UpdateFinder::Private::slotDownloadDone()
+void UpdateFinder::slotDownloadDone()
{
- ++downloadCompleteCount;
+ ++m_downloadCompleteCount;
- int pc = computePercent(downloadCompleteCount, m_downloadsToComplete);
+ int pc = computePercent(m_downloadCompleteCount, m_downloadsToComplete);
pc = computeProgressPercentage(0, 45, pc);
- q->reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
+ reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
}
@@ -589,8 +600,9 @@ int KDUpdater::compareVersion(const QString &v1, const QString &v2)
return 0;
// Split version components across ".", "-" or "_"
- QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-|_")));
- QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-|_")));
+ static const QRegularExpression regex(QLatin1String( "\\.|-|_"));
+ QStringList v1_comps = v1.split(regex);
+ QStringList v2_comps = v2.split(regex);
// Check each component of the version
int index = 0;
diff --git a/src/libs/kdtools/updatefinder.h b/src/libs/kdtools/updatefinder.h
index 5a3f50f62..626a700fd 100644
--- a/src/libs/kdtools/updatefinder.h
+++ b/src/libs/kdtools/updatefinder.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,19 +32,68 @@
#include "task.h"
#include "packagesource.h"
+#include "filedownloader.h"
+#include "updatesinfo_p.h"
+#include "abstracttask.h"
#include <memory>
+using namespace QInstaller;
namespace KDUpdater {
class LocalPackageHub;
class Update;
+class ParseXmlFilesTask : public AbstractTask<void>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ParseXmlFilesTask)
+
+public:
+ ParseXmlFilesTask(UpdatesInfo *info)
+ : m_info(info)
+ {}
+
+ void doTask(QFutureInterface<void> &fi) override
+ {
+ fi.reportStarted();
+ fi.setExpectedResultCount(1);
+
+ if (fi.isCanceled()) {
+ fi.reportFinished();
+ return; // ignore already canceled
+ }
+ m_info->parseFile();
+
+ fi.reportFinished();
+ }
+
+private:
+ UpdatesInfo *const m_info;
+};
+
+
class KDTOOLS_EXPORT UpdateFinder : public Task
{
Q_OBJECT
class Private;
+ struct Data {
+ Data()
+ : downloader(0) {}
+ explicit Data(const QInstaller::PackageSource &i, KDUpdater::FileDownloader *d = 0)
+ : info(i), downloader(d) {}
+
+ QInstaller::PackageSource info;
+ KDUpdater::FileDownloader *downloader;
+ };
+
+ enum struct Resolution {
+ AddPackage,
+ KeepExisting,
+ RemoveExisting
+ };
+
public:
UpdateFinder();
~UpdateFinder();
@@ -59,10 +108,35 @@ private:
bool doStop() override;
bool doPause() override;
bool doResume() override;
+ void clear();
+ void computeUpdates();
+ void cancelComputeUpdates();
+ bool downloadUpdateXMLFiles();
+ bool parseUpdateXMLFiles();
+ bool removeInvalidObjects();
+ bool computeApplicableUpdates();
+
+ QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo);
+ void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList);
+ Resolution checkPriorityAndVersion(const QInstaller::PackageSource &source, const QVariantHash &data) const;
+ bool waitForJobToFinish(const int &currentCount, const int &totalsCount);
+
+private slots:
+ void parseUpdatesXmlTaskFinished();
+ void slotDownloadDone();
private:
- Private *d;
- Q_PRIVATE_SLOT(d, void slotDownloadDone())
+ QSet<PackageSource> m_packageSources;
+ std::weak_ptr<LocalPackageHub> m_localPackageHub;
+ QHash<QString, Update *> m_updates;
+
+ bool m_cancel;
+ int m_downloadCompleteCount;
+ int m_downloadsToComplete;
+ QHash<UpdatesInfo *, Data> m_updatesInfoList;
+ int m_updatesXmlTasks;
+ int m_updatesXmlTasksToComplete;
+ QList<ParseXmlFilesTask*> m_xmlFileTasks;
};
} // namespace KDUpdater
diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp
index 81f456755..6a7e62f2b 100644
--- a/src/libs/kdtools/updateoperation.cpp
+++ b/src/libs/kdtools/updateoperation.cpp
@@ -652,7 +652,7 @@ bool UpdateOperation::fromXml(const QDomDocument &doc)
const QString value = v.text();
const QVariant::Type t = QVariant::nameToType(type.toLatin1().data());
- QVariant var = qVariantFromValue(value);
+ QVariant var = QVariant::fromValue(value);
if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) {
QDataStream stream(QByteArray::fromBase64( value.toLatin1()));
stream >> var;
diff --git a/src/libs/kdtools/updatesinfo.cpp b/src/libs/kdtools/updatesinfo.cpp
index 545931a55..707daf11f 100644
--- a/src/libs/kdtools/updatesinfo.cpp
+++ b/src/libs/kdtools/updatesinfo.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,13 +29,14 @@
#include "updatesinfo_p.h"
#include "utils.h"
+#include "constants.h"
-#include <QDomDocument>
#include <QFile>
#include <QLocale>
#include <QPair>
#include <QVector>
#include <QUrl>
+#include <QXmlStreamReader>
using namespace KDUpdater;
@@ -62,35 +64,26 @@ void UpdatesInfoData::parseFile(const QString &updateXmlFile)
return;
}
- QDomDocument doc;
- QString parseErrorMessage;
- int parseErrorLine, parseErrorColumn;
- if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) {
- error = UpdatesInfo::InvalidXmlError;
- errorMessage = tr("Parse error in %1 at %2, %3: %4").arg(updateXmlFile,
- QString::number(parseErrorLine), QString::number(parseErrorColumn), parseErrorMessage);
- return;
- }
-
- QDomElement rootE = doc.documentElement();
- if (rootE.tagName() != QLatin1String("Updates")) {
- setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(rootE.tagName()));
- return;
- }
-
- QDomNodeList childNodes = rootE.childNodes();
- for(int i = 0; i < childNodes.count(); i++) {
- const QDomElement childE = childNodes.at(i).toElement();
- if (childE.isNull())
- continue;
-
- if (childE.tagName() == QLatin1String("ApplicationName"))
- applicationName = childE.text();
- else if (childE.tagName() == QLatin1String("ApplicationVersion"))
- applicationVersion = childE.text();
- else if (childE.tagName() == QLatin1String("PackageUpdate")) {
- if (!parsePackageUpdateElement(childE))
- return; //error handled in subroutine
+ QXmlStreamReader reader(&file);
+ if (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Updates")) {
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("ApplicationName")) {
+ applicationName = reader.readElementText();
+ } else if (reader.name() == QLatin1String("ApplicationVersion")) {
+ applicationVersion = reader.readElementText();
+ } else if (reader.name() == QLatin1String("Checksum")) {
+ checkSha1CheckSum = (reader.readElementText());
+ } else if (reader.name() == QLatin1String("PackageUpdate")) {
+ if (!parsePackageUpdateElement(reader, checkSha1CheckSum))
+ return; //error handled in subroutine
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+ } else {
+ setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(reader.name()));
+ return;
}
}
@@ -108,74 +101,51 @@ void UpdatesInfoData::parseFile(const QString &updateXmlFile)
error = UpdatesInfo::NoError;
}
-bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
+bool UpdatesInfoData::parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum)
{
- if (updateE.isNull())
- return false;
-
UpdateInfo info;
- QMap<QString, QString> localizedDescriptions;
- for (int i = 0; i < updateE.childNodes().count(); i++) {
- QDomElement childE = updateE.childNodes().at(i).toElement();
- if (childE.isNull())
+ QHash<QString, QVariant> scriptHash;
+ while (reader.readNext()) {
+ const QString elementName = reader.name().toString();
+ if ((reader.name() == QLatin1String("PackageUpdate"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (elementName.isEmpty() || reader.tokenType() == QXmlStreamReader::EndElement)
continue;
-
- if (childE.tagName() == QLatin1String("ReleaseNotes")) {
- info.data[childE.tagName()] = QUrl(childE.text());
- } else if (childE.tagName() == QLatin1String("Licenses")) {
- QHash<QString, QVariant> licenseHash;
- const QDomNodeList licenseNodes = childE.childNodes();
- 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;
- attributes.insert(QLatin1String("file"), element.attributeNode(QLatin1String("file")).value());
- if (!element.attributeNode(QLatin1String("priority")).isNull())
- attributes.insert(QLatin1String("priority"), element.attributeNode(QLatin1String("priority")).value());
- else
- attributes.insert(QLatin1String("priority"), QLatin1String("0"));
- licenseHash.insert(element.attributeNode(QLatin1String("name")).value(), attributes);
- }
- }
- if (!licenseHash.isEmpty())
- info.data.insert(QLatin1String("Licenses"), licenseHash);
- } else if (childE.tagName() == QLatin1String("TreeName")) {
- const bool moveChildren = QVariant(childE.attribute(QLatin1String("moveChildren"))).toBool();
- const QPair<QString, bool> treeNamePair(childE.text(), moveChildren);
+ if (elementName == QLatin1String("Licenses")) {
+ parseLicenses(reader, info.data);
+ } else if (elementName == QLatin1String("TreeName")) {
+ const QXmlStreamAttributes attr = reader.attributes();
+ const bool moveChildren = attr.value(QLatin1String("moveChildren")).toString().toLower() == QInstaller::scTrue ? true : false;
+ const QPair<QString, bool> treeNamePair(reader.readElementText(), moveChildren);
info.data.insert(QLatin1String("TreeName"), QVariant::fromValue(treeNamePair));
- } else if (childE.tagName() == QLatin1String("Version")) {
+ } else if (elementName == QLatin1String("Version")) {
+ const QXmlStreamAttributes attr = reader.attributes();
info.data.insert(QLatin1String("inheritVersionFrom"),
- childE.attribute(QLatin1String("inheritVersionFrom")));
- info.data[childE.tagName()] = childE.text();
- } else if (childE.tagName() == QLatin1String("DisplayName")) {
- processLocalizedTag(childE, info.data);
- } else if (childE.tagName() == QLatin1String("Description")) {
- if (!childE.hasAttribute(QLatin1String("xml:lang")))
- info.data[QLatin1String("Description")] = childE.text();
- QString languageAttribute = childE.attribute(QLatin1String("xml:lang"), QLatin1String("en"));
- localizedDescriptions.insert(languageAttribute.toLower(), childE.text());
- } else if (childE.tagName() == QLatin1String("UpdateFile")) {
- info.data[QLatin1String("CompressedSize")] = childE.attribute(QLatin1String("CompressedSize"));
- info.data[QLatin1String("UncompressedSize")] = childE.attribute(QLatin1String("UncompressedSize"));
- } else if (childE.tagName() == QLatin1String("Operations")) {
- const QDomNodeList operationNodes = childE.childNodes();
- QVariant operationListVariant = parseOperations(childE.childNodes());
- info.data.insert(QLatin1String("Operations"), operationListVariant);
+ attr.value(QLatin1String("inheritVersionFrom")).toString());
+ info.data[elementName] = reader.readElementText();
+ } else if (elementName == QLatin1String("DisplayName")
+ || elementName == QLatin1String("Description")) {
+ processLocalizedTag(reader, info.data);
+ } else if (elementName == QLatin1String("UpdateFile")) {
+ info.data[QLatin1String("CompressedSize")] = reader.attributes().value(QLatin1String("CompressedSize")).toString();
+ info.data[QLatin1String("UncompressedSize")] = reader.attributes().value(QLatin1String("UncompressedSize")).toString();
+ } else if (elementName == QLatin1String("Operations")) {
+ parseOperations(reader, info.data);
+ } else if (elementName == QLatin1String("Script")) {
+ const QXmlStreamAttributes attr = reader.attributes();
+ const bool postLoad = attr.value(QLatin1String("postLoad")).toString().toLower() == QInstaller::scTrue ? true : false;
+ if (postLoad)
+ scriptHash.insert(QLatin1String("postLoadScript"), reader.readElementText());
+ else
+ scriptHash.insert(QLatin1String("installScript"), reader.readElementText());
} else {
- info.data[childE.tagName()] = childE.text();
- }
- }
-
- QStringList candidates;
- foreach (const QString &lang, QLocale().uiLanguages())
- candidates << QInstaller::localeCandidates(lang.toLower());
- foreach (const QString &candidate, candidates) {
- if (localizedDescriptions.contains(candidate)) {
- info.data[QLatin1String("Description")] = localizedDescriptions.value(candidate);
- break;
+ info.data[elementName] = reader.readElementText();
}
}
+ if (!scriptHash.isEmpty())
+ info.data.insert(QLatin1String("Script"), scriptHash);
if (!info.data.contains(QLatin1String("Name"))) {
setInvalidContentError(tr("PackageUpdate element without Name"));
@@ -189,49 +159,82 @@ bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
setInvalidContentError(tr("PackageUpdate element without ReleaseDate"));
return false;
}
-
+ info.data[QLatin1String("CheckSha1CheckSum")] = checkSha1CheckSum;
updateInfoList.append(info);
return true;
}
-void UpdatesInfoData::processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const
+void UpdatesInfoData::processLocalizedTag(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
{
- QString languageAttribute = childE.attribute(QLatin1String("xml:lang")).toLower();
- if (!info.contains(childE.tagName()) && (languageAttribute.isEmpty()))
- info[childE.tagName()] = childE.text();
+ const QString languageAttribute = reader.attributes().value(QLatin1String("xml:lang")).toString().toLower();
+ const QString elementName = reader.name().toString();
+ if (!info.contains(elementName) && (languageAttribute.isEmpty()))
+ info[elementName] = reader.readElementText();
+ if (languageAttribute.isEmpty())
+ return;
// overwrite default if we have a language specific description
if (QLocale().name().startsWith(languageAttribute, Qt::CaseInsensitive))
- info[childE.tagName()] = childE.text();
+ info[elementName] = reader.readElementText();
}
-QVariant UpdatesInfoData::parseOperations(const QDomNodeList &operationNodes)
+void UpdatesInfoData::parseOperations(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
{
- QVariant operationListVariant;
QList<QPair<QString, QVariant>> operationsList;
- for (int i = 0; i < operationNodes.count(); ++i) {
- const QDomNode operationNode = operationNodes.at(i);
- if (operationNode.nodeName() == QLatin1String("Operation")) {
- const QDomNodeList argumentNodes = operationNode.childNodes();
- QStringList attributes;
- 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());
- }
+ while (reader.readNext()) {
+ const QString subElementName = reader.name().toString();
+ // End of parsing operations
+ if ((subElementName == QLatin1String("Operations"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName != QLatin1String("Operation") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ QStringList attributes;
+ const QXmlStreamAttributes attr = reader.attributes();
+ while (reader.readNext()) {
+ const QString subElementName2 = reader.name().toString();
+ // End of parsing single operation
+ if ((subElementName2 == QLatin1String("Operation"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
}
- QPair<QString, QVariant> pair;
- pair.first = operationNode.toElement().attributeNode(QLatin1String("name")).value();
- pair.second = attributes;
- operationsList.append(pair);
+ if (subElementName2 != QLatin1String("Argument") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ attributes.append(reader.readElementText());
}
+ QPair<QString, QVariant> pair;
+ pair.first = attr.value(QLatin1String("name")).toString();
+ pair.second = attributes;
+ operationsList.append(pair);
}
- operationListVariant.setValue(operationsList);
- return operationListVariant;
+ info.insert(QLatin1String("Operations"), QVariant::fromValue(operationsList));
}
-
+void UpdatesInfoData::parseLicenses(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
+{
+ QHash<QString, QVariant> licenseHash;
+ while (reader.readNext()) {
+ const QString subElementName = reader.name().toString();
+ // End of parsing Licenses
+ if ((subElementName == QLatin1String("Licenses"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName != QLatin1String("License") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ const QXmlStreamAttributes attr = reader.attributes();
+ QVariantMap attributes;
+ attributes.insert(QLatin1String("file"), attr.value(QLatin1String("file")).toString());
+ if (!attr.value(QLatin1String("priority")).isNull())
+ attributes.insert(QLatin1String("priority"), attr.value(QLatin1String("priority")).toString());
+ else
+ attributes.insert(QLatin1String("priority"), QLatin1String("0"));
+ licenseHash.insert(attr.value(QLatin1String("name")).toString(), attributes);
+ }
+ if (!licenseHash.isEmpty())
+ info.insert(QLatin1String("Licenses"), licenseHash);
+}
//
// UpdatesInfo
//
@@ -264,6 +267,10 @@ void UpdatesInfo::setFileName(const QString &updateXmlFile)
d->updateInfoList.clear();
d->updateXmlFile = updateXmlFile;
+}
+
+void UpdatesInfo::parseFile()
+{
d->parseFile(d->updateXmlFile);
}
@@ -282,6 +289,11 @@ QString UpdatesInfo::applicationVersion() const
return d->applicationVersion;
}
+QString UpdatesInfo::checkSha1CheckSum() const
+{
+ return d->checkSha1CheckSum;
+}
+
int UpdatesInfo::updateInfoCount() const
{
return d->updateInfoList.count();
diff --git a/src/libs/kdtools/updatesinfo_p.h b/src/libs/kdtools/updatesinfo_p.h
index 93e2fe8c6..bd9885327 100644
--- a/src/libs/kdtools/updatesinfo_p.h
+++ b/src/libs/kdtools/updatesinfo_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -68,10 +69,13 @@ public:
QString fileName() const;
void setFileName(const QString &updateXmlFile);
+ void parseFile();
QString applicationName() const;
QString applicationVersion() const;
+ QString checkSha1CheckSum() const;
+
int updateInfoCount() const;
UpdateInfo updateInfo(int index) const;
QList<UpdateInfo> updatesInfo() const;
diff --git a/src/libs/kdtools/updatesinfodata_p.h b/src/libs/kdtools/updatesinfodata_p.h
index 07da6fcf0..9229f8577 100644
--- a/src/libs/kdtools/updatesinfodata_p.h
+++ b/src/libs/kdtools/updatesinfodata_p.h
@@ -32,8 +32,7 @@
#include <QCoreApplication>
#include <QSharedData>
-QT_FORWARD_DECLARE_CLASS(QDomElement)
-QT_FORWARD_DECLARE_CLASS(QDomNodeList)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
namespace KDUpdater {
@@ -52,16 +51,18 @@ public:
QString updateXmlFile;
QString applicationName;
QString applicationVersion;
+ QString checkSha1CheckSum;
QList<UpdateInfo> updateInfoList;
void parseFile(const QString &updateXmlFile);
- bool parsePackageUpdateElement(const QDomElement &updateE);
+ bool parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum);
void setInvalidContentError(const QString &detail);
private:
- void processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const;
- QVariant parseOperations(const QDomNodeList &operationNodes);
+ void processLocalizedTag(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
+ void parseOperations(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
+ void parseLicenses(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
};
} // namespace KDUpdater
diff --git a/src/sdk/commandlineinterface.cpp b/src/sdk/commandlineinterface.cpp
index a1eea1f8d..afc5c2f42 100644
--- a/src/sdk/commandlineinterface.cpp
+++ b/src/sdk/commandlineinterface.cpp
@@ -303,7 +303,7 @@ QHash<QString, QString> CommandLineInterface::parsePackageFilters()
const QString element = filter.left(i).trimmed();
const QString value = filter.mid(i + 1).trimmed();
- if ((i == -1) || (filter.count(QLatin1Char('=') > 1))
+ 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,...\".";
diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp
index dcbf15228..20a4f312b 100644
--- a/src/sdk/installerbase.cpp
+++ b/src/sdk/installerbase.cpp
@@ -104,8 +104,8 @@ int InstallerBase::run()
if (squishPort <= 0 || squishPort > maxSquishPortNumber) {
qWarning().noquote() << "Invalid Squish port:" << squishPort;
} else {
- Squish::allowAttaching(squishPort);
- qCDebug(QInstaller::lcDeveloperBuild) << "Attaching to squish port" << squishPort << "succeeded";
+ bool attachSucceeded = Squish::allowAttaching(squishPort);
+ qCDebug(QInstaller::lcDeveloperBuild) << "Attach to squish port" << squishPort << "succeeded: "<<attachSucceeded;
}
}
#endif
diff --git a/src/sdk/main.cpp b/src/sdk/main.cpp
index f7368883b..efaf44582 100644
--- a/src/sdk/main.cpp
+++ b/src/sdk/main.cpp
@@ -49,11 +49,12 @@
#include <QDateTime>
#include <QNetworkProxyFactory>
#include <QThread>
+#include <QThreadPool>
#include <QDeadlineTimer>
#include <iostream>
-#if defined(Q_OS_MACOS) or defined(Q_OS_UNIX)
+#if defined(Q_OS_MACOS) || defined(Q_OS_UNIX)
# include <unistd.h>
# include <sys/types.h>
#endif
@@ -111,8 +112,10 @@ int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
}
#if defined(Q_OS_WIN)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
+#endif
// increase maximum numbers of file descriptors
#if defined(Q_OS_MACOS)
QCoreApplication::setSetuidAllowed(true);
@@ -122,6 +125,13 @@ int main(int argc, char *argv[])
setrlimit(RLIMIT_NOFILE, &rl);
#endif
+ // Try to avoid running into situations where the application would hang due to "nested" blocking
+ // usage of the global threadpool that has only one thread available, i.e. main thread invokes
+ // QtConcurrent::run(&myFunction) and myFunction() calls QtConcurrent::blockingFiltered()
+ // for a container.
+ if (QThread::idealThreadCount() == 1)
+ QThreadPool::globalInstance()->setMaxThreadCount(2);
+
// We need to start either a command line application or a GUI application. Since we
// fail doing so at least on Linux while parsing the argument using a core application
// object and later starting the GUI application, we now parse the arguments first.
diff --git a/src/sdk/sdk.pro b/src/sdk/sdk.pro
index 4a47fb825..fac8f4f49 100644
--- a/src/sdk/sdk.pro
+++ b/src/sdk/sdk.pro
@@ -104,7 +104,7 @@ HEADERS += \
installerbase.h \
aboutapplicationdialog.h
-SOURCES = \
+SOURCES += \
main.cpp \
installerbase.cpp \
tabcontroller.cpp \
diff --git a/src/sdk/sdkapp.h b/src/sdk/sdkapp.h
index 7d39ee3cf..ba542691e 100644
--- a/src/sdk/sdkapp.h
+++ b/src/sdk/sdkapp.h
@@ -200,7 +200,8 @@ public:
const QStringList translations = m_core->settings().translations();
if (translations.isEmpty()) {
- foreach (const QLocale locale, QLocale().uiLanguages()) {
+ for (const QString &language : QLocale().uiLanguages()) {
+ const QLocale locale(language);
QScopedPointer<QTranslator> qtTranslator(new QTranslator(QCoreApplication::instance()));
bool qtLoaded = qtTranslator->load(locale, QLatin1String("qt"),
QLatin1String("_"), newDirectory);
@@ -597,7 +598,7 @@ public:
QString controlScript = QString();
if (m_parser.isSet(CommandLineOptions::scScriptLong)) {
controlScript = m_parser.value(CommandLineOptions::scScriptLong);
- if (!QFileInfo(controlScript).exists())
+ if (!QFileInfo::exists(controlScript))
qCDebug(QInstaller::lcInstallerInstallLog) << "Script file does not exist.";
} else if (!m_core->settings().controlScript().isEmpty()) {
diff --git a/src/sdk/settingsdialog.cpp b/src/sdk/settingsdialog.cpp
index b807deccd..acd364b2f 100644
--- a/src/sdk/settingsdialog.cpp
+++ b/src/sdk/settingsdialog.cpp
@@ -257,6 +257,13 @@ SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent)
}
m_ui->m_cachePathLineEdit->setText(settings.localCachePath());
+ showClearCacheProgress(false);
+}
+
+void SettingsDialog::showClearCacheProgress(bool show)
+{
+ m_ui->m_clearCacheProgressLabel->setVisible(show);
+ m_ui->m_clearCacheProgressBar->setVisible(show);
}
void SettingsDialog::accept()
diff --git a/src/sdk/settingsdialog.h b/src/sdk/settingsdialog.h
index bc618d6b7..5f5c017a8 100644
--- a/src/sdk/settingsdialog.h
+++ b/src/sdk/settingsdialog.h
@@ -104,6 +104,8 @@ class SettingsDialog : public QDialog
public:
explicit SettingsDialog(QInstaller::PackageManagerCore *core, QWidget *parent = 0);
+ void showClearCacheProgress(bool show);
+
public slots:
void accept();
diff --git a/src/sdk/settingsdialog.ui b/src/sdk/settingsdialog.ui
index 0254abd98..9d4ba9577 100644
--- a/src/sdk/settingsdialog.ui
+++ b/src/sdk/settingsdialog.ui
@@ -304,6 +304,42 @@
</layout>
</item>
<item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_clearCacheProgressLabel">
+ <property name="text">
+ <string>Clearing cache...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="m_clearCacheProgressBar">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximum">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>-1</number>
+ </property>
+ <property name="format">
+ <string>%p%</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp
index 6edc758a5..f82664126 100644
--- a/src/sdk/tabcontroller.cpp
+++ b/src/sdk/tabcontroller.cpp
@@ -38,6 +38,7 @@
#include <QtCore/QTimer>
#include <QtWidgets/QMessageBox>
+#include <QtConcurrent>
using namespace QInstaller;
@@ -198,10 +199,31 @@ void TabController::onAboutApplicationClicked()
void TabController::onClearCacheClicked()
{
- QDialog *settingsDialog = static_cast<QDialog *>(sender());
+ SettingsDialog *settingsDialog = static_cast<SettingsDialog *>(sender());
+ settingsDialog->setEnabled(false);
+ settingsDialog->showClearCacheProgress(true);
QString errorMessage;
- const bool success = d->m_core->clearLocalCache(&errorMessage);
+ bool success = true;
+
+ // Clearing might take some time, run in a separate thread
+ QEventLoop loop;
+ QFutureWatcher<bool> futureWatcher;
+
+ connect(&futureWatcher, &QFutureWatcher<bool>::finished, this, [&]() {
+ success = futureWatcher.future().result();
+ if (loop.isRunning())
+ loop.quit();
+ });
+
+ futureWatcher.setFuture(QtConcurrent::run(d->m_core,
+ &PackageManagerCore::clearLocalCache, &errorMessage));
+
+ if (!futureWatcher.isFinished())
+ loop.exec();
+
+ settingsDialog->setEnabled(true);
+ settingsDialog->showClearCacheProgress(false);
QMessageBox msgBox(settingsDialog);
msgBox.setWindowModality(Qt::WindowModal);
diff --git a/src/sdk/translations/ifw_ar.ts b/src/sdk/translations/ifw_ar.ts
index 94cde4954..0d9f700e4 100644
--- a/src/sdk/translations/ifw_ar.ts
+++ b/src/sdk/translations/ifw_ar.ts
@@ -548,52 +548,32 @@
<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>
+ <source>Default</source>
+ <translation>الافتراضي</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>حدد المكونات الافتراضية في العرض الشجري.</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>
+ <source>Reset</source>
+ <translation>أعد الضبط</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>إعادة تعيين جميع المكونات إلى حالة التحديد الأصلية في طريقة العرض الشجري.</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>
+ <source>Select All</source>
+ <translation>حدّد الكل</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>قم بتحديد جميع المكونات في عرض الشجري.</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>
+ <source>Deselect All</source>
+ <translation>ألغِ تحديد الكل</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -659,8 +639,8 @@
<translation>خطأ</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>معلومات المكون</translation>
+ <source>Information</source>
+ <translation type="unfinished">معلومات المكون</translation>
</message>
</context>
<context>
@@ -2367,8 +2347,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>لا يمكن فتح ملف الإعدادات %1 للقراءة: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>حدد الاقسام</translation>
+ <source>Categories</source>
+ <translation type="unfinished">حدد الاقسام</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_ca.ts b/src/sdk/translations/ifw_ca.ts
index 20853e60f..acf1b9b32 100644
--- a/src/sdk/translations/ifw_ca.ts
+++ b/src/sdk/translations/ifw_ca.ts
@@ -830,32 +830,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>Predetermin&amp;at</translation>
+ <source>Default</source>
+ <translation>Predeterminat</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Restableix</translation>
+ <source>Reset</source>
+ <translation>Restableix</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Selecciona-ho tot</translation>
+ <source>Select All</source>
+ <translation>Selecciona-ho tot</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Desselecciona-ho tot</translation>
+ <source>Deselect All</source>
+ <translation>Desselecciona-ho tot</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -941,7 +941,7 @@
<translation>Error</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2613,8 +2613,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>No s&apos;ha pogut obrir el fitxer d&apos;ajustaments %1 per a lectura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Selecciona les categories dels paquets</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Selecciona les categories dels paquets</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_da.ts b/src/sdk/translations/ifw_da.ts
index 6b2f6ce1a..1b7ad5c1b 100644
--- a/src/sdk/translations/ifw_da.ts
+++ b/src/sdk/translations/ifw_da.ts
@@ -830,32 +830,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;Standard</translation>
+ <source>Default</source>
+ <translation>Standard</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Nulstil</translation>
+ <source>Reset</source>
+ <translation>Nulstil</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Vælg alle</translation>
+ <source>Select All</source>
+ <translation>Vælg alle</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Fravælg alle</translation>
+ <source>Deselect All</source>
+ <translation>Fravælg alle</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2574,7 +2574,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Kan ikke åbne indstillingsfilen %1 til læsning: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2793,7 +2793,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Fejl</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/src/sdk/translations/ifw_de.ts b/src/sdk/translations/ifw_de.ts
index 2ef0f358e..9e17ffbac 100644
--- a/src/sdk/translations/ifw_de.ts
+++ b/src/sdk/translations/ifw_de.ts
@@ -830,52 +830,32 @@
<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>St&amp;andard</translation>
+ <source>Default</source>
+ <translation>Standard</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Wählt die Standardkomponenten in der Baumansicht aus.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+Z</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;Zurücksetzen</translation>
+ <source>Reset</source>
+ <translation>Zurücksetzen</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Setzt alle Komponenten in der Baumansicht auf die ursprüngliche Auswahl zurück.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>Alle au&amp;swählen</translation>
+ <source>Select All</source>
+ <translation>Alle auswählen</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Wählt alle Komponenten in der Baumansicht aus.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+B</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>Alle a&amp;bwählen</translation>
+ <source>Deselect All</source>
+ <translation>Alle abwählen</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -941,8 +921,8 @@
<translation>Fehler</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Komponenteninformationen</translation>
+ <source>Information</source>
+ <translation type="unfinished">Komponenteninformationen</translation>
</message>
</context>
<context>
@@ -2622,8 +2602,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Konnte Einstellungsdatei %1 nicht zum Lesen öffnen: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Kategorien auswählen</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Kategorien auswählen</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_es.ts b/src/sdk/translations/ifw_es.ts
index f85347718..721fed1df 100644
--- a/src/sdk/translations/ifw_es.ts
+++ b/src/sdk/translations/ifw_es.ts
@@ -830,52 +830,32 @@
<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>Predetermin&amp;ado</translation>
+ <source>Default</source>
+ <translation>Predeterminado</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Selecciona los componentes predeterminados en la vista de árbol.</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;Restablecer</translation>
+ <source>Reset</source>
+ <translation>Restablecer</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Restablece todos los componentes a su estado de selección original en la vista de árbol.</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;Seleccionar todo</translation>
+ <source>Select All</source>
+ <translation>Seleccionar todo</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Selecciona todos los componentes en la vista de árbol.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>Anular selección de to&amp;do</translation>
+ <source>Deselect All</source>
+ <translation>Anular selección de todo</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2578,8 +2558,8 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<translation>No se puede abrir el archivo de configuración %1 para la lectura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Seleccione las categorías</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Seleccione las categorías</translation>
</message>
</context>
<context>
@@ -2797,8 +2777,8 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<translation>Error</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Información de componentes</translation>
+ <source>Information</source>
+ <translation type="unfinished">Información de componentes</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_fr.ts b/src/sdk/translations/ifw_fr.ts
index 30093be81..683540c79 100644
--- a/src/sdk/translations/ifw_fr.ts
+++ b/src/sdk/translations/ifw_fr.ts
@@ -830,52 +830,32 @@
<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>Par déf&amp;aut</translation>
+ <source>Default</source>
+ <translation>Par défaut</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Sélectionner les composants par défaut dans l&apos;arborescence.</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;Réinitialiser</translation>
+ <source>Reset</source>
+ <translation>Réinitialiser</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Réinitialiser tous les composants à leur état de sélection d&apos;origine dans l&apos;arborescence.</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;Sélectionner tout</translation>
+ <source>Select All</source>
+ <translation>Sélectionner tout</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Sélectionner tous les composants dans l&apos;arborescence.</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;Tout désélectionner</translation>
+ <source>Deselect All</source>
+ <translation>Tout désélectionner</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2580,8 +2560,8 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<translation>Impossible d’ouvrir le fichier de paramètres %1 en lecture : %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Sélectionner les catégories</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Sélectionner les catégories</translation>
</message>
</context>
<context>
@@ -2799,8 +2779,8 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<translation>Erreur</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informations sur les composants</translation>
+ <source>Information</source>
+ <translation type="unfinished">Informations sur les composants</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_hr.ts b/src/sdk/translations/ifw_hr.ts
index 2f00004a5..d2a88f62d 100644
--- a/src/sdk/translations/ifw_hr.ts
+++ b/src/sdk/translations/ifw_hr.ts
@@ -1946,32 +1946,32 @@ Kopiraj program za instaliranje na računalo</translation>
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>St&amp;andardne</translation>
+ <source>Default</source>
+ <translation>Standardne</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Resetiraj</translation>
+ <source>Reset</source>
+ <translation>Resetiraj</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>Odaberi &amp;sve</translation>
+ <source>Select All</source>
+ <translation>Odaberi sve</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>O&amp;dznači sav odabir</translation>
+ <source>Deselect All</source>
+ <translation>dznači sav odabir</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2318,7 +2318,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Nije moguće otvoriti datoteke postavaka %1 za učitavanje: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2832,7 +2832,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Greška</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/src/sdk/translations/ifw_hu.ts b/src/sdk/translations/ifw_hu.ts
index 7a181fb19..a66e0725c 100644
--- a/src/sdk/translations/ifw_hu.ts
+++ b/src/sdk/translations/ifw_hu.ts
@@ -825,38 +825,23 @@
<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;Alapértelmezett</translation>
+ <source>Default</source>
+ <translation>Alapértelmezett</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Alapértelmezett komponensek kiválasztása a fa nézetben.</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;Visszaállítás</translation>
+ <source>Reset</source>
+ <translation>Visszaállítás</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Összes komponens visszaállítása az eredeti kiválasztási állapotba a fa nézetben. </translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
+ <source>Select All</source>
<translation>Mindent kijelöl</translation>
</message>
<message>
@@ -864,12 +849,7 @@
<translation>Minden komponens kijelölése a fa nézetben.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
+ <source>Deselect All</source>
<translation>Összes kijelölés megszüntetése</translation>
</message>
<message>
@@ -932,8 +912,8 @@
<translation>Szűrő</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Komponens Információ</translation>
+ <source>Information</source>
+ <translation type="unfinished">Komponens Információ</translation>
</message>
<message>
<source>Error</source>
@@ -2622,8 +2602,8 @@ a megfelelő jogokkal rendelkező felhasználóként, majd kattintson az OK gomb
<translation>Nem sikerült megnyitni &quot;%1&quot; beállítás fájlt olvasásra: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Válasszon Kategóriákat</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Válasszon Kategóriákat</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_it.ts b/src/sdk/translations/ifw_it.ts
index 0221b41bc..2b9c749f1 100644
--- a/src/sdk/translations/ifw_it.ts
+++ b/src/sdk/translations/ifw_it.ts
@@ -830,32 +830,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;Predefinito</translation>
+ <source>Default</source>
+ <translation>Predefinito</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Reimposta</translation>
+ <source>Reset</source>
+ <translation>Reimposta</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Seleziona tutto</translation>
+ <source>Select All</source>
+ <translation>Seleziona tutto</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Deseleziona tutto</translation>
+ <source>Deselect All</source>
+ <translation>Deseleziona tutto</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2574,7 +2574,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Impossibile aprire il file di impostazioni %1 per la lettura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2793,7 +2793,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Errore</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/src/sdk/translations/ifw_ja.ts b/src/sdk/translations/ifw_ja.ts
index 65dc1198d..341c9680e 100644
--- a/src/sdk/translations/ifw_ja.ts
+++ b/src/sdk/translations/ifw_ja.ts
@@ -825,52 +825,32 @@
<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;A)</translation>
+ <source>Default</source>
+ <translation>デフォルト</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>ツリー表示でデフォルトのコンポーネントを選択します。</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;R)</translation>
+ <source>Reset</source>
+ <translation>リセット</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>ツリー表示ですべてのコンポーネントを元の選択状態にリセットします。</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;S)</translation>
+ <source>Select All</source>
+ <translation>すべて選択</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>ツリー表示ですべてのコンポーネントを選択します。</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;D)</translation>
+ <source>Deselect All</source>
+ <translation>すべて選択解除</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2564,8 +2544,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>読み取り用の設定ファイル %1 を開けません: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>カテゴリを選択</translation>
+ <source>Categories</source>
+ <translation type="unfinished">カテゴリを選択</translation>
</message>
</context>
<context>
@@ -2781,8 +2761,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>エラー</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>コンポーネント情報</translation>
+ <source>Information</source>
+ <translation type="unfinished">コンポーネント情報</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_ko.ts b/src/sdk/translations/ifw_ko.ts
index 3db69c42b..be96480d9 100644
--- a/src/sdk/translations/ifw_ko.ts
+++ b/src/sdk/translations/ifw_ko.ts
@@ -548,52 +548,32 @@
<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;A):</translation>
+ <source>Default</source>
+ <translation>기본:</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>트리 보기에서 기본 구성요소를 선택합니다.</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;R)</translation>
+ <source>Reset</source>
+ <translation>재설정</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>트리 보기에서 모든 구성요소를 원래 선택된 상태로 재설정합니다.</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;S)</translation>
+ <source>Select All</source>
+ <translation>모두 선택</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>트리 보기에서 모든 구성요소를 선택합니다.</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;D)</translation>
+ <source>Deselect All</source>
+ <translation>모두 선택 해제</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -655,8 +635,8 @@
<translation>필터</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>구성요소 정보</translation>
+ <source>Information</source>
+ <translation type="unfinished">구성요소 정보</translation>
</message>
<message>
<source>Error</source>
@@ -2322,8 +2302,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>설정 파일 &quot;%1&quot;을(를) 읽기 위해 열 수 없음: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>범주 선택</translation>
+ <source>Categories</source>
+ <translation type="unfinished">범주 선택</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_pl.ts b/src/sdk/translations/ifw_pl.ts
index 427878716..2b7a721fe 100644
--- a/src/sdk/translations/ifw_pl.ts
+++ b/src/sdk/translations/ifw_pl.ts
@@ -835,32 +835,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;Domyślne</translation>
+ <source>Default</source>
+ <translation>Domyślne</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Zaznacz domyślne komponenty.</translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Resetuj</translation>
+ <source>Reset</source>
+ <translation>Resetuj</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Zresetuj wszystkie komponenty do ich pierwotnego stanu zaznaczenia.</translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Zaznacz wszystkie</translation>
+ <source>Select All</source>
+ <translation>Zaznacz wszystkie</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Zaznacz wszystkie komponenty.</translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Usuń zaznaczenie wszystkich</translation>
+ <source>Deselect All</source>
+ <translation>Usuń zaznaczenie wszystkich</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2592,8 +2592,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Nie można otworzyć pliku ustawień %1 do odczytu: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Wybierz kategorie</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Wybierz kategorie</translation>
</message>
</context>
<context>
@@ -2813,8 +2813,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Błąd</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informacje o składnikach</translation>
+ <source>Information</source>
+ <translation type="unfinished">Informacje o składnikach</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_pt.ts b/src/sdk/translations/ifw_pt.ts
index 8c01d5075..060f3ed30 100644
--- a/src/sdk/translations/ifw_pt.ts
+++ b/src/sdk/translations/ifw_pt.ts
@@ -1913,52 +1913,32 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<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>Def&amp;eito</translation>
+ <source>Default</source>
+ <translation>Defeito</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Selecione os componentes por defeito na vista de árvore.</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;Reverter</translation>
+ <source>Reset</source>
+ <translation>Reverter</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Reverter todos os componentes para o seu estado original na visualização em árvore.</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;Selecionar Todos</translation>
+ <source>Select All</source>
+ <translation>Selecionar Todos</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Selecione todos os componentes na visualização em árvore.</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;Desmarcar Todos</translation>
+ <source>Deselect All</source>
+ <translation>Desmarcar Todos</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2288,8 +2268,8 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<translation>Não é possível abrir o ficheiro de configurações %1 para leitura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Selecionar categorias</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Selecionar categorias</translation>
</message>
</context>
<context>
@@ -2797,8 +2777,8 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<translation>Erro</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informação do componente</translation>
+ <source>Information</source>
+ <translation type="unfinished">Informação do componente</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_pt_BR.ts b/src/sdk/translations/ifw_pt_BR.ts
index 6d7b2f15e..7f500067f 100644
--- a/src/sdk/translations/ifw_pt_BR.ts
+++ b/src/sdk/translations/ifw_pt_BR.ts
@@ -1912,32 +1912,32 @@ Por favor, copie o instalador para uma unidade local</translation>
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>Def&amp;ault</translation>
+ <source>Default</source>
+ <translation>Default</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Resetar</translation>
+ <source>Reset</source>
+ <translation>Resetar</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Selecionar Todos</translation>
+ <source>Select All</source>
+ <translation>Selecionar Todos</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Desmarcar Todos</translation>
+ <source>Deselect All</source>
+ <translation>Desmarcar Todos</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2287,8 +2287,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Não é possível abrir o arquivo de configurações %1 para leitura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Selecionar Categorias</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Selecionar Categorias</translation>
</message>
</context>
<context>
@@ -2796,8 +2796,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Erro</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informação Componente.</translation>
+ <source>Information</source>
+ <translation type="unfinished">Informação Componente.</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_ru.ts b/src/sdk/translations/ifw_ru.ts
index b47cf9661..75cdc532b 100644
--- a/src/sdk/translations/ifw_ru.ts
+++ b/src/sdk/translations/ifw_ru.ts
@@ -835,32 +835,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;По умолчанию</translation>
+ <source>Default</source>
+ <translation>По умолчанию</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Выберите компоненты по умолчанию в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Отменить</translation>
+ <source>Reset</source>
+ <translation>Отменить</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Сбросьте все компоненты в исходное состояние выбора в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Выбрать всё</translation>
+ <source>Select All</source>
+ <translation>Выбрать всё</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Выберите все компоненты в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Отменить выделенное</translation>
+ <source>Deselect All</source>
+ <translation>Отменить выделенное</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -946,8 +946,8 @@
<translation>Ошибка</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Сведения о компонентах</translation>
+ <source>Information</source>
+ <translation type="unfinished">Сведения о компонентах</translation>
</message>
</context>
<context>
@@ -2629,8 +2629,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Невозможно открыть файл настроек %1 на чтение: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Выберите категории пакетов</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Выберите категории пакетов</translation>
</message>
</context>
<context>
diff --git a/src/sdk/translations/ifw_zh_CN.ts b/src/sdk/translations/ifw_zh_CN.ts
index 7b715f2e3..337f99bda 100644
--- a/src/sdk/translations/ifw_zh_CN.ts
+++ b/src/sdk/translations/ifw_zh_CN.ts
@@ -825,52 +825,32 @@
<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;A)</translation>
+ <source>Default</source>
+ <translation>默认</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>在树视图中选择默认组件。</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;R)</translation>
+ <source>Reset</source>
+ <translation>重置</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>在树视图中将所有组件重置为其原始选择状态。</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;S)</translation>
+ <source>Select All</source>
+ <translation>全选</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>在树视图中选择所有组件。</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;D)</translation>
+ <source>Deselect All</source>
+ <translation>取消全选</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -2569,8 +2549,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>无法打开设置文件 %1 进行读取:%2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>选择类别</translation>
+ <source>Categories</source>
+ <translation type="unfinished">选择类别</translation>
</message>
</context>
<context>
@@ -2786,8 +2766,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>错误</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>组件信息</translation>
+ <source>Information</source>
+ <translation type="unfinished">组件信息</translation>
</message>
</context>
<context>
diff --git a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
index afa11d707..5978b3bb2 100644
--- a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
+++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
@@ -125,7 +125,7 @@ private slots:
const QString debugMessage = QString("Exception while loading the component script");
const QRegularExpression re(debugMessage);
QTest::ignoreMessage(QtWarningMsg, re);
- invalidScriptComponent->loadComponentScript(":///data/broken_script.qs");
+ invalidScriptComponent->evaluateComponentScript(":///data/broken_script.qs");
model->reset(components);
testModelState(model, m_checkedComponentsWithBrokenScript, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithBrokenScript);
@@ -157,6 +157,7 @@ private:
{
UpdatesInfo updatesInfo;
updatesInfo.setFileName(":///data/updates.xml");
+ updatesInfo.parseFile();
const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo();
QList <Component*> components;
diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml
index 81831ed5b..3b5e22cc0 100644
--- a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml
@@ -170,4 +170,14 @@
<DownloadableArchives>content.7z</DownloadableArchives>
<Virtual>true</Virtual>
</PackageUpdate>
+ <PackageUpdate>
+ <Name>componentJ</Name>
+ <DisplayName>component J</DisplayName>
+ <Description>This component has post install script.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>50</SortingPriority>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <Script postLoad="True">postLoadscript.js</Script>
+ </PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z
new file mode 100644
index 000000000..58ff52baa
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z
new file mode 100644
index 000000000..be99410a8
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineinstall/settings.qrc b/tests/auto/installer/commandlineinstall/settings.qrc
index 824517e1e..992dbfd58 100644
--- a/tests/auto/installer/commandlineinstall/settings.qrc
+++ b/tests/auto/installer/commandlineinstall/settings.qrc
@@ -14,6 +14,8 @@
<file>data/installPackagesRepository/componentG/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentH/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentI/1.0.0content.7z</file>
+ <file>data/installPackagesRepository/componentJ/1.0.0content.7z</file>
+ <file>data/installPackagesRepository/componentJ/1.0.0meta.7z</file>
<file>data/installPackagesRepository/componentG/1.0.0meta.7z</file>
<file>data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z</file>
diff --git a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
index 1e269b7ff..a986234c8 100644
--- a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
+++ b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
@@ -47,19 +47,32 @@ class tst_CommandLineInstall : public QObject
{
Q_OBJECT
+public:
+ void ignoreAvailablePackagesMissingMessages()
+ {
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Searching packages with regular expression:"));
+ QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
+ QTest::ignoreMessage(QtDebugMsg, "Loading component scripts...");
+ QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
+ }
+
+ void ignoreInstallPackageFailsMessages(const QRegularExpression &regExp)
+ {
+ QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
+ QTest::ignoreMessage(QtDebugMsg, "Loading component scripts...");
+ QTest::ignoreMessage(QtDebugMsg, regExp);
+ }
+
private slots:
void testListAvailablePackages()
{
QString loggingRules = (QLatin1String("ifw.* = false\n"));
- QTest::ignoreMessage(QtDebugMsg, "Operations sanity check succeeded.");
-
+ QLoggingCategory::setFilterRules(loggingRules);
QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
(m_installDir, ":///data/repository"));
- QLoggingCategory::setFilterRules(loggingRules);
auto func = &PackageManagerCore::listAvailablePackages;
-
verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
"<availablepackages>\n"
" <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
@@ -113,13 +126,11 @@ private slots:
" <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
"</availablepackages>\n"), func, QString(), searchHash);
- // Need to change rules here to catch messages
QLoggingCategory::setFilterRules("ifw.* = true\n");
-
- QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
+ ignoreAvailablePackagesMissingMessages();
core->listAvailablePackages(QLatin1String("C.virt"));
- QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
+ ignoreAvailablePackagesMissingMessages();
core->listAvailablePackages(QLatin1String("C.virt.subcomponent"));
}
@@ -128,37 +139,33 @@ private slots:
QString loggingRules = (QLatin1String("ifw.* = false\n"
"ifw.installer.installlog = true\n"));
+ QLoggingCategory::setFilterRules(loggingRules);
+ QTest::ignoreMessage(QtDebugMsg, "Operations sanity check succeeded.");
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Using metadata cache from "));
QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
(m_installDir, ":///data/uninstallableComponentsRepository"));
- QLoggingCategory::setFilterRules(loggingRules);
-
- QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install component A. Component "
- "is installed only as automatic dependency to autoDep.\n"));
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install component A. Component "
+ "is installed only as automatic dependency to autoDep.\n"));
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("A")));
- QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install component AB. Component "
- "is not checkable, meaning you have to select one of the subcomponents.\n"));
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install component AB. Component "
+ "is not checkable, meaning you have to select one of the subcomponents.\n"));
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("AB")));
- QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install B. Component is virtual.\n"));
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install B. Component is virtual.\n"));
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("B")));
- QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install B.subcomponent. Component "
- "is a descendant of a virtual component B.\n"));
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install B.subcomponent. Component "
+ "is a descendant of a virtual component B.\n"));
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("B.subcomponent")));
- QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install MissingComponent. "
- "Component not found.\n"));
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install MissingComponent. "
+ "Component not found.\n"));
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("MissingComponent")));
QCOMPARE(PackageManagerCore::Canceled, core->status());
@@ -722,6 +729,21 @@ private slots:
QVERIFY(file.remove());
}
+ void testPostScript()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepository"));
+ QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList() << QLatin1String("componentJ")));
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentJ", "1.0.0content.txt"); //Selected
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default
+
+ //componentJ is extracted to "extractToAnotherPath" -folder in post install script
+ bool fileExists = QFileInfo::exists(m_installDir + QDir::separator() + "extractToAnotherPath" + QDir::separator() + "installcontentJ.txt");
+ QVERIFY2(fileExists, QString("File \"%1\" does not exist.").arg("installcontentJ.txt").toLatin1());
+ }
+
void init()
{
m_installDir = QInstaller::generateTemporaryFileName();
diff --git a/tests/auto/installer/componentmodel/data/updates.xml b/tests/auto/installer/componentmodel/data/updates.xml
index 3a6139446..cc2da0f8e 100644
--- a/tests/auto/installer/componentmodel/data/updates.xml
+++ b/tests/auto/installer/componentmodel/data/updates.xml
@@ -8,6 +8,8 @@
<DisplayName xml:lang="ru_RU">Корневая компонента</DisplayName>
<DisplayName xml:lang="de_DE">Wurzel Komponente</DisplayName>
<Description>Install this example.</Description>
+ <Description xml:lang="ru_RU">Установите этот пример.</Description>
+ <Description xml:lang="de_DE">Installieren Sie dieses Beispiel.</Description>
<Version>0.1.0-1</Version>
<ReleaseDate>2010-09-21</ReleaseDate>
<Default>true</Default>
diff --git a/tests/auto/installer/componentmodel/tst_componentmodel.cpp b/tests/auto/installer/componentmodel/tst_componentmodel.cpp
index 463485d96..6bf78d88d 100644
--- a/tests/auto/installer/componentmodel/tst_componentmodel.cpp
+++ b/tests/auto/installer/componentmodel/tst_componentmodel.cpp
@@ -59,6 +59,12 @@ static const QMap<QString, QString> rootComponentDisplayNames = {
{"de_de", QString::fromUtf8("Wurzel Komponente")}
};
+static const QMap<QString, QString> rootComponentDescriptions = {
+ {"", QLatin1String("Install this example.")},
+ {"ru_ru", QString::fromUtf8("Установите этот пример.")},
+ {"de_de", QString::fromUtf8("Installieren Sie dieses Beispiel.")}
+};
+
class tst_ComponentModel : public QObject
{
Q_OBJECT
@@ -372,6 +378,10 @@ private slots:
? rootComponentDisplayNames[localeToTest.toLower()]
: rootComponentDisplayNames[QString()];
+ QString expectedDescription = rootComponentDescriptions.contains(localeToTest.toLower())
+ ? rootComponentDescriptions[localeToTest.toLower()]
+ : rootComponentDescriptions[QString()];
+
setPackageManagerOptions(NoFlags);
QList<Component*> rootComponents = loadComponents();
@@ -384,6 +394,9 @@ private slots:
const QModelIndex root = model.indexFromComponentName(vendorProduct);
QCOMPARE(model.data(root, Qt::DisplayRole).toString(), expectedName);
+ Component *comp = model.componentFromIndex(root);
+ QCOMPARE(comp->value("Description"), expectedDescription);
+
qDeleteAll(rootComponents);
}
}
@@ -513,6 +526,7 @@ private:
{
UpdatesInfo updatesInfo;
updatesInfo.setFileName(":///data/updates.xml");
+ updatesInfo.parseFile();
const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo();
QHash<QString, Component*> components;
@@ -529,6 +543,7 @@ private:
component->setValue("Virtual", info.data.value("Virtual").toString());
component->setValue("DisplayName", info.data.value("DisplayName").toString());
component->setValue("Checkable", info.data.value("Checkable").toString());
+ component->setValue("Description", info.data.value("Description").toString());
QString forced = info.data.value("ForcedInstallation", scFalse).toString().toLower();
if (m_core.noForceInstallation())
diff --git a/tests/auto/installer/contentsha1check/contentsha1check.pro b/tests/auto/installer/contentsha1check/contentsha1check.pro
new file mode 100644
index 000000000..dd659bffa
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/contentsha1check.pro
@@ -0,0 +1,9 @@
+include(../../qttest.pri)
+
+QT += qml
+
+SOURCES += tst_contentsha1check.cpp
+
+RESOURCES += \
+ settings.qrc \
+ ../shared/config.qrc
diff --git a/tests/auto/installer/contentsha1check/data/config.xml b/tests/auto/installer/contentsha1check/data/config.xml
new file mode 100644
index 000000000..041ce5062
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/config.xml
@@ -0,0 +1,8 @@
+<?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/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z
new file mode 100644
index 000000000..9109d284f
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1
new file mode 100644
index 000000000..564e8290b
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1
@@ -0,0 +1 @@
+eb5a464ab1a33bd1484e9b8f22b2c5f97abdfdf6 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z
new file mode 100644
index 000000000..947979354
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1
new file mode 100644
index 000000000..0f2144f0c
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1
@@ -0,0 +1 @@
+7e592e4b96adcefc77f2613100a3bd5e8835cce0 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7z
new file mode 100644
index 000000000..c14f55e4a
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml
new file mode 100644
index 000000000..1d2fb780b
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml
@@ -0,0 +1,27 @@
+<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.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="74" CompressedSize="215" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>dec2797a059da9303fec87cc0c1dfb0866afeb8f</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="74" CompressedSize="215" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>2370e0b7dae861088c056d2de40c7ab7051bda13</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z
new file mode 100644
index 000000000..793ce161c
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1
new file mode 100644
index 000000000..641396e0f
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1
@@ -0,0 +1 @@
+2c185d45cb84cec7a71e317f8cfc64dd23094c32 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7z
new file mode 100644
index 000000000..4feab5c34
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z
new file mode 100644
index 000000000..f53a705dd
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1
new file mode 100644
index 000000000..daf89acba
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1
@@ -0,0 +1 @@
+d33a5fb638047372e9793b48d6c5ff85da560595 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7z
new file mode 100644
index 000000000..9931c0a7f
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml
new file mode 100644
index 000000000..bda013684
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml
@@ -0,0 +1,27 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>E</Name>
+ <DisplayName>E</DisplayName>
+ <Description>Example component E, invalid checksum</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile CompressedSize="215" OS="Any" UncompressedSize="74"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>db7e010425aaaaaaeebc6281a9d4c91e5666fd8f</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>F</Name>
+ <DisplayName>F</DisplayName>
+ <Description>Example component F</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile CompressedSize="215" OS="Any" UncompressedSize="74"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>b69b864cef5d0aecb496273374dd24bb8cba83bd</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7z
new file mode 100644
index 000000000..f96bfa9a3
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7z
new file mode 100644
index 000000000..976c57b43
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7z
new file mode 100644
index 000000000..015670af0
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7z
new file mode 100644
index 000000000..0b36609d7
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml
new file mode 100644
index 000000000..ae2ba911a
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml
@@ -0,0 +1,27 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile OS="Any" UncompressedSize="74" CompressedSize="215"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>f82e1d1bbfd252715ace26db8c62595252e28a3b</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>D</Name>
+ <DisplayName>D</DisplayName>
+ <Description>Example component D</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile OS="Any" UncompressedSize="74" CompressedSize="215"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>402f299ec90f215db390b150c9429101344cf1ea</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentsha1check/settings.qrc b/tests/auto/installer/contentsha1check/settings.qrc
new file mode 100644
index 000000000..e150ea61e
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/settings.qrc
@@ -0,0 +1,18 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/config.xml</file>
+ <file>data/repositorywithchecksumcheck/Updates.xml</file>
+ <file>data/repositorywithchecksumcheck/A/1.0.2-1content.7z</file>
+ <file>data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1</file>
+ <file>data/repositorywithchecksumcheck/B/1.0.0-1content.7z</file>
+ <file>data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1</file>
+ <file>data/repositorywithnochecksumcheck/Updates.xml</file>
+ <file>data/repositorywithnochecksumcheck/C/1.0.2-1content.7z</file>
+ <file>data/repositorywithnochecksumcheck/D/1.0.0-1content.7z</file>
+ <file>data/repositorywithinvalidchecksum/Updates.xml</file>
+ <file>data/repositorywithinvalidchecksum/E/1.0.2-1content.7z</file>
+ <file>data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1</file>
+ <file>data/repositorywithinvalidchecksum/F/1.0.0-1content.7z</file>
+ <file>data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/contentsha1check/tst_contentsha1check.cpp b/tests/auto/installer/contentsha1check/tst_contentsha1check.cpp
new file mode 100644
index 000000000..e587d3011
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/tst_contentsha1check.cpp
@@ -0,0 +1,181 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 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 <component.h>
+#include <packagemanagercore.h>
+
+#include <QLoggingCategory>
+#include <QTest>
+#include <QMessageBox>
+
+using namespace QInstaller;
+
+typedef QList<QPair<QString, QString> > ComponentResourceHash;
+typedef QPair<QString, QString> ComponentResource;
+
+static QStringList expectedMessages;
+
+void downloadingArchiveOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ Q_UNUSED(type)
+ Q_UNUSED(context)
+ QByteArray localMsg = msg.toLocal8Bit();
+ if (!msg.startsWith("Downloading archive"))
+ return;
+ if (expectedMessages.contains(msg))
+ expectedMessages.removeOne(msg);
+}
+
+class tst_ContentSha1Check : public QObject
+{
+ Q_OBJECT
+
+private slots:
+
+ void testInstall_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+ QTest::addColumn<QStringList >("expectedDownloadingArchiveMessages");
+
+ /*********** Install with checksum check **********/
+ ComponentResourceHash componentResources;
+ componentResources.append(ComponentResource("A", "1.0.2-1content.txt"));
+ componentResources.append(ComponentResource("B", "1.0.0-1content.txt"));
+
+ QTest::newRow("Check checksum")
+ << ":///data/repositorywithchecksumcheck"
+ << (QStringList() << "A" << "B")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "A.txt" << "B.txt")
+ << (QStringList() << "Downloading archive \"1.0.2-1content.7z.sha1\" for component A."
+ << "Downloading archive \"1.0.2-1content.7z\" for component A."
+ << "Downloading archive \"1.0.0-1content.7z.sha1\" for component B."
+ << "Downloading archive \"1.0.0-1content.7z\" for component B.");
+
+ /*********** Install with and without checksum check **********/
+ componentResources.clear();
+ componentResources.append(ComponentResource("C", "1.0.2-1content.txt"));
+ componentResources.append(ComponentResource("D", "1.0.0-1content.txt"));
+
+ QTest::newRow("Without checksum check")
+ << ":///data/repositorywithnochecksumcheck"
+ << (QStringList() << "C" << "D")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "C.txt" << "D.txt")
+ << (QStringList() << "Downloading archive \"1.0.2-1content.7z\" for component C."
+ << "Downloading archive \"1.0.0-1content.7z\" for component D.");
+
+ }
+
+ void testInstallWithInvalidChecksum_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+
+ /*********** Install with checksum check **********/
+ ComponentResourceHash componentResources;
+
+ QTest::newRow("Invalid checksum")
+ << ":///data/repositorywithinvalidchecksum"
+ << (QStringList() << "E" << "F")
+ << PackageManagerCore::Failure
+ << componentResources
+ << (QStringList());
+ }
+
+ void testInstall()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+ QFETCH(QStringList, expectedDownloadingArchiveMessages);
+
+ expectedMessages = expectedDownloadingArchiveMessages;
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+ qInstallMessageHandler(downloadingArchiveOutput);
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
+ for (const ComponentResource &resource : componentResources)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFiles);
+
+ QVERIFY(expectedMessages.isEmpty());
+ }
+
+ void testInstallWithInvalidChecksum()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+ core->setMessageBoxAutomaticAnswer("DownloadError", QMessageBox::Cancel);
+ core->setMessageBoxAutomaticAnswer("installationError", QMessageBox::Ok);
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
+ QVERIFY(!QDir().exists(m_installDir));
+ }
+
+ void init()
+ {
+ m_installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(m_installDir));
+ }
+
+ void cleanup()
+ {
+ QDir dir(m_installDir);
+ QVERIFY(dir.removeRecursively());
+ }
+
+private:
+ QString m_installDir;
+ QStringList m_expectedMessages;
+};
+
+
+QTEST_MAIN(tst_ContentSha1Check)
+
+#include "tst_contentsha1check.moc"
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml
new file mode 100644
index 000000000..fe5dc4dec
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml
@@ -0,0 +1,28 @@
+<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>componentEssential</Name>
+ <DisplayName>Component Essential</DisplayName>
+ <Description>Component Essential</Description>
+ <Essential>true</Essential>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile UncompressedSize="101" CompressedSize="283" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7z
new file mode 100644
index 000000000..bdbabc7fd
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z
new file mode 100644
index 000000000..9c86042c0
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml
new file mode 100644
index 000000000..f76c03faa
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml
@@ -0,0 +1,27 @@
+<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>componentEssential</Name>
+ <DisplayName>Component Essential</DisplayName>
+ <Description>Component Essential</Description>
+ <Essential>true</Essential>
+ <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/repositoryWithEssential/componentA/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7z
new file mode 100644
index 000000000..46a9f1d1e
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7z
new file mode 100644
index 000000000..435f260ee
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/settings.qrc b/tests/auto/installer/contentshaupdate/settings.qrc
index a5d045f46..e3426be92 100644
--- a/tests/auto/installer/contentshaupdate/settings.qrc
+++ b/tests/auto/installer/contentshaupdate/settings.qrc
@@ -5,10 +5,16 @@
<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/repositoryWithEssential/Updates.xml</file>
+ <file>data/repositoryWithEssential/componentA/1.0.0content.7z</file>
+ <file>data/repositoryWithEssential/componentEssential/2.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>
+ <file>data/repositoryUpdateWithEssential/Updates.xml</file>
+ <file>data/repositoryUpdateWithEssential/componentA/0.1.0content.7z</file>
+ <file>data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp b/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
index d8d2f5377..5d282fe38 100644
--- a/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
+++ b/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
@@ -58,34 +58,50 @@ private slots:
void updateWithContentSha1_data()
{
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QString>("repositoryForUpdate");
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;
+ QTest::newRow("Sha1UpdateForEssential")
+ << ":///data/repositoryWithEssential" << ":///data/repositoryUpdateWithEssential"
+ << "componentEssential" << "2.0.0content.txt" << "1.0.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::EssentialUpdated;
+ QTest::newRow("ContentSha1Change")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentA" << "1.0.0content.txt" << "0.1.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("NewContentSha1")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentB" << "1.0.0content.txt" << "0.1.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("SameContentSha1")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentC" << "1.0.0content.txt" << "1.0.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Canceled;
+ QTest::newRow("Sha1RemovedFromRepo")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentD" << "1.0.0content.txt" << "2.0.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Success;
}
void updateWithContentSha1()
{
+ QFETCH(QString, repository);
+ QFETCH(QString, repositoryForUpdate);
QFETCH(QString, component);
QFETCH(QString, content);
QFETCH(QString, updatedContent);
QFETCH(PackageManagerCore::Status, expectedStatusAfterInstall);
QFETCH(PackageManagerCore::Status, expectedStatusAfterUpdate);
- setRepository(":///data/repository");
+ setRepository(repository);
QCOMPARE(expectedStatusAfterInstall, core->installSelectedComponentsSilently(QStringList() << component));
QCOMPARE(expectedStatusAfterInstall, core->status());
VerifyInstaller::verifyInstallerResources(m_installDir, component, content);
core->commitSessionOperations();
core->setPackageManager();
- setRepository(":///data/repositoryUpdate");
+ setRepository(repositoryForUpdate);
QCOMPARE(expectedStatusAfterUpdate, core->updateComponentsSilently(QStringList()));
VerifyInstaller::verifyInstallerResources(m_installDir, component, updatedContent);
}
diff --git a/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp b/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp
index aa2849559..ef185a5cf 100644
--- a/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp
+++ b/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -46,12 +46,12 @@ private slots:
m_core.setValue(QLatin1String("QMAKE_BINARY"), QUOTE(QMAKE_BINARY));
m_core.setValue(QLatin1String("QMAKE_BINARY_OLD"), QLatin1String("FAKE_QMAKE"));
ElevatedExecuteOperation operation(&m_core);
- operation.setArguments(QStringList() << QLatin1String("UNDOEXECUTE") << QLatin1String("FAKE_QMAKE"));
+ operation.setArguments(QStringList() << QLatin1String("UNDOEXECUTE") << QLatin1String("FAKE_QMAKE") << QLatin1String("-v"));
- QTest::ignoreMessage(QtDebugMsg, "\"FAKE_QMAKE\" started, arguments: \"\"");
- QString message = "Failed to run undo operation \"Execute\" for component . Trying again with arguments %1";
+ QTest::ignoreMessage(QtDebugMsg, "\"FAKE_QMAKE\" started, arguments: \"-v\"");
+ QString message = "Failed to run undo operation \"Execute\" for component . Trying again with arguments %1, -v";
QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(QUOTE(QMAKE_BINARY))));
- message = "\"%1\" started, arguments: \"\"";
+ message = "\"%1\" started, arguments: \"-v\"";
QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(QUOTE(QMAKE_BINARY))));
QCOMPARE(operation.undoOperation(), true);
diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro
index 9325e71c5..ff070d143 100644
--- a/tests/auto/installer/installer.pro
+++ b/tests/auto/installer/installer.pro
@@ -42,7 +42,8 @@ SUBDIRS += \
createoffline \
contentshaupdate \
componentreplace \
- metadatacache
+ metadatacache \
+ contentsha1check
CONFIG(libarchive) {
SUBDIRS += libarchivearchive
diff --git a/tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z b/tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z
new file mode 100644
index 000000000..df1f72b51
--- /dev/null
+++ b/tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z
Binary files differ
diff --git a/tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7z b/tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7z
new file mode 100644
index 000000000..6afaae497
--- /dev/null
+++ b/tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7z
Binary files differ
diff --git a/tests/auto/installer/metadatajob/settings.qrc b/tests/auto/installer/metadatajob/settings.qrc
index 6e56a7854..5df3befd9 100644
--- a/tests/auto/installer/metadatajob/settings.qrc
+++ b/tests/auto/installer/metadatajob/settings.qrc
@@ -3,5 +3,7 @@
<file>data/repository/Updates.xml</file>
<file>data/repositoryActionAdd/Updates.xml</file>
<file>data/repositoryActionRemove/Updates.xml</file>
+ <file>data/repositoryZipped/repositoryZipped.7z</file>
+ <file>data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/metadatajob/tst_metadatajob.cpp b/tests/auto/installer/metadatajob/tst_metadatajob.cpp
index 54609ab95..cb974e7ad 100644
--- a/tests/auto/installer/metadatajob/tst_metadatajob.cpp
+++ b/tests/auto/installer/metadatajob/tst_metadatajob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -97,6 +97,49 @@ private slots:
metadata.waitForFinished();
QCOMPARE(metadata.metadata().count(), 1);
}
+
+ void testZippedRepository_data()
+ {
+ QTest::addColumn<QStringList>("repositories");
+ QTest::addColumn<int>("metacount");
+ QTest::addColumn<bool>("allowUnstable");
+
+ QStringList repositories;
+ repositories << ":///data/repositoryZipped/repositoryZipped.7z";
+ QTest::newRow("7z repository") << repositories << 1 << true;
+
+ repositories.clear();
+ repositories << ":///data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z";
+ QTest::newRow("7z with invalid meta sha1") << repositories << 0 << true;
+
+ repositories.clear();
+ repositories << ":///data/repositoryZipped/repositoryZipped.7z" << ":///data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z";
+ QTest::newRow("7z with one valid repository") << repositories << 1 << true;
+
+ repositories.clear();
+ repositories << ":///data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z" << ":///data/repositoryZipped/repositoryZipped.7z";
+ QTest::newRow("7z with one valid repository") << repositories << 0 << false;
+ }
+
+ void testZippedRepository()
+ {
+ QFETCH(QStringList, repositories);
+ QFETCH(int, metacount);
+ QFETCH(bool, allowUnstable);
+
+ PackageManagerCore core;
+ core.setInstaller();
+ core.setTemporaryRepositories(repositories, false, true);
+ core.settings().setAllowUnstableComponents(allowUnstable);
+
+ MetadataJob metadata;
+ metadata.setPackageManagerCore(&core);
+ metadata.addDownloadType(DownloadType::CompressedPackage);
+ metadata.setAutoDelete(true);
+ metadata.start();
+ metadata.waitForFinished();
+ QCOMPARE(metadata.metadata().count(), metacount);
+ }
};
diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
index 105bcf5d7..6c55fe205 100644
--- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp
+++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -388,13 +388,24 @@ private slots:
QCOMPARE(result.isError(), false);
}
+ void loadSimpleComponentScript_data()
+ {
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("postLoad");
+ QTest::newRow("Pre component script") << ":///data/component1.qs" << false;
+ QTest::newRow("Post component script") << ":///data/component1.qs" << true;
+ }
+
void loadSimpleComponentScript()
{
- try {
- // ignore retranslateUi which is called by loadComponentScript
+ QFETCH(QString, path);
+ QFETCH(bool, postLoad);
+
+ try {
+ // ignore retranslateUi which is called by evaluateComponentScript
setExpectedScriptOutput("Component constructor - OK");
setExpectedScriptOutput("retranslateUi - OK");
- m_component->loadComponentScript(":///data/component1.qs");
+ m_component->evaluateComponentScript(path, postLoad);
setExpectedScriptOutput("retranslateUi - OK");
m_component->languageChanged();
@@ -422,8 +433,19 @@ private slots:
}
}
+ void loadBrokenComponentScript_data()
+ {
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("postLoad");
+ QTest::newRow("Pre component script") << ":///data/component2.qs" << false;
+ QTest::newRow("Post component script") << ":///data/component2.qs" << true;
+ }
+
void loadBrokenComponentScript()
{
+ QFETCH(QString, path);
+ QFETCH(bool, postLoad);
+
Component *testComponent = new Component(&m_core);
testComponent->setValue(scName, "broken.component");
@@ -433,21 +455,31 @@ private slots:
try {
// ignore Output from script
setExpectedScriptOutput("script function: Component");
- testComponent->loadComponentScript(":///data/component2.qs");
+ testComponent->evaluateComponentScript(path, postLoad);
} catch (const Error &error) {
const QString debugMessage(
- QString("create Error-Exception: \"Exception while loading the component script \"%1\": "
- "ReferenceError: broken is not defined\"").arg(QDir::toNativeSeparators(":///data/component2.qs")));
+ QString("Exception while loading the component script \"%1\": "
+ "ReferenceError: broken is not defined on line number: 33").arg(QDir::toNativeSeparators(":///data/component2.qs")));
QVERIFY2(debugMessage.contains(error.message()), "(ReferenceError: broken is not defined)");
}
}
+ void loadComponentUserInterfaces_data()
+ {
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("postLoad");
+ QTest::newRow("Pre component script") << ":///data/userinterface.qs" << false;
+ QTest::newRow("Post component script") << ":///data/userinterface.qs" << true;
+ }
+
void loadComponentUserInterfaces()
{
- try {
+ QFETCH(QString, path);
+ QFETCH(bool, postLoad);
+ try {
setExpectedScriptOutput("checked: false");
m_component->loadUserInterfaces(QDir(":///data"), QStringList() << QLatin1String("form.ui"));
- m_component->loadComponentScript(":///data/userinterface.qs");
+ m_component->evaluateComponentScript(path, postLoad);
} catch (const Error &error) {
QFAIL(qPrintable(error.message()));
}
@@ -591,7 +623,7 @@ private slots:
try {
m_core.setPackageManager();
Component *component = m_core.componentByName("component.test.addOperation");
- component->loadComponentScript(":///data/addOperation.qs");
+ component->evaluateComponentScript(":///data/addOperation.qs");
setExpectedScriptOutput("Component::createOperations()");
component->createOperations();
diff --git a/tests/auto/installer/solver/tst_solver.cpp b/tests/auto/installer/solver/tst_solver.cpp
index ee05cc7f4..85fb387e2 100644
--- a/tests/auto/installer/solver/tst_solver.cpp
+++ b/tests/auto/installer/solver/tst_solver.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,7 +39,7 @@
using namespace QInstaller;
typedef QMap<Component *, QStringList> ComponentToStringList;
-typedef QList<QPair<Component *, UninstallerCalculator::UninstallReasonType>> UninstallReasonList;
+typedef QList<QPair<Component *, CalculatorBase::Resolution>> UninstallReasonList;
Q_DECLARE_METATYPE(UninstallReasonList)
@@ -149,7 +149,7 @@ private slots:
QTest::addColumn<PackageManagerCore *>("core");
QTest::addColumn<QList<Component *> >("selectedComponents");
QTest::addColumn<QList<Component *> >("expectedResult");
- QTest::addColumn<QList<int> >("installReason");
+ QTest::addColumn<QList<CalculatorBase::Resolution> >("installReason");
QTest::addColumn<AutoDependencyHash >("autodependencyHash");
PackageManagerCore *core = new PackageManagerCore();
@@ -176,11 +176,11 @@ private slots:
QTest::newRow("Installer resolved") << core
<< (QList<Component *>() << componentB)
<< (QList<Component *>() << componentB_NewVersion << componentAB << componentB << componentB_Auto)
- << (QList<int>()
- << InstallerCalculator::Dependent
- << InstallerCalculator::Dependent
- << InstallerCalculator::Resolved
- << InstallerCalculator::Automatic)
+ << (QList<CalculatorBase::Resolution>()
+ << CalculatorBase::Resolution::Dependent
+ << CalculatorBase::Resolution::Dependent
+ << CalculatorBase::Resolution::Resolved
+ << CalculatorBase::Resolution::Automatic)
<< autodependencyHash;
}
@@ -189,17 +189,17 @@ private slots:
QFETCH(PackageManagerCore *, core);
QFETCH(QList<Component *> , selectedComponents);
QFETCH(QList<Component *> , expectedResult);
- QFETCH(QList<int>, installReason);
+ QFETCH(QList<CalculatorBase::Resolution>, installReason);
QFETCH(AutoDependencyHash, autodependencyHash);
InstallerCalculator calc(core, autodependencyHash);
- calc.appendComponentsToInstall(selectedComponents);
- QList<Component *> result = calc.orderedComponentsToInstall();
+ calc.solve(selectedComponents);
+ QList<Component *> result = calc.resolvedComponents();
QCOMPARE(result.count(), expectedResult.count());
for (int i = 0; i < result.count(); i++) {
QCOMPARE(result.at(i), expectedResult.at(i));
- QCOMPARE((int)calc.installReasonType(result.at(i)), installReason.at(i));
+ QCOMPARE(calc.resolutionType(result.at(i)), installReason.at(i));
}
delete core;
}
@@ -237,9 +237,9 @@ private slots:
InstallerCalculator calc(core, QHash<QString, QStringList>());
QTest::ignoreMessage(QtWarningMsg, "Cannot find missing dependency \"B->=2.0.0\" for \"A\".");
- calc.appendComponentsToInstall(selectedComponents);
+ calc.solve(selectedComponents);
- QList<Component *> result = calc.orderedComponentsToInstall();
+ QList<Component *> result = calc.resolvedComponents();
QCOMPARE(result.count(), expectedResult.count());
delete core;
}
@@ -248,7 +248,7 @@ private slots:
{
QTest::addColumn<PackageManagerCore *>("core");
QTest::addColumn<QList<Component *> >("selectedToUninstall");
- QTest::addColumn<QSet<Component *> >("expectedResult");
+ QTest::addColumn<QList<Component *> >("expectedResult");
QTest::addColumn<UninstallReasonList >("uninstallReasons");
QTest::addColumn<LocalDependencyHash >("dependencyHash");
@@ -272,11 +272,11 @@ private slots:
QHash<QString, QStringList> dependencyComponentHash;
dependencyComponentHash.insert(QLatin1String("A.B"), QStringList() << QLatin1String("B"));
- uninstallReasonList.append(qMakePair(componentAB, UninstallerCalculator::Selected));
- uninstallReasonList.append(qMakePair(componentB, UninstallerCalculator::Dependent));
+ uninstallReasonList.append(qMakePair(componentAB, CalculatorBase::Resolution::Selected));
+ uninstallReasonList.append(qMakePair(componentB, CalculatorBase::Resolution::Dependent));
QTest::newRow("Uninstaller resolved") << core
<< (QList<Component *>() << componentAB)
- << (QSet<Component *>() << componentAB << componentB)
+ << (QList<Component *>() << componentB << componentAB)
<< uninstallReasonList
<< dependencyComponentHash;
@@ -297,11 +297,11 @@ private slots:
dependencyComponentHash.insert(QLatin1String("A"), QStringList() << QLatin1String("B"));
- uninstallReasonList.append(qMakePair(compA, UninstallerCalculator::Selected));
- uninstallReasonList.append(qMakePair(compB, UninstallerCalculator::Dependent));
+ uninstallReasonList.append(qMakePair(compA, CalculatorBase::Resolution::Selected));
+ uninstallReasonList.append(qMakePair(compB, CalculatorBase::Resolution::Dependent));
QTest::newRow("Cascade dependencies") << core
<< (QList<Component *>() << compA)
- << (QSet<Component *>() << compA << compB)
+ << (QList<Component *>() << compB << compA)
<< (uninstallReasonList)
<< dependencyComponentHash;
}
@@ -310,15 +310,15 @@ private slots:
{
QFETCH(PackageManagerCore *, core);
QFETCH(QList<Component *> , selectedToUninstall);
- QFETCH(QSet<Component *> , expectedResult);
+ QFETCH(QList<Component *> , expectedResult);
QFETCH(UninstallReasonList, uninstallReasons);
QFETCH(LocalDependencyHash, dependencyHash);
UninstallerCalculator calc(core, QHash<QString, QStringList>(), dependencyHash, QStringList());
- calc.appendComponentsToUninstall(selectedToUninstall);
- QSet<Component *> result = calc.componentsToUninstall();
+ calc.solve(selectedToUninstall);
+ QList<Component *> result = calc.resolvedComponents();
for (auto pair : uninstallReasons) {
- UninstallerCalculator::UninstallReasonType type = calc.uninstallReasonType(pair.first);
+ CalculatorBase::Resolution type = calc.resolutionType(pair.first);
QCOMPARE(pair.second, type);
}
QCOMPARE(result.count(), expectedResult.count());
diff --git a/tools/repogen/repogen.cpp b/tools/repogen/repogen.cpp
index a8720a0de..2abe67e78 100644
--- a/tools/repogen/repogen.cpp
+++ b/tools/repogen/repogen.cpp
@@ -185,7 +185,7 @@ int main(int argc, char** argv)
"Error: Repository parameter missing argument"));
}
- if (!QFileInfo(args.first()).exists()) {
+ if (!QFileInfo::exists(args.first())) {
return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller",
"Error: Only local filesystem repositories now supported"));
}