summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--coin/instructions/make_instructions.yaml28
-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/config/ifw.qdocconf16
-rw-r--r--doc/doc.pri5
-rw-r--r--doc/images/ifw-overview.pngbin31363 -> 27901 bytes
-rw-r--r--doc/includes/IFWDoc126
-rw-r--r--doc/installerfw-overview.qdoc76
-rw-r--r--doc/installerfw.qdoc11
-rw-r--r--doc/operations.qdoc24
-rw-r--r--doc/scripting-api/gui.qdoc24
-rw-r--r--doc/scripting-api/packagemanagercore.qdoc18
-rw-r--r--doc/scripting-api/qdesktopservices.qdoc8
-rw-r--r--doc/scripting-api/qfiledialog.qdoc9
-rw-r--r--doc/systeminfo.qdoc4
-rw-r--r--installerfw.pri17
-rw-r--r--src/libs/installer/component.cpp3
-rw-r--r--src/libs/installer/component_p.cpp1
-rw-r--r--src/libs/installer/componentmodel.cpp27
-rw-r--r--src/libs/installer/componentmodel.h3
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp50
-rw-r--r--src/libs/installer/componentselectionpage_p.h5
-rw-r--r--src/libs/installer/constants.h1
-rw-r--r--src/libs/installer/copydirectoryoperation.cpp2
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp2
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp9
-rw-r--r--src/libs/installer/downloadarchivesjob.h2
-rw-r--r--src/libs/installer/downloadfiletask.cpp5
-rw-r--r--src/libs/installer/environmentvariablesoperation.cpp9
-rw-r--r--src/libs/installer/genericdatacache.cpp2
-rw-r--r--src/libs/installer/globals.cpp57
-rw-r--r--src/libs/installer/globals.h2
-rw-r--r--src/libs/installer/globalsettingsoperation.cpp2
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp2
-rw-r--r--src/libs/installer/messageboxhandler.cpp4
-rw-r--r--src/libs/installer/metadata.cpp12
-rw-r--r--src/libs/installer/metadatajob.cpp50
-rw-r--r--src/libs/installer/metadatajob.h1
-rw-r--r--src/libs/installer/metadatajob_p.h6
-rw-r--r--src/libs/installer/packagemanagercore.cpp285
-rw-r--r--src/libs/installer/packagemanagercore.h34
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp187
-rw-r--r--src/libs/installer/packagemanagercore_p.h13
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp2
-rw-r--r--src/libs/installer/packagemanagergui.cpp110
-rw-r--r--src/libs/installer/packagemanagergui.h5
-rw-r--r--src/libs/installer/packagesource.cpp2
-rw-r--r--src/libs/installer/productkeycheck.cpp19
-rw-r--r--src/libs/installer/productkeycheck.h4
-rw-r--r--src/libs/installer/progresscoordinator.cpp47
-rw-r--r--src/libs/installer/progresscoordinator.h6
-rw-r--r--src/libs/installer/registerfiletypeoperation.cpp2
-rw-r--r--src/libs/installer/resources/installer.qrc1
-rw-r--r--src/libs/installer/resources/qt/etc/qt.conf0
-rw-r--r--src/libs/installer/scriptengine.cpp15
-rw-r--r--src/libs/installer/scriptengine_p.h3
-rw-r--r--src/libs/installer/simplemovefileoperation.cpp2
-rw-r--r--src/libs/installer/sysinfo_win.cpp4
-rw-r--r--src/libs/installer/systeminfo.cpp6
-rw-r--r--src/libs/kdtools/updateoperation.cpp17
-rw-r--r--src/libs/kdtools/updateoperation.h2
-rw-r--r--src/libs/kdtools/updateoperations.cpp65
-rw-r--r--src/libs/kdtools/updateoperations.h2
-rw-r--r--src/sdk/commandlineinterface.cpp6
-rw-r--r--src/sdk/installerbase.cpp4
-rw-r--r--src/sdk/sdk.pro4
-rw-r--r--src/sdk/settingsdialog.cpp20
-rw-r--r--src/sdk/tabcontroller.cpp1
-rw-r--r--src/sdk/translations/ifw_ar.ts2
-rw-r--r--src/sdk/translations/ifw_ca.ts4
-rw-r--r--src/sdk/translations/ifw_da.ts4
-rw-r--r--src/sdk/translations/ifw_es.ts4
-rw-r--r--src/sdk/translations/ifw_fr.ts4
-rw-r--r--src/sdk/translations/ifw_hr.ts4
-rw-r--r--src/sdk/translations/ifw_hu.ts4
-rw-r--r--src/sdk/translations/ifw_it.ts4
-rw-r--r--src/sdk/translations/ifw_ja.ts2
-rw-r--r--src/sdk/translations/ifw_ko.ts2
-rw-r--r--src/sdk/translations/ifw_pl.ts4
-rw-r--r--src/sdk/translations/ifw_pt.ts4
-rw-r--r--src/sdk/translations/ifw_pt_BR.ts4
-rw-r--r--src/sdk/translations/ifw_ru.ts4
-rw-r--r--src/sdk/translations/ifw_zh_CN.ts2
-rw-r--r--tests/auto/installer/appendfileoperation/tst_appendfileoperation.cpp35
-rw-r--r--tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp57
-rw-r--r--tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp47
-rw-r--r--tests/auto/installer/createoffline/tst_createoffline.cpp6
-rw-r--r--tests/auto/installer/deleteoperation/tst_deleteoperation.cpp49
-rw-r--r--tests/auto/installer/installer.pro2
-rw-r--r--tests/auto/installer/metadatacache/data/existing-cache/manifest.json2
-rw-r--r--tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp28
-rw-r--r--tests/auto/installer/moveoperation/tst_moveoperation.cpp4
-rw-r--r--tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1content.7zbin0 -> 129 bytes
-rw-r--r--tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1meta.7zbin0 -> 910 bytes
-rw-r--r--tests/auto/installer/prependfileoperation/data/repository/Updates.xml16
-rw-r--r--tests/auto/installer/prependfileoperation/data/xmloperationrepository/C/1.0.0content.7zbin0 -> 129 bytes
-rw-r--r--tests/auto/installer/prependfileoperation/data/xmloperationrepository/Updates.xml20
-rw-r--r--tests/auto/installer/prependfileoperation/prependfileoperation.pro10
-rw-r--r--tests/auto/installer/prependfileoperation/settings.qrc9
-rw-r--r--tests/auto/installer/prependfileoperation/tst_prependfileoperation.cpp180
-rw-r--r--tests/auto/installer/rmdiroperationtest/rmdiroperationtest.pro6
-rw-r--r--tests/auto/installer/rmdiroperationtest/tst_rmdiroperationtest.cpp111
103 files changed, 1478 insertions, 657 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 57b97ccc9..8b71acb75 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,2 +1,2 @@
-VERSION=4.7.0
+VERSION=4.8.0
CONFIG=prepare_docs qt_docs_targets $$CONFIG
diff --git a/coin/instructions/make_instructions.yaml b/coin/instructions/make_instructions.yaml
index 0957f2bc7..aa9216ce5 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.7.0.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.8.0.run"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.7.0.run"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.7.0.run"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.8.0.run"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.8.0.run"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -100,13 +100,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-aarch64-4.7.0.run"
+ command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-linux-aarch64-4.8.0.run"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-aarch64-4.7.0.run"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-aarch64-4.7.0.run"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-aarch64-4.8.0.run"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-aarch64-4.8.0.run"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -123,7 +123,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.7.0.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.8.0.app"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
@@ -140,18 +140,18 @@ instructions:
- type: ChangeDirectory
directory: "{{.AgentWorkingDir}}/qtsdk/tqtc-qtsdk/packaging_tools"
- type: ExecuteCommand
- command: "python3 -m pipenv run python sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.7.0.app"
+ command: "python3 -m pipenv run python sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.8.0.app"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to sign the ifw installer"
- type: ExecuteCommand
- command: "python3 -m pipenv run python notarize.py --path={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.7.0.dmg"
+ command: "python3 -m pipenv run python notarize.py --path={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.8.0.dmg"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to notarize the ifw installer"
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.7.0.dmg"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.7.0.dmg"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.8.0.dmg"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.8.0.dmg"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -165,7 +165,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.7.0"
+ 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.8.0"
maxTimeInSeconds: 1200
maxTimeBetweenOutput: 1200
userMessageOnFailure: "Failed to create ifw installer."
@@ -186,8 +186,8 @@ instructions:
equals_value: Windows
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.7.0.exe"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.7.0.exe"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.8.0.exe"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.8.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 9e6f3dbef..82eed4eb2 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.7.0</Version>
- <ReleaseDate>2023-04-27</ReleaseDate>
+ <Version>4.8.0</Version>
+ <ReleaseDate>2024-02-05</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 10b4f73da..a5c9a2d7a 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.7.0</Version>
- <ReleaseDate>2023-04-27</ReleaseDate>
+ <Version>4.8.0</Version>
+ <ReleaseDate>2024-02-05</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/config/ifw.qdocconf b/doc/config/ifw.qdocconf
index ba7c39394..17440d802 100644
--- a/doc/config/ifw.qdocconf
+++ b/doc/config/ifw.qdocconf
@@ -2,23 +2,21 @@ include($QT_INSTALL_DOCS/global/qt-cpp-defines.qdocconf)
include($QT_INSTALL_DOCS/global/compat.qdocconf)
include($QT_INSTALL_DOCS/global/fileextensions.qdocconf)
-moduleheader = IFWDoc
-includepaths += -I ./includes \
- -I ../src/libs/installer \
- -I ../src/libs/kdtools \
- -I ../src/libs/3rdparty/7zip/unix/CPP \
- -I ../src/libs/3rdparty/7zip/win/CPP \
- -I ../src/libs/3rdparty/libarchive
+includepaths += \
+ ../../src/libs/installer \
+ ../../src/libs/3rdparty/7zip/unix/CPP \
+ ../../src/libs/3rdparty/7zip/win/CPP \
+ ../../src/libs/3rdparty/libarchive
language = Cpp
project = "QtInstallerFramework"
description = "Qt Installer Framework Manual"
-url = http://qt-project.org/doc/qtinstallerframework/
+url = https://doc.qt.io/qtinstallerframework/
sourcedirs += ../../src/libs/installer ../../src/libs/kdtools ../includes
headerdirs += ../../src/libs/installer ../../src/libs/kdtools
-imagedirs = ../images ../templates/images
+imagedirs = ../images
exampledirs = .. \
../../examples
diff --git a/doc/doc.pri b/doc/doc.pri
index 5f17fa26c..4f6a07d6f 100644
--- a/doc/doc.pri
+++ b/doc/doc.pri
@@ -1,4 +1,4 @@
-QT += core-private widgets concurrent network qml xml
+QT += widgets concurrent network qml xml
DOC_TARGETDIR = html
INSTALL_DOC_PATH = $$IFW_BUILD_TREE/doc/$$DOC_TARGETDIR
@@ -29,7 +29,8 @@ DOC_QCH_INSTALLDIR = $$INSTALL_DOC_PATH
for (include_path, INCLUDEPATH): \
DOC_INCLUDES += -I $$shell_quote($$include_path)
for (module, QT) {
- MOD_INCLUDES = $$eval(QT.$${module}.includes)
+ MOD = $$replace(module, \-,_)
+ MOD_INCLUDES = $$eval(QT.$${MOD}.includes)
for (include_path, MOD_INCLUDES): \
DOC_INCLUDES += -I $$shell_quote($$include_path)
}
diff --git a/doc/images/ifw-overview.png b/doc/images/ifw-overview.png
index 4aac3cedf..8bda13d8e 100644
--- a/doc/images/ifw-overview.png
+++ b/doc/images/ifw-overview.png
Binary files differ
diff --git a/doc/includes/IFWDoc b/doc/includes/IFWDoc
deleted file mode 100644
index a569a2f9e..000000000
--- a/doc/includes/IFWDoc
+++ /dev/null
@@ -1,126 +0,0 @@
-#pragma once
-
-#ifdef Q_CLANG_QDOC
-
-// installer
-#include "abstractfiletask.h"
-#include "abstracttask.h"
-#include "adminauthorization.h"
-#include "binarycontent.h"
-#include "binaryformatengine.h"
-#include "binaryformatenginehandler.h"
-#include "binaryformat.h"
-#include "binarylayout.h"
-#include "commandlineparser.h"
-#include "componentchecker.h"
-#include "component.h"
-#include "componentalias.h"
-#include "componentmodel.h"
-#include "concurrentoperationrunner.h"
-#include "constants.h"
-#include "consumeoutputoperation.h"
-#include "copydirectoryoperation.h"
-#include "copyfiletask.h"
-#include "createdesktopentryoperation.h"
-#include "createlinkoperation.h"
-#include "createlocalrepositoryoperation.h"
-#include "createshortcutoperation.h"
-#include "downloadarchivesjob.h"
-#include "downloadfiletask.h"
-#include "elevatedexecuteoperation.h"
-#include "environmentvariablesoperation.h"
-#include "errors.h"
-#include "extractarchiveoperation.h"
-#include "fakestopprocessforupdateoperation.h"
-#include "fileguard.h"
-#include "fileio.h"
-#include "fileutils.h"
-#include "genericdatacache.h"
-#include "globalsettingsoperation.h"
-#include "globals.h"
-#include "graph.h"
-#include "init.h"
-#include "installercalculator.h"
-#include "installer_global.h"
-#include "installiconsoperation.h"
-#include "keepaliveobject.h"
-#include "licenseoperation.h"
-#include "linereplaceoperation.h"
-#include "lib7z_extract.h"
-#include "lib7z_list.h"
-#include "lib7z_facade.h"
-#include "lib7zarchive.h"
-#include "libarchivearchive.h"
-#include "libarchivewrapper.h"
-#include "libarchivewrapper_p.h"
-#include "abstractarchive.h"
-#include "archivefactory.h"
-#include "directoryguard.h"
-#include "link.h"
-#include "messageboxhandler.h"
-#include "metadata.h"
-#include "metadatajob.h"
-#include "minimumprogressoperation.h"
-#include "observer.h"
-#include "operationtracer.h"
-#include "packagemanagercoredata.h"
-#include "packagemanagercore.h"
-#include "packagemanagergui.h"
-#include "packagemanagerpagefactory.h"
-#include "packagemanagerproxyfactory.h"
-#include "packagesource.h"
-#include "performinstallationform.h"
-#include "permissionsettings.h"
-#include "productkeycheck.h"
-#include "progresscoordinator.h"
-#include "protocol.h"
-#include "proxycredentialsdialog.h"
-#include "qinstallerglobal.h"
-#include "qprocesswrapper.h"
-#include "qsettingswrapper.h"
-#include "qtpatch.h"
-#include "range.h"
-#include "registerfiletypeoperation.h"
-#include "remoteclient.h"
-#include "remotefileengine.h"
-#include "remoteobject.h"
-#include "remoteserverconnection.h"
-#include "remoteserver.h"
-#include "replaceoperation.h"
-#include "repositorycategory.h"
-#include "repository.h"
-#include "runextensions.h"
-#include "scriptengine.h"
-#include "selfrestartoperation.h"
-#include "serverauthenticationdialog.h"
-#include "settings.h"
-#include "settingsoperation.h"
-#include "simplemovefileoperation.h"
-#include "systeminfo.h"
-#include "testrepository.h"
-#include "uninstallercalculator.h"
-#include "unziptask.h"
-#include "utils.h"
-
-//kdtools
-#include "environment.h"
-#include "filedownloaderfactory.h"
-#include "filedownloader.h"
-#include "genericfactory.h"
-#include "job.h"
-#include "kdtoolsglobal.h"
-#include "localpackagehub.h"
-#include "lockfile.h"
-#include "runoncechecker.h"
-#include "selfrestarter.h"
-#include "sysinfo.h"
-#include "task.h"
-#include "updatefinder.h"
-#include "update.h"
-#include "updateoperationfactory.h"
-#include "updateoperation.h"
-#include "updateoperations.h"
-#include "updater.h"
-
-#endif // Q_CLANG_QDOC
-
diff --git a/doc/installerfw-overview.qdoc b/doc/installerfw-overview.qdoc
index 498d7a733..b1ad69dc5 100644
--- a/doc/installerfw-overview.qdoc
+++ b/doc/installerfw-overview.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,21 +32,30 @@
\title Overview of Qt Installer Framework
- 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 of the platform on which they run: Linux,
+ With Qt Installer Framework you can create both simple and complex installers
+ with thousands of components and deploy your installers across all the supported
+ desktop Qt platforms without rewriting the source code. Your final installers
+ 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 uninstall
+ For example Qt installers are made with the Qt Installer Framework.
+
+ Both open-source and commercial users can download Qt Installer Framework from their
+ Qt Account.
+
+ Qt Installer Framework tools generate installers with a set of pages
+ that guide the users during the installation, update, or uninstallation
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 offer users more options. You can
- create scripts to add operations to the installer.
+ or by adding whole pages to offer users more options.
+
+ Each installable package in the installer can contain one component script that
+ gives a comprehensive API to fine-tune how the package should be installed
+ on the system. You can, for example, add shortcuts to the desktop or register
+ file extensions for your tool.
\section1 Choosing Installer Type
@@ -55,17 +64,8 @@
\image ifw-overview.png
- Both installers install a \e {\MT}, 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 \MT that then
- downloads and installs components from an online repository on a web server.
- 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 downloading and running an offline installer if the end users do not
- install all the available components.
-
+ Both installers install a \e {\MT}, which allows your end users to later
+ add, update, and remove components.
End users can use the \MT 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 available on the server.
@@ -73,15 +73,47 @@
repository address in the offline installer configuration or if end users
specify the repository address themselves in the \MT settings.
+ \section2 Offline Installers
+
+ Offline installers contain all the installable components and do not require
+ network connections during the installation.
+
Create an offline installer to enable users to directly download the
installation package on a media for installation on a computer later. You
can also distribute the installation package on a CD-ROM or USB stick, for
example.
+ \section2 Online installers
+
+ Online installers install the \MT and components from an online
+ repository on a web server. After installation, the \MT can be used to
+ modify the installation from an online repository.
+
+ 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 downloading and running an offline installer if the end users do not
+ install all the available components.
+
Create an online installer to enable users to always install the latest
- versions of the content binaries.
+ versions of the content packages.
+ \list
+ \li Online repositories
+ \li Online installer
+ \endlist
+
+ \section2 Signing installers
+
+ Signing your installer is an integral step in finalizing your product. Signing
+ shows that your code is safe and secure.
+
+ Find more information on signing your installer on Windows platform
+ on \l{https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool} {Microsoft website}.
+ For more information on signing your installer in macOS, see Apple website for
+ \l{https://developer.apple.com/documentation/security/code_signing_services} {code signing}
+ and \{https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution} {notarizing}.
- \section1 Promoting Updates
+ \section1 Promoting Updates for Online Installers
Make online repositories available to promote updates to end users who
install your product. The easiest way to offer an update is to recreate
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc
index c834e5c8a..4c55c6d43 100644
--- a/doc/installerfw.qdoc
+++ b/doc/installerfw.qdoc
@@ -39,9 +39,14 @@
\section1 Version \ifwversion
- The Qt Installer Framework provides a set of tools and utilities to
- create installers for the supported desktop Qt platforms: Linux, Microsoft
- Windows, and macOS.
+ Qt Installer Framework is a robust toolset for creating custom online
+ and offline installers. It’s highly configurable and customizable and works for all
+ supported Qt platforms: Linux, Microsoft Windows, and macOS.
+
+ Here are some examples to illustrate the versatility
+ of Qt Installer Framework.
+
+
\note Report bugs and suggestions for the Qt Installer Framework project
in the \l{https://bugreports.qt.io/browse/QTIFW}{Qt Bugtracker}.
diff --git a/doc/operations.qdoc b/doc/operations.qdoc
index 032f2a730..e148d6fc1 100644
--- a/doc/operations.qdoc
+++ b/doc/operations.qdoc
@@ -69,10 +69,16 @@
\li Copy
\li "Copy" \c source \c target
\li Copies a file from \c source to \c target.
+ \note The file will be restored during unistallation. If you want
+ to skip the copying, you can overwrite the \e UNDO by passing
+ \e UNDOOPERATION and \e "", to the end of the argument list.
\row
\li Move
\li "Move" \c source \c target
\li Moves a file from \c source to \c target.
+ \note Files restored during uninstallation. If you want to move the
+ files persistently, you can overwrite the \e UNDO by passing \e
+ UNDOOPERATION and \e "", to the end of the argument list.
\row
\li SimpleMoveFile
\li "SimpleMoveFile" \c source \c target
@@ -85,14 +91,25 @@
\li Delete
\li "Delete" \c filename
\li Deletes the file specified by \c filename.
+ \note File will be restored during uninstallation. If you want to
+ delete the files persistently, you can overwrite the \e UNDO by
+ passing \e UNDOOPERATION and \e "", to the end of the argument list.
\row
\li Mkdir
\li "Mkdir" \c path
\li Creates the directory path \c path.
+ \note Directory will be deleted during uninstallation.
+ If you want to create the directory persistently, you can overwrite the \e UNDO
+ by passing \e UNDOOPERATION and \e "", to the end of the argument list. Note that
+ during full uninstall, directory will be deleted if it was created to target directory
+ and \c RemoveTargetDir is false.
\row
\li Rmdir
\li "Rmdir" \c path
\li Removes the directory path \c path.
+ \note Directory will be recreated during uninstallation.
+ If you want to remove the directory persistently, you can overwrite the \e UNDO
+ by passing \e UNDOOPERATION and \e "", to the end of the argument list.
\row
\li CopyDirectory
\li "CopyDirectory" \c sourcePath \c targetPath
@@ -105,11 +122,17 @@
\li "AppendFile" \c filename \c text
\li Appends \c text to the file specified by \c filename. \c text is
treated as ASCII text.
+ \note Text will be removed from file during unistallation. If you want to append the
+ text persistently, you can overwrite the \e UNDO by passing \e UNDOOPERATION
+ and \e "", to the end of the argument list.
\row
\li PrependFile
\li "PrependFile" \c filename \c text
\li Prepends \c text to the file specified by \c filename. \c text
is treated as ASCII text.
+ \note Text will be removed from file during unistallation. If you want to append the
+ text persistently, you can overwrite the \e UNDO by passing \e UNDOOPERATION
+ and \e "", to the end of the argument list.
\row
\li Replace
\li "Replace" \c file \c search \c replace \c mode
@@ -291,6 +314,7 @@
\li Sets or removes the \c value of \c key in the settings file located at
\c path, depending on the value of \c method: \c set, \c remove,
\c add_array_value, and \c remove_array_value.
+ Example: \c{component.addOperation("Settings", "path=settings.ini", "method=add", "key=myKey", "value=myValue")}
\endtable
The Extract, License, and MinimumProgress operations are automatically added for matching
diff --git a/doc/scripting-api/gui.qdoc b/doc/scripting-api/gui.qdoc
index 149e1bc84..b9e6c1b5f 100644
--- a/doc/scripting-api/gui.qdoc
+++ b/doc/scripting-api/gui.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -126,6 +126,28 @@
*/
/*!
+ \qmlmethod void gui::setWizardPageButtonText(int pageId, int buttonId, string buttonText)
+
+ Sets \a buttonText for button specified by \a buttonId to a installer page \a pageId.
+
+ \note In some pages, installer will change the button text when entering
+ the page. In that case, you need to connect to \c entered() -signal of the
+ page to change the \a buttonText.
+
+ \code
+ function Component()
+ {
+ var page = gui.pageByObjectName("FinishedPage");
+ page.entered.connect(Component.prototype.finishPageEntered);
+ }
+ Component.prototype.finishPageEntered = function()
+ {
+ gui.setWizardPageButtonText(QInstaller.InstallationFinished, buttons.CommitButton, "Commit");
+ }
+ \endcode
+*/
+
+/*!
\qmlmethod void gui::showSettingsButton(boolean show)
Shows the \uicontrol Settings button if \a show is \c true. This function
diff --git a/doc/scripting-api/packagemanagercore.qdoc b/doc/scripting-api/packagemanagercore.qdoc
index b45d8edc8..ecf4b1eaa 100644
--- a/doc/scripting-api/packagemanagercore.qdoc
+++ b/doc/scripting-api/packagemanagercore.qdoc
@@ -71,24 +71,6 @@
\qmlsignal installer::componentAdded(Component component)
Emitted when a new root \a component is added.
-
- \sa rootComponentsAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \qmlsignal installer::rootComponentsAdded(list<Component> components)
-
- Emitted when a new list of root \a components is added.
-
- \sa componentAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \qmlsignal installer::updaterComponentsAdded(list<Component> components)
-
- Emitted when a new list of updater \a components is added.
-
- \sa componentAdded(), rootComponentsAdded()
*/
/*!
diff --git a/doc/scripting-api/qdesktopservices.qdoc b/doc/scripting-api/qdesktopservices.qdoc
index 0fc6cf898..a9c8a149f 100644
--- a/doc/scripting-api/qdesktopservices.qdoc
+++ b/doc/scripting-api/qdesktopservices.qdoc
@@ -56,14 +56,18 @@
\li DesktopServices.PicturesLocation
\li DesktopServices.TempLocation
\li DesktopServices.HomeLocation
- \li DesktopServices.DataLocation
+ \li DesktopServices.AppLocalDataLocation
\li DesktopServices.CacheLocation
+ \li DesktopServices.GenericCacheLocation
\li DesktopServices.GenericDataLocation
\li DesktopServices.RuntimeLocation
\li DesktopServices.ConfigLocation
\li DesktopServices.DownloadLocation
- \li DesktopServices.GenericCacheLocation
\li DesktopServices.GenericConfigLocation
+ \li DesktopServices.AppDataLocation
+ \li DesktopServices.AppConfigLocation
+ \li DesktopServices.PublicShareLocation
+ \li DesktopServices.TemplatesLocation
\endlist
The enum values correspond to the values of the
diff --git a/doc/scripting-api/qfiledialog.qdoc b/doc/scripting-api/qfiledialog.qdoc
index 3f281f44f..6fd01de3d 100644
--- a/doc/scripting-api/qfiledialog.qdoc
+++ b/doc/scripting-api/qfiledialog.qdoc
@@ -42,17 +42,19 @@
*/
/*!
- \qmlmethod string QFileDialog::getExistingDirectory(string caption, string dir)
+ \qmlmethod string QFileDialog::getExistingDirectory(string caption, string dir, string identifier)
Returns an existing directory selected by the user.
The dialog's working directory is set to \a dir, and the caption is set to
\a caption. Either of these may be an empty string, in which case the
current directory and a default caption will be used, respectively.
+ The \a identifier is used in command line interface to allow to identify
+ specific file dialogs for automatic answer.
*/
/*!
- \qmlmethod string QFileDialog::getOpenFileName(string caption, string dir, string filter)
+ \qmlmethod string QFileDialog::getOpenFileName(string caption, string dir, string filter, string identifier)
Returns an existing file selected by the user. If the user selects
\uicontrol Cancel, returns a null string.
@@ -64,6 +66,9 @@
file name, the file will be selected. Only files that match the specified
\a filter are shown. Either of these may be an empty string.
+ The \a identifier is used in command line interface to allow to identify
+ specific file dialogs for automatic answer.
+
To specify multiple filters, separate them with two semicolons (;;). For
example:
diff --git a/doc/systeminfo.qdoc b/doc/systeminfo.qdoc
index fd32b06b8..3695dbc07 100644
--- a/doc/systeminfo.qdoc
+++ b/doc/systeminfo.qdoc
@@ -67,7 +67,7 @@
there's an emulation layer or if the CPU supports multiple architectures (like x86-64
processors supporting i386 applications). To detect that, use \c installer.currentCpuArchitecture()
- \sa QSysInfo::buildCpuArchitecture() \sa currentCpuArchitecture()
+ \sa QSysInfo::buildCpuArchitecture() currentCpuArchitecture
*/
/*!
@@ -117,7 +117,7 @@
\list
\li "windows"
\li "opensuse" (for the Linux openSUSE distribution)
- \li "osx"
+ \li "macos"
\endlist
\sa QSysInfo::productType()
diff --git a/installerfw.pri b/installerfw.pri
index 8a73c10ed..9b969e4ee 100644
--- a/installerfw.pri
+++ b/installerfw.pri
@@ -3,14 +3,14 @@
}
IFW_PRI_INCLUDED = 1
-IFW_VERSION_STR = 4.7.0
-IFW_VERSION = 0x040700
-IFW_VERSION_WIN32 = 4,7,0,0
+IFW_VERSION_STR = 4.8.0
+IFW_VERSION = 0x040800
+IFW_VERSION_WIN32 = 4,8,0,0
IFW_VERSION_STR_WIN32 = $$IFW_VERSION_STR\0
IFW_REPOSITORY_FORMAT_VERSION = 1.0.0
-IFW_CACHE_FORMAT_VERSION = 1.1.0
+IFW_CACHE_FORMAT_VERSION = 1.2.0
IFW_NEWLINE = $$escape_expand(\\n\\t)
isEmpty(IFW_DISABLE_TRANSLATIONS): IFW_DISABLE_TRANSLATIONS = $$(IFW_DISABLE_TRANSLATIONS)
@@ -98,6 +98,15 @@ win32 {
LCONVERT = $${LCONVERT}.exe
QMAKE_BINARY = $${QMAKE_BINARY}.exe
}
+
+#6.6.0 rcc has been moved to libexec in linux/mac and the RCC variable no longer
+#points to correct location
+!exists($$RCC) {
+ RCC = $$toNativeSeparators($$cleanPath($$[QT_INSTALL_LIBEXECS]/rcc))
+}
+!exists($$RCC) {
+ warning("Resource compiler '$$RCC' not found.")
+}
win32-g++*:QMAKE_CXXFLAGS += -Wno-attributes
macx:QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index a1891f725..ce76a2927 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -1195,9 +1195,6 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
return operation;
}
- if (operation->name() == scDelete)
- operation->setValue(scPerformUndo, false);
-
// Operation can contain variables which are resolved when performing the operation
if (operation->requiresUnreplacedVariables())
operation->setArguments(parameters);
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 7cf47c925..bf3941274 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -188,6 +188,7 @@ void ComponentModelHelper::setCheckable(bool checkable)
setData(Qt::Unchecked, Qt::CheckStateRole);
}
changeFlags(checkable, Qt::ItemIsUserCheckable);
+ m_componentPrivate->m_vars[scCheckable] = checkable ? scTrue : scFalse;
}
/*!
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index dbb80f2f6..1e8dd1ff7 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -335,6 +335,22 @@ QSet<Component *> ComponentModel::uncheckable() const
return m_uncheckable;
}
+bool ComponentModel::componentsSelected() const
+{
+ if (m_core->isInstaller() || m_core->isUpdater())
+ return checked().count();
+
+ if (checkedState().testFlag(ComponentModel::DefaultChecked) == false)
+ return true;
+
+ const QSet<Component *> uncheckables = uncheckable();
+ for (auto &component : uncheckables) {
+ if (component->forcedInstallation() && !component->isInstalled())
+ return true; // allow installation for new forced components
+ }
+ return false;
+}
+
/*!
Returns a pointer to the PackageManagerCore this model belongs to.
*/
@@ -562,12 +578,11 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone
for (int i = sortedNodes.count(); i > 0; i--) {
Component * const node = sortedNodes.at(i - 1);
- bool checkable = true;
- if (node->value(scCheckable, scTrue).toLower() == scFalse) {
- checkable = false;
- }
+ if (!node->isEnabled() || node->isUnstable())
+ continue;
- if ((!node->isCheckable() && checkable) || !node->isEnabled() || node->isUnstable())
+ //Do not let forced installations to be uninstalled
+ if (!m_core->isUpdater() && node->forcedInstallation() && (node->checkState() != Qt::Unchecked))
continue;
if (!m_core->isUpdater() && !node->autoDependencies().isEmpty())
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index 1e8a2d297..c93dd60ae 100644
--- a/src/libs/installer/componentmodel.h
+++ b/src/libs/installer/componentmodel.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -80,6 +80,7 @@ public:
QSet<Component *> partially() const;
QSet<Component *> unchecked() const;
QSet<Component *> uncheckable() const;
+ bool componentsSelected() const;
PackageManagerCore *core() const;
ComponentModel::ModelState checkedState() const;
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index d9ed9a0ec..b68eebf06 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -74,20 +74,17 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
, m_tabWidget(nullptr)
, m_descriptionBaseWidget(nullptr)
, m_categoryWidget(Q_NULLPTR)
- , m_allowCompressedRepositoryInstall(false)
, m_allowCreateOfflineInstaller(false)
, m_categoryLayoutVisible(false)
, m_allModel(m_core->defaultComponentModel())
, m_updaterModel(m_core->updaterComponentModel())
, m_currentModel(m_allModel)
- , m_proxyModel(new ComponentSortFilterProxyModel(q))
+ , m_proxyModel(m_core->componentSortFilterProxyModel())
, m_componentsResolved(false)
, m_headerStretchLastSection(false)
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
m_treeView->setUniformRowHeights(true);
- m_proxyModel->setRecursiveFilteringEnabled(true);
- m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_descriptionBaseWidget = new QWidget(q);
m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
@@ -237,10 +234,6 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
connect(m_core, &PackageManagerCore::metaJobTotalProgress, this,
&ComponentSelectionPagePrivate::setTotalProgress);
-
-#ifdef INSTALLCOMPRESSED
- allowCompressedRepositoryInstall();
-#endif
}
ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
@@ -248,11 +241,6 @@ ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
}
-void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall()
-{
- m_allowCompressedRepositoryInstall = true;
-}
-
void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow)
{
m_allowCreateOfflineInstaller = allow;
@@ -260,7 +248,7 @@ void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow)
void ComponentSelectionPagePrivate::showCompressedRepositoryButton()
{
- if (m_allowCompressedRepositoryInstall)
+ if (m_core->allowCompressedRepositoryInstall())
m_qbspPushButton->setVisible(true);
}
@@ -498,27 +486,6 @@ void ComponentSelectionPagePrivate::deselectAll()
m_currentModel->setCheckedState(ComponentModel::AllUnchecked);
}
-void ComponentSelectionPagePrivate::enableRepositoryCategory(const QString &repositoryName, bool enable)
-{
- QMap<QString, RepositoryCategory> organizedRepositoryCategories = m_core->settings().organizedRepositoryCategories();
-
- QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName);
- RepositoryCategory repoCategory;
- while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) {
- repoCategory = i.value();
- i++;
- }
-
- RepositoryCategory replacement = repoCategory;
- replacement.setEnabled(enable);
- QSet<RepositoryCategory> tmpRepoCategories = m_core->settings().repositoryCategories();
- if (tmpRepoCategories.contains(repoCategory)) {
- tmpRepoCategories.remove(repoCategory);
- tmpRepoCategories.insert(replacement);
- m_core->settings().addRepositoryCategories(tmpRepoCategories);
- }
-}
-
void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
{
if (show)
@@ -546,7 +513,7 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
QList<QCheckBox*> checkboxes = m_categoryGroupBox->findChildren<QCheckBox *>();
for (int i = 0; i < checkboxes.count(); i++) {
QCheckBox *checkbox = checkboxes.at(i);
- enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
+ m_core->enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
}
if (!m_core->fetchRemotePackagesTree()) {
@@ -571,15 +538,8 @@ void ComponentSelectionPagePrivate::qbspButtonClicked()
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) {
+ if (m_core->addQBspRepositories(fileNames)) {
updateWidgetVisibility(true);
- m_core->settings().addTemporaryRepositories(set, false);
if (!m_core->fetchCompressedPackagesTree()) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index ee00347a8..187fce61d 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -66,7 +66,6 @@ public:
explicit ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core);
~ComponentSelectionPagePrivate();
- void allowCompressedRepositoryInstall();
void setAllowCreateOfflineInstaller(bool allow);
void showCompressedRepositoryButton();
void hideCompressedRepositoryButton();
@@ -83,7 +82,6 @@ public slots:
void updateAllCheckStates(int which);
void selectAll();
void deselectAll();
- void enableRepositoryCategory(const QString &repositoryName, bool enable);
void updateWidgetVisibility(bool show);
void fetchRepositoryCategories();
void createOfflineButtonClicked();
@@ -117,7 +115,6 @@ private:
QProgressBar *m_progressBar;
QGridLayout *m_mainGLayout;
QVBoxLayout *m_rightSideVLayout;
- bool m_allowCompressedRepositoryInstall;
bool m_allowCreateOfflineInstaller;
bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index d0173b872..7bf816b5f 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -129,7 +129,6 @@ 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/");
diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp
index a2ef2cf5a..c0fec0649 100644
--- a/src/libs/installer/copydirectoryoperation.cpp
+++ b/src/libs/installer/copydirectoryoperation.cpp
@@ -153,7 +153,7 @@ bool CopyDirectoryOperation::performOperation()
bool CopyDirectoryOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
if (!checkArgumentCount(2))
diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp
index 7090f9a8b..286cc9b5b 100644
--- a/src/libs/installer/createlocalrepositoryoperation.cpp
+++ b/src/libs/installer/createlocalrepositoryoperation.cpp
@@ -378,7 +378,7 @@ bool CreateLocalRepositoryOperation::performOperation()
bool CreateLocalRepositoryOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
if (!checkArgumentCount(2))
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
index fcad22cfa..65eead1f9 100644
--- a/src/libs/installer/downloadarchivesjob.cpp
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -49,7 +49,7 @@ static constexpr uint scMaxRetries = 5;
/*!
Creates a new DownloadArchivesJob with parent \a core.
*/
-DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
+DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core, const QString &objectName)
: Job(core)
, m_core(core)
, m_downloader(nullptr)
@@ -63,6 +63,7 @@ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
, m_retryCount(scMaxRetries)
{
setCapabilities(Cancelable);
+ setObjectName(objectName);
}
/*!
@@ -292,11 +293,13 @@ void DownloadArchivesJob::registerFile()
const QMessageBox::Button res =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while "
- "downloading failed. This is a temporary error, please retry."),
+ "downloading failed. This is a temporary error, please retry.\n\n"
+ "Expected: %1 \nDownloaded: %2").arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())),
QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Retry);
if (res == QMessageBox::Cancel) {
- finishWithError(tr("Cannot verify Hash"));
+ finishWithError(tr("Cannot verify Hash\nExpected: %1 \nDownloaded: %2")
+ .arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())));
return;
}
// When using command line instance, only retry a number of times to avoid
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
index 122af2831..5155c881a 100644
--- a/src/libs/installer/downloadarchivesjob.h
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -51,7 +51,7 @@ class DownloadArchivesJob : public Job
Q_OBJECT
public:
- explicit DownloadArchivesJob(PackageManagerCore *core);
+ explicit DownloadArchivesJob(PackageManagerCore *core, const QString &objectName);
~DownloadArchivesJob();
int numberOfDownloads() const { return m_archivesDownloaded; }
diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp
index 5cd72109b..a959677a9 100644
--- a/src/libs/installer/downloadfiletask.cpp
+++ b/src/libs/installer/downloadfiletask.cpp
@@ -30,6 +30,7 @@
#include "downloadfiletask_p.h"
#include "globals.h"
+#include "productkeycheck.h"
#include <QCoreApplication>
#include <QDir>
@@ -286,6 +287,10 @@ void Downloader::errorOccurred(QNetworkReply::NetworkError error)
if (data.taskItem.source().contains(QLatin1String("Updates.xml"), Qt::CaseInsensitive)) {
qCWarning(QInstaller::lcServer) << QString::fromLatin1("Network error while downloading '%1': %2.").arg(
data.taskItem.source(), reply->errorString());
+ } else if (data.taskItem.source().contains(QLatin1String("_meta"), Qt::CaseInsensitive)) {
+ QString errorString = tr("Network error while downloading '%1': %2.").arg(data.taskItem.source(), reply->errorString());
+ errorString.append(ProductKeyCheck::instance()->additionalMetaDownloadWarning());
+ m_futureInterface->reportException(TaskException(errorString));
} else {
m_futureInterface->reportException(
TaskException(tr("Network error while downloading '%1': %2.").arg(
diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp
index 44fe7d657..94cd1e36f 100644
--- a/src/libs/installer/environmentvariablesoperation.cpp
+++ b/src/libs/installer/environmentvariablesoperation.cpp
@@ -30,6 +30,7 @@
#include "qsettingswrapper.h"
#include <stdlib.h>
+#include <QDir>
#include "environment.h"
#include "globals.h"
@@ -168,11 +169,11 @@ UpdateOperation::Error undoSetting(const QString &regPath,
if (actual != value)
{
- //For unknown reason paths with @TargetDir@ variable get modified
- //so that Windows file separators get replaced with unix style separators,
- //fix separators before matching to actual value in register
+ //Ignore the separators
+ static const QRegularExpression regex(QLatin1String("(\\\\|/)"));
QString tempValue = value;
- QString fixedValue = tempValue.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ QString fixedValue = tempValue.replace(regex, QDir::separator());
+ actual = actual.replace(regex, QDir::separator());
if (actual != fixedValue) //key changed, don't undo
return UpdateOperation::UserDefinedError;
diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp
index 73d34b94a..a1e31ccfe 100644
--- a/src/libs/installer/genericdatacache.cpp
+++ b/src/libs/installer/genericdatacache.cpp
@@ -428,7 +428,7 @@ T *GenericDataCache<T>::itemByPath(const QString &path) const
The cache takes ownership of the object pointed by \a item. The contents of the
item are copied or moved to the cache with a subdirectory name that matches the checksum
- of the item. The \c mode decides how the contents of the item are registered, either by
+ of the item. The \a mode decides how the contents of the item are registered, either by
copying or moving.
Returns \c true on success or \c false if the item could not be registered.
diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp
index b6bf0ca3e..3fd084768 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,14 @@
#include "globals.h"
+#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <conio.h>
+#endif
+#include <iostream>
+
const char IFW_SERVER[] = "ifw.server";
const char IFW_INSTALLER_INSTALLLOG[] = "ifw.installer.installlog";
const char IFW_DEVELOPER_BUILD[] = "ifw.developer.build";
@@ -119,5 +127,52 @@ QString enumToString(const QMetaObject& metaObject, const char *enumerator, int
return value;
}
+void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle)
+{
+ std::string usernameStdStr;
+ std::string passwordStdStr;
+
+ std::cout << qPrintable(usernameTitle);
+ std::cin >> usernameStdStr;
+
+ std::cout << qPrintable(passwordTitle);
+#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+ termios oldTerm;
+ termios term;
+
+ // Turn off echoing
+ tcgetattr(STDIN_FILENO, &oldTerm);
+ term = oldTerm;
+ term.c_lflag &= ~ECHO;
+ tcsetattr(STDIN_FILENO, TCSANOW, &term);
+
+ std::cin >> passwordStdStr;
+
+ // Clear input buffer
+ std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+
+ // Restore old attributes
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
+#elif defined(Q_OS_WIN)
+ char ch;
+ while ((ch = _getch()) != '\r') { // Return key
+ if (ch == '\b') { // Backspace key
+ if (!passwordStdStr.empty())
+ passwordStdStr.pop_back();
+ } else {
+ passwordStdStr.push_back(ch);
+ }
+ }
+ // Clear input buffer
+ int c;
+ while ((c = getchar()) != '\n' && c != EOF);
+
+#endif
+ std::cout << "\n";
+
+ *username = username->fromStdString(usernameStdStr);
+ *password = password->fromStdString(passwordStdStr);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/globals.h b/src/libs/installer/globals.h
index 3b3f4e3ab..2d119048b 100644
--- a/src/libs/installer/globals.h
+++ b/src/libs/installer/globals.h
@@ -59,6 +59,8 @@ QSet<T> toQSet(const C<T> &container)
return QSet<T>(container.begin(), container.end());
}
+void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle);
+
} // QInstaller
#endif // GLOBALS_H
diff --git a/src/libs/installer/globalsettingsoperation.cpp b/src/libs/installer/globalsettingsoperation.cpp
index 9608bba66..6ca50f96f 100644
--- a/src/libs/installer/globalsettingsoperation.cpp
+++ b/src/libs/installer/globalsettingsoperation.cpp
@@ -77,7 +77,7 @@ bool GlobalSettingsOperation::performOperation()
bool GlobalSettingsOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
const QStringList args = parsePerformOperationArguments();
diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp
index 233ff816e..b4325243d 100644
--- a/src/libs/installer/libarchivewrapper_p.cpp
+++ b/src/libs/installer/libarchivewrapper_p.cpp
@@ -41,12 +41,14 @@ namespace QInstaller {
*/
/*!
+ \internal
\fn QInstaller::LibArchiveWrapperPrivate::dataBlockRequested()
Emitted when the server process has requested another data block.
*/
/*!
+ \internal
\fn QInstaller::LibArchiveWrapperPrivate::remoteWorkerFinished()
Emitted when the server process has finished extracting an archive.
diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp
index 78abc88fa..052709e51 100644
--- a/src/libs/installer/messageboxhandler.cpp
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -368,7 +368,9 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton defaultButton)
{
QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
- msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+ msgBox.setTextInteractionFlags(Qt::TextBrowserInteraction);
+ msgBox.setTextFormat(Qt::RichText);
+
QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox *>();
Q_ASSERT(buttonBox != nullptr);
diff --git a/src/libs/installer/metadata.cpp b/src/libs/installer/metadata.cpp
index 3fca6d098..2eccb020e 100644
--- a/src/libs/installer/metadata.cpp
+++ b/src/libs/installer/metadata.cpp
@@ -78,20 +78,12 @@ static bool verifyFileIntegrityFromElement(const QDomElement &element, const QSt
if (!testChecksum)
continue;
- QFile hashFile(file.fileName() + QLatin1String(".sha1"));
- if (!hashFile.open(QIODevice::ReadOnly)) {
- qCWarning(QInstaller::lcInstallerInstallLog)
- << "Cannot open" << hashFile.fileName()
- << "for reading:" << hashFile.errorString();
- return false;
- }
-
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(&file);
const QByteArray checksum = hash.result().toHex();
- const QByteArray expectedChecksum = hashFile.readAll();
- if (checksum != expectedChecksum) {
+ if (!QFileInfo::exists(dir.absolutePath() + QDir::separator()
+ + QString::fromLatin1(checksum) + QLatin1String(".sha1"))) {
qCWarning(QInstaller::lcInstallerInstallLog)
<< "Unexpected checksum for file" << file.fileName();
return false;
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index 03ad84e20..dd222dd92 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.cpp
@@ -41,6 +41,7 @@
#include <QtConcurrent>
#include <QtMath>
#include <QRandomGenerator>
+#include <QApplication>
namespace QInstaller {
@@ -212,6 +213,11 @@ bool MetadataJob::clearCache()
return false;
}
+bool MetadataJob::isValidCache() const
+{
+ return m_metaFromCache.isValid();
+}
+
// -- private slots
void MetadataJob::doStart()
@@ -497,16 +503,25 @@ void MetadataJob::xmlTaskFinished()
}
} catch (const AuthenticationRequiredException &e) {
if (e.type() == AuthenticationRequiredException::Type::Proxy) {
- const QNetworkProxy proxy = e.proxy();
- ProxyCredentialsDialog proxyCredentials(proxy);
qCWarning(QInstaller::lcInstallerInstallLog) << e.message();
-
- if (proxyCredentials.exec() == QDialog::Accepted) {
+ QString username;
+ QString password;
+ const QNetworkProxy proxy = e.proxy();
+ if (m_core->isCommandLineInstance()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("The proxy %1:%2 requires a username and password").arg(proxy.hostName(), proxy.port());
+ askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: "));
+ } else {
+ ProxyCredentialsDialog proxyCredentials(proxy);
+ if (proxyCredentials.exec() == QDialog::Accepted) {
+ username = proxyCredentials.userName();
+ password = proxyCredentials.password();
+ }
+ }
+ if (!username.isEmpty()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Retrying with new credentials ...";
PackageManagerProxyFactory *factory = m_core->proxyFactory();
- factory->setProxyCredentials(proxy, proxyCredentials.userName(),
- proxyCredentials.password());
+ factory->setProxyCredentials(proxy, username, password);
m_core->setProxyFactory(factory);
status = XmlDownloadRetry;
} else {
@@ -515,13 +530,25 @@ void MetadataJob::xmlTaskFinished()
}
} else if (e.type() == AuthenticationRequiredException::Type::Server) {
qCWarning(QInstaller::lcInstallerInstallLog) << e.message();
- ServerAuthenticationDialog dlg(e.message(), e.taskItem());
- if (dlg.exec() == QDialog::Accepted) {
+ QString username;
+ QString password;
+ if (m_core->isCommandLineInstance()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Server Requires Authentication";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "You need to supply a username and password to access this site.";
+ askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: "));
+ } else {
+ ServerAuthenticationDialog dlg(e.message(), e.taskItem());
+ if (dlg.exec() == QDialog::Accepted) {
+ username = dlg.user();
+ password = dlg.password();
+ }
+ }
+ if (!username.isEmpty()) {
Repository original = e.taskItem().value(TaskRole::UserRole)
.value<Repository>();
Repository replacement = original;
- replacement.setUsername(dlg.user());
- replacement.setPassword(dlg.password());
+ replacement.setUsername(username);
+ replacement.setPassword(password);
Settings &s = m_core->settings();
QSet<Repository> temporaries = s.temporaryRepositories();
@@ -575,8 +602,9 @@ void MetadataJob::xmlTaskFinished()
// No new metadata packages to fetch, still need to update the cache
// for refreshed repositories.
startUpdateCacheTask();
- }
+ }
} else if (status == XmlDownloadRetry) {
+ reset();
QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
} else {
reset();
diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h
index c862215de..13ad3ea8c 100644
--- a/src/libs/installer/metadatajob.h
+++ b/src/libs/installer/metadatajob.h
@@ -74,6 +74,7 @@ public:
bool resetCache(bool init = false);
bool clearCache();
+ bool isValidCache() const;
private slots:
void doStart() override;
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index 0bd47b324..837a7e9ae 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -115,14 +115,14 @@ public:
hash.addData(&file);
const QByteArray hexChecksum = hash.result().toHex();
- QFile hashFile(file.fileName() + QLatin1String(".sha1"));
+ QFileInfo fileInfo(file.fileName());
+ QFile hashFile(fileInfo.absolutePath() + QDir::separator()
+ + QString::fromLatin1(hexChecksum) + QLatin1String(".sha1"));
if (!hashFile.open(QIODevice::WriteOnly)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
"writing: %2").arg(QDir::toNativeSeparators(hashFile.fileName()), hashFile.errorString())));
break;
}
- QTextStream stream(&hashFile);
- stream << hexChecksum;
}
}
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 0ed744ca0..2fb39a4a8 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,6 +47,7 @@
#include "installercalculator.h"
#include "uninstallercalculator.h"
#include "loggingutils.h"
+#include "componentsortfilterproxymodel.h"
#include <productkeycheck.h>
@@ -182,25 +183,6 @@ using namespace QInstaller;
Emitted when the new root component \a comp is added.
\sa {installer::componentAdded}{installer.componentAdded}
- \sa rootComponentsAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \fn QInstaller::PackageManagerCore::rootComponentsAdded(QList<QInstaller::Component*> components)
-
- Emitted when the list of root components specified by \a components is added.
-
- \sa {installer::rootComponentsAdded}{installer.rootComponentsAdded}
- \sa componentAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \fn QInstaller::PackageManagerCore::updaterComponentsAdded(QList<QInstaller::Component*> components)
-
- Emitted when a new list of updater components specified by \a components is added.
-
- \sa {installer::updaterComponentsAdded}{installer.updaterComponentsAdded}
- \sa componentAdded(), rootComponentsAdded()
*/
/*!
@@ -607,6 +589,14 @@ bool PackageManagerCore::clearLocalCache(QString *error)
}
/*!
+ Returns \c true if the metadata cache is initialized and valid, \c false otherwise.
+*/
+bool PackageManagerCore::isValidCache() const
+{
+ return d->m_metadataJob.isValidCache();
+}
+
+/*!
\internal
*/
template <typename T>
@@ -619,6 +609,22 @@ template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const
template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
/*!
+ Saves the installer \a args user has given when running installer. Command and option arguments
+ are not saved.
+*/
+void PackageManagerCore::saveGivenArguments(const QStringList &args)
+{
+ m_arguments = args;
+}
+
+/*!
+ Returns the commands and options user has given when running installer.
+*/
+QStringList PackageManagerCore::givenArguments() const
+{
+ return m_arguments;
+}
+/*!
\deprecated [4.5] Use recalculateAllComponents() instead.
\sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
@@ -847,7 +853,7 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ tr("Downloading packages..."));
- DownloadArchivesJob archivesJob(this);
+ DownloadArchivesJob archivesJob(this, QLatin1String("downloadArchiveJob"));
archivesJob.setAutoDelete(false);
archivesJob.setArchivesToDownload(archivesToDownload);
archivesJob.setExpectedTotalSize(archivesToDownloadTotalSize);
@@ -1604,7 +1610,7 @@ void PackageManagerCore::networkSettingsChanged()
d->m_repoFetched = false;
d->m_updateSourcesAdded = false;
- if (isMaintainer() ) {
+ if (!isInstaller()) {
bool gainedAdminRights = false;
if (!directoryWritable(d->targetDir())) {
gainAdminRights();
@@ -1677,11 +1683,42 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
return fetchPackagesTree(packages, installedPackages);
}
+bool PackageManagerCore::fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched)
+{
+ auto checkComponents = [&]() {
+ if (!fetchRemotePackagesTree(components))
+ return false;
+ return true;
+ };
+
+ if (!checkComponents()) {
+ // error when fetching packages tree
+ if (status() != NoPackagesFound)
+ return false;
+ //retry fetching packages with all categories enabled
+ fallBackReposFetched = true;
+ if (!d->enableAllCategories())
+ return false;
+
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << "Components not found for installation with the current selection."
+ << "Searching from additional repositories";
+ if (!ProductKeyCheck::instance()->securityWarning().isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << ProductKeyCheck::instance()->securityWarning();
+ }
+ if (!checkComponents()) {
+ return false;
+ }
+ }
+ return true;
+}
+
/*!
Checks for packages to install. Returns \c true if newer versions exist
- and they can be installed.
+ and they can be installed. Returns \c false if not \a components are found
+ for install, or if error occurred when fetching and generating package tree.
*/
-bool PackageManagerCore::fetchRemotePackagesTree()
+bool PackageManagerCore::fetchRemotePackagesTree(const QStringList& components)
{
d->setStatus(Running);
@@ -1709,8 +1746,14 @@ bool PackageManagerCore::fetchRemotePackagesTree()
return false;
const PackagesList &packages = d->remotePackages();
- if (packages.isEmpty())
+ if (packages.isEmpty()) {
+ d->setStatus(PackageManagerCore::NoPackagesFound);
+ return false;
+ }
+
+ if (!d->installablePackagesFound(components))
return false;
+
return fetchPackagesTree(packages, installedPackages);
}
@@ -1959,6 +2002,82 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie
settings().setTemporaryRepositories(repositorySet, replace);
}
+bool PackageManagerCore::addQBspRepositories(const QStringList &repositories)
+{
+ QSet<Repository> set;
+ foreach (QString fileName, repositories) {
+ Repository repository = Repository::fromUserInput(fileName, true);
+ repository.setEnabled(true);
+ set.insert(repository);
+ }
+ if (set.count() > 0) {
+ settings().addTemporaryRepositories(set, false);
+ return true;
+ }
+ return false;
+}
+
+bool PackageManagerCore::validRepositoriesAvailable() const
+{
+ foreach (const Repository &repo, settings().repositories()) {
+ if (repo.isEnabled() && repo.isValid()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PackageManagerCore::setAllowCompressedRepositoryInstall(bool allow)
+{
+ d->m_allowCompressedRepositoryInstall = allow;
+}
+
+bool PackageManagerCore::allowCompressedRepositoryInstall() const
+{
+ return d->m_allowCompressedRepositoryInstall;
+}
+
+bool PackageManagerCore::showRepositoryCategories() const
+{
+ bool showCagetories = settings().repositoryCategories().count() > 0 && !isOfflineOnly() && !isUpdater();
+ if (showCagetories)
+ settings().setAllowUnstableComponents(true);
+ return showCagetories;
+}
+
+QVariantMap PackageManagerCore::organizedRepositoryCategories() const
+{
+ QVariantMap map;
+ QSet<RepositoryCategory> categories = settings().repositoryCategories();
+ foreach (const RepositoryCategory &category, categories)
+ map.insert(category.displayname(), QVariant::fromValue(category));
+ return map;
+}
+
+void PackageManagerCore::enableRepositoryCategory(const QString &repositoryName, bool enable)
+{
+ QMap<QString, RepositoryCategory> organizedRepositoryCategories = settings().organizedRepositoryCategories();
+
+ QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName);
+ while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) {
+ d->enableRepositoryCategory(i.value(), enable);
+ i++;
+ }
+}
+
+void PackageManagerCore::runProgram()
+{
+ const QString program = replaceVariables(value(scRunProgram));
+
+ const QStringList args = replaceVariables(values(scRunProgramArguments));
+ if (program.isEmpty())
+ return;
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args;
+ QProcess::startDetached(program, args);
+}
+
+
/*!
Returns the script engine that prepares and runs the component scripts.
@@ -2536,6 +2655,20 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const
}
/*!
+ Returns the proxy model
+*/
+
+ComponentSortFilterProxyModel *PackageManagerCore::componentSortFilterProxyModel()
+{
+ if (!d->m_componentSortFilterProxyModel) {
+ d->m_componentSortFilterProxyModel = new ComponentSortFilterProxyModel(this);
+ d->m_componentSortFilterProxyModel->setRecursiveFilteringEnabled(true);
+ d->m_componentSortFilterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ }
+ return d->m_componentSortFilterProxyModel;
+}
+
+/*!
Lists available packages filtered with \a regexp without GUI. Virtual
components are not listed unless set visible. Optionally, a \a filters
hash containing package information elements and regular expressions
@@ -2700,7 +2833,7 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co
eligible for installation, otherwise returns \c false. An error message can be retrieved
with \a errorMessage.
*/
-bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names, QString &errorMessage)
+bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound)
{
bool installComponentsFound = false;
@@ -2713,6 +2846,7 @@ bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names
if (alias->isUnstable()) {
errorMessage.append(tr("Cannot select alias %1. There was a problem loading this alias, "
"so it is marked unstable and cannot be selected.").arg(name) + QLatin1Char('\n'));
+ unstableAliasFound = true;
continue;
} else if (alias->isVirtual()) {
errorMessage.append(tr("Cannot select %1. Alias is marked virtual, meaning it cannot "
@@ -2811,7 +2945,16 @@ PackageManagerCore::Status PackageManagerCore::updateComponentsSilently(const QS
ComponentModel *model = updaterComponentModel();
- fetchRemotePackagesTree();
+ bool fallbackReposFetched = false;
+ bool packagesFound = fetchPackagesWithFallbackRepositories(componentsToUpdate, fallbackReposFetched);
+
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available for update with the current selection.";
+ d->setStatus(Canceled);
+ return status();
+ }
+
// List contains components containing update, if essential found contains only essential component
const QList<QInstaller::Component*> componentList = componentsMarkedForInstallation();
@@ -2917,6 +3060,20 @@ void PackageManagerCore::addLicenseItem(const QHash<QString, QVariantMap> &licen
}
}
+bool PackageManagerCore::hasLicenses() const
+{
+ foreach (Component* component, orderedComponentsToInstall()) {
+ if (isMaintainer() && component->isInstalled())
+ continue; // package manager or updater, hide as long as the component is installed
+
+ // The component is about to be installed and provides a license, so the page needs to
+ // be shown.
+ if (!component->licenses().isEmpty())
+ return true;
+ }
+ return false;
+}
+
/*!
* Adds \a component local \a dependencies to a hash table for quicker search for
* uninstall dependency components.
@@ -3000,23 +3157,7 @@ PackageManagerCore::Status PackageManagerCore::removeInstallationSilently()
PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStringList &componentsToAdd)
{
setOfflineGenerator();
- // init default model before fetching remote packages tree
- ComponentModel *model = defaultComponentModel();
- Q_UNUSED(model);
- if (!fetchRemotePackagesTree())
- return status();
-
- QString errorMessage;
- if (checkComponentsForInstallation(componentsToAdd, errorMessage)) {
- if (d->calculateComponentsAndRun()) {
- qCDebug(QInstaller::lcInstallerInstallLog)
- << "Created installer to:" << offlineBinaryName();
- }
- } else {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
- << "\nNo components available with the current selection.";
- }
- return status();
+ return d->fetchComponentsAndInstall(componentsToAdd);
}
/*!
@@ -3040,24 +3181,7 @@ PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently
return PackageManagerCore::Canceled;
}
}
-
- // init default model before fetching remote packages tree
- ComponentModel *model = defaultComponentModel();
- Q_UNUSED(model);
- if (!fetchRemotePackagesTree())
- return status();
-
- QString errorMessage;
- if (checkComponentsForInstallation(components, errorMessage)) {
- if (!errorMessage.isEmpty())
- qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage;
- if (d->calculateComponentsAndRun())
- qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully";
- } else {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
- << "\nNo components available for installation with the current selection.";
- }
- return status();
+ return d->fetchComponentsAndInstall(components);
}
/*!
@@ -3146,12 +3270,22 @@ void PackageManagerCore::setCheckAvailableSpace(bool check)
}
/*!
- Checks available disk space if the feature is not explicitly disabled. Informative
- text about space status can be retrieved by passing \a message parameter. Returns
+ * Returns informative text about disk space status
+ */
+QString PackageManagerCore::availableSpaceMessage() const
+{
+ return m_availableSpaceMessage;
+}
+
+/*!
+ Checks available disk space if the feature is not explicitly disabled. Returns
\c true if there is sufficient free space on installation and temporary volumes.
+
+ \sa availableSpaceMessage()
*/
-bool PackageManagerCore::checkAvailableSpace(QString &message) const
+bool PackageManagerCore::checkAvailableSpace()
{
+ m_availableSpaceMessage.clear();
const quint64 extraSpace = 256 * 1024 * 1024LL;
quint64 required(requiredDiskSpace());
quint64 tempRequired(requiredTemporaryDiskSpace());
@@ -3207,21 +3341,21 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
}
if (cacheOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
- message = tr("Not enough disk space to store temporary files and the "
+ m_availableSpaceMessage = tr("Not enough disk space to store temporary files and the "
"installation. %1 are available, while the minimum required is %2.").arg(
humanReadableSize(installVolumeAvailableSize), humanReadableSize(required + tempRequired));
return false;
}
if (installVolumeAvailableSize < required) {
- message = tr("Not enough disk space to store all selected components! %1 are "
+ m_availableSpaceMessage = tr("Not enough disk space to store all selected components! %1 are "
"available, while the minimum required is %2.").arg(humanReadableSize(installVolumeAvailableSize),
humanReadableSize(required));
return false;
}
if (cacheVolumeAvailableSize < tempRequired) {
- message = tr("Not enough disk space to store temporary files! %1 are available, "
+ m_availableSpaceMessage = tr("Not enough disk space to store temporary files! %1 are available, "
"while the minimum required is %2. You may select another location for the "
"temporary files by modifying the local cache path from the installer settings.")
.arg(humanReadableSize(cacheVolumeAvailableSize), humanReadableSize(tempRequired));
@@ -3230,22 +3364,22 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) {
// warn for less than 1% of the volume's space being free
- message = tr("The volume you selected for installation seems to have sufficient space for "
+ m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient space for "
"installation, but there will be less than 1% of the volume's space available afterwards.");
} else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) {
// warn for less than 100MB being free
- message = tr("The volume you selected for installation seems to have sufficient "
+ m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient "
"space for installation, but there will be less than 100 MB available afterwards.");
}
#ifdef Q_OS_WIN
if (isOfflineGenerator() && (required > UINT_MAX)) {
- message = tr("The estimated installer size %1 would exceed the supported executable "
+ m_availableSpaceMessage = tr("The estimated installer size %1 would exceed the supported executable "
"size limit of %2. The application may not be able to run.")
.arg(humanReadableSize(required), humanReadableSize(UINT_MAX));
}
#endif
}
- message = QString::fromLatin1("%1 %2").arg(message,
+ m_availableSpaceMessage = QString::fromLatin1("%1 %2").arg(m_availableSpaceMessage,
(isOfflineGenerator()
? tr("Created installer will use %1 of disk space.")
: tr("Installation will use %1 of disk space."))
@@ -4168,13 +4302,6 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
component->setUninstalled();
const QString localPath = component->localTempPath();
- if (LoggingHandler::instance().verboseLevel() == LoggingHandler::Detailed) {
- static QString lastLocalPath;
- if (lastLocalPath != localPath)
- qCDebug(QInstaller::lcDeveloperBuild()) << "Url is:" << localPath;
- lastLocalPath = localPath;
- }
-
const Repository repo = d->m_metadataJob.repositoryForCacheDirectory(localPath);
if (repo.isValid()) {
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index e0c27d6e3..a6d16add7 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -53,6 +53,7 @@ class ScriptEngine;
class PackageManagerCorePrivate;
class PackageManagerProxyFactory;
class Settings;
+class ComponentSortFilterProxyModel;
// -- PackageManagerCore
@@ -83,7 +84,8 @@ public:
Canceled = 3,
Unfinished = 4,
ForceUpdate = 5,
- EssentialUpdated = 6
+ EssentialUpdated = 6,
+ NoPackagesFound = 7
};
Status status() const;
QString error() const;
@@ -148,8 +150,9 @@ public:
void setProxyFactory(PackageManagerProxyFactory *factory);
PackagesList remotePackages();
- bool fetchRemotePackagesTree();
+ bool fetchRemotePackagesTree(const QStringList& components = QStringList());
bool fetchCompressedPackagesTree();
+ bool fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched);
bool run();
void reset();
@@ -210,6 +213,15 @@ public:
Q_INVOKABLE void addUserRepositories(const QStringList &repositories);
Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories,
bool replace = false, bool compressed = false);
+ bool addQBspRepositories(const QStringList &repositories);
+ bool validRepositoriesAvailable() const;
+ Q_INVOKABLE void setAllowCompressedRepositoryInstall(bool allow);
+ bool allowCompressedRepositoryInstall() const;
+ bool showRepositoryCategories() const;
+ QVariantMap organizedRepositoryCategories() const;
+ void enableRepositoryCategory(const QString &repositoryName, bool enable);
+ void runProgram();
+
Q_INVOKABLE void autoAcceptMessageBoxes();
Q_INVOKABLE void autoRejectMessageBoxes();
Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button);
@@ -274,6 +286,7 @@ public:
ComponentModel *defaultComponentModel() const;
ComponentModel *updaterComponentModel() const;
+ ComponentSortFilterProxyModel *componentSortFilterProxyModel();
void listInstalledPackages(const QString &regexp = QString());
bool listAvailablePackages(const QString &regexp = QString(),
@@ -326,7 +339,8 @@ public:
Q_INVOKABLE bool hasAdminRights() const;
void setCheckAvailableSpace(bool check);
- bool checkAvailableSpace(QString &message) const;
+ bool checkAvailableSpace();
+ QString availableSpaceMessage() const;
Q_INVOKABLE quint64 requiredDiskSpace() const;
Q_INVOKABLE quint64 requiredTemporaryDiskSpace() const;
@@ -370,14 +384,20 @@ public:
void clearLicenses();
QHash<QString, QMap<QString, QString>> sortedLicenses();
void addLicenseItem(const QHash<QString, QVariantMap> &licenses);
+ bool hasLicenses() const;
void createLocalDependencyHash(const QString &component, const QString &dependencies) const;
void createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies) const;
bool resetLocalCache(bool init = false);
bool clearLocalCache(QString *error = nullptr);
+ bool isValidCache() const;
+
template <typename T>
bool loadComponentScripts(const T &components, const bool postScript = false);
+ void saveGivenArguments(const QStringList &args);
+ QStringList givenArguments() const;
+
public Q_SLOTS:
bool runInstaller();
bool runUninstaller();
@@ -397,8 +417,6 @@ Q_SIGNALS:
void aboutCalculateComponentsToUninstall() const;
void finishedCalculateComponentsToUninstall() const;
void componentAdded(QInstaller::Component *comp);
- void rootComponentsAdded(QList<QInstaller::Component*> components);
- void updaterComponentsAdded(QList<QInstaller::Component*> components);
void valueChanged(const QString &key, const QString &value);
void statusChanged(QInstaller::PackageManagerCore::Status);
void defaultTranslationsLoadedForLanguage(QLocale::Language lang);
@@ -467,13 +485,15 @@ private:
bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesMap installedPackages);
bool componentUninstallableFromCommandLine(const QString &componentName);
- bool checkComponentsForInstallation(const QStringList &names, QString &errorMessage);
+ bool checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound);
private:
PackageManagerCorePrivate *const d;
friend class PackageManagerCorePrivate;
QHash<QString, QString> m_fileDialogAutomaticAnswers;
QHash<QString, QStringList> m_localVirtualWithDependants;
+ QString m_availableSpaceMessage;
+ QStringList m_arguments;
private:
// remove once we deprecate isSelected, setSelected etc...
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 7ccfe2b1c..858698a7a 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -57,11 +57,13 @@
#include "selfrestarter.h"
#include "filedownloaderfactory.h"
#include "updateoperationfactory.h"
+#include "constants.h"
#include <productkeycheck.h>
#include <QSettings>
#include <QtConcurrentRun>
+#include <QtConcurrent>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
@@ -152,6 +154,27 @@ static void deferredRename(const QString &oldName, const QString &newName, bool
#endif
}
+static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->name() == component);
+ if (packageFound)
+ break;
+ }
+ return !packageFound;
+}
+
+static bool filterMissingPackagesToInstall(const QString& component, const PackagesList& packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->data(scName).toString() == component);
+ if (packageFound)
+ break;
+ }
+ return !packageFound;
+}
// -- PackageManagerCorePrivate
@@ -188,9 +211,16 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(nullptr)
, m_datFileName(QString())
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
}
@@ -228,9 +258,16 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(new RemoteFileEngineHandler)
, m_datFileName(datFileName)
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
foreach (const OperationBlob &operation, performedOperations) {
std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
@@ -1173,8 +1210,11 @@ void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const ope
connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation()));
if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) {
+ // create unique object names for progress information track
+ operationObject->setObjectName(QLatin1String("operation_%1").arg(QString::number(m_connectedOperations)));
ProgressCoordinator::instance()->registerPartProgress(operationObject,
SIGNAL(progressChanged(double)), operationPartSize);
+ m_connectedOperations++;
}
}
}
@@ -2603,6 +2643,56 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
+PackageManagerCore::Status PackageManagerCorePrivate::fetchComponentsAndInstall(const QStringList& components)
+{
+ // init default model before fetching remote packages tree
+ ComponentModel *model = m_core->defaultComponentModel();
+ Q_UNUSED(model);
+
+ bool fallbackReposFetched = false;
+ auto fetchComponents = [&]() {
+ bool packagesFound = m_core->fetchPackagesWithFallbackRepositories(components, fallbackReposFetched);
+
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available with the current selection.";
+ setStatus(PackageManagerCore::Canceled);
+ return false;
+ }
+ QString errorMessage;
+ bool unstableAliasFound = false;
+ if (m_core->checkComponentsForInstallation(components, errorMessage, unstableAliasFound)) {
+ if (!errorMessage.isEmpty())
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage;
+ if (calculateComponentsAndRun()) {
+ if (m_core->isOfflineGenerator())
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Created installer to:" << offlineBinaryName();
+ else
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully";
+ }
+ } else {
+ // We found unstable alias and all repos were not fetched. Alias might have dependency to component
+ // which exists in non-default repository. Fetch all repositories now.
+ if (unstableAliasFound && !fallbackReposFetched) {
+ return false;
+ }
+ else {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
+ << "No components available with the current selection.";
+ }
+ }
+ return true;
+ };
+
+ if (!fetchComponents() && !fallbackReposFetched) {
+ setStatus(PackageManagerCore::Running);
+ enableAllCategories();
+ fetchComponents();
+ }
+
+ return m_core->status();
+}
+
void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state)
{
ComponentModel *model = m_core->isUpdater() ? m_core->updaterComponentModel() : m_core->defaultComponentModel();
@@ -2720,9 +2810,12 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
settings.setValue(QLatin1String("Comments"), m_data.value(scTitle));
settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString());
settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir()));
- settings.setValue(QLatin1String("UninstallString"), quoted(maintenanceTool));
- settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool)
- + QLatin1String(" --manage-packages")));
+ settings.setValue(QLatin1String("UninstallString"), QString(quoted(maintenanceTool)
+ + QLatin1String(" --") + CommandLineOptions::scStartUninstallerLong));
+ if (!isOfflineOnly()) {
+ settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool)
+ + QLatin1String(" --") + CommandLineOptions::scStartPackageManagerLong));
+ }
// required disk space of the installed components
quint64 estimatedSizeKB = m_core->requiredDiskSpace() / 1024;
// add required space for the maintenance tool
@@ -3015,6 +3108,63 @@ void PackageManagerCorePrivate::updateComponentInstallActions()
component->setInstallAction(ComponentModelHelper::Install);
}
+bool PackageManagerCorePrivate::enableAllCategories()
+{
+ QSet<RepositoryCategory> repoCategories = m_data.settings().repositoryCategories();
+ bool additionalRepositoriesEnabled = false;
+ for (const auto &category : repoCategories) {
+ if (!category.isEnabled()) {
+ additionalRepositoriesEnabled = true;
+ enableRepositoryCategory(category, true);
+ }
+ }
+ return additionalRepositoriesEnabled;
+}
+
+void PackageManagerCorePrivate::enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable)
+{
+ RepositoryCategory replacement = repoCategory;
+ replacement.setEnabled(enable);
+ QSet<RepositoryCategory> tmpRepoCategories = m_data.settings().repositoryCategories();
+ if (tmpRepoCategories.contains(repoCategory)) {
+ tmpRepoCategories.remove(repoCategory);
+ tmpRepoCategories.insert(replacement);
+ m_data.settings().addRepositoryCategories(tmpRepoCategories);
+ }
+}
+
+bool PackageManagerCorePrivate::installablePackagesFound(const QStringList& components)
+{
+ if (components.isEmpty())
+ return true;
+
+ PackagesList remotes = remotePackages();
+
+ auto componentsNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [remotes](const QString& installerPackage) {
+ return filterMissingPackagesToInstall(installerPackage, remotes);
+ }
+ );
+
+ if (componentsNotFoundForInstall.count() > 0) {
+ QList<ComponentAlias *> aliasList = componentAliases();
+ auto aliasesNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [aliasList](const QString& installerPackage) {
+ return filterMissingAliasesToInstall(installerPackage, aliasList);
+ }
+ );
+
+ if (aliasesNotFoundForInstall.count() > 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot install " << aliasesNotFoundForInstall.join(QLatin1String(", ")) << ". Component(s) not found.";
+ setStatus(PackageManagerCore::NoPackagesFound);
+ return false;
+ }
+ }
+ return true;
+}
+
void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
{
QObject *const operationObject = dynamic_cast<QObject *> (operation);
@@ -3126,9 +3276,8 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun()
qCDebug(QInstaller::lcInstallerInstallLog).noquote()
<< htmlToString(m_core->componentResolveReasons());
- QString spaceInfo;
- const bool spaceOk = m_core->checkAvailableSpace(spaceInfo);
- qCDebug(QInstaller::lcInstallerInstallLog) << spaceInfo;
+ const bool spaceOk = m_core->checkAvailableSpace();
+ qCDebug(QInstaller::lcInstallerInstallLog) << m_core->availableSpaceMessage();
if (!spaceOk || !(m_autoConfirmCommand || askUserConfirmCommand())) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation aborted.";
@@ -3155,6 +3304,13 @@ bool PackageManagerCorePrivate::acceptLicenseAgreements() const
m_core->addLicenseItem(component->licenses());
}
+ const QString acceptanceText = ProductKeyCheck::instance()->licenseAcceptanceText();
+ if (!acceptanceText.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << acceptanceText;
+ if (!m_autoAcceptLicenses && !acceptRejectCliQuery())
+ return false;
+ }
+
QHash<QString, QMap<QString, QString>> priorityHash = m_core->sortedLicenses();
QStringList priorities = priorityHash.keys();
priorities.sort();
@@ -3203,6 +3359,23 @@ bool PackageManagerCorePrivate::askUserAcceptLicense(const QString &name, const
}
}
+bool PackageManagerCorePrivate::acceptRejectCliQuery() const
+{
+ forever {
+ const QString input = m_core->readConsoleLine(QLatin1String("Accept|Reject"));
+
+ if (QString::compare(input, QLatin1String("Accept"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("A"), Qt::CaseInsensitive) == 0) {
+ return true;
+ } else if (QString::compare(input, QLatin1String("Reject"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("R"), Qt::CaseInsensitive) == 0) {
+ return false;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input;
+ }
+ }
+}
+
bool PackageManagerCorePrivate::askUserConfirmCommand() const
{
qCDebug(QInstaller::lcInstallerInstallLog) << "Do you want to continue?";
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index 22b3ca9f3..c0c55c4cc 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -62,6 +62,7 @@ class ComponentAlias;
class InstallerCalculator;
class UninstallerCalculator;
class RemoteFileEngineHandler;
+class ComponentSortFilterProxyModel;
class PackageManagerCorePrivate : public QObject
{
@@ -176,6 +177,7 @@ public:
void unpackComponents(const QList<Component *> &components, double progressOperationSize);
void installComponent(Component *component, double progressOperationSize);
+ PackageManagerCore::Status fetchComponentsAndInstall(const QStringList& components);
void setComponentSelection(const QString &id, Qt::CheckState state);
@@ -274,6 +276,7 @@ private:
bool calculateComponentsAndRun();
bool acceptLicenseAgreements() const;
bool askUserAcceptLicense(const QString &name, const QString &content) const;
+ bool acceptRejectCliQuery() const;
bool askUserConfirmCommand() const;
bool packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const;
void commitPendingUnstableComponents();
@@ -281,6 +284,11 @@ private:
void createLocalDependencyHash(const QString &componentName, const QString &dependencies);
void updateComponentInstallActions();
+ bool enableAllCategories();
+ void enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable);
+
+ bool installablePackagesFound(const QStringList& components);
+
// remove once we deprecate isSelected, setSelected etc...
void restoreCheckState();
void storeCheckState();
@@ -314,6 +322,7 @@ private:
ComponentModel *m_defaultModel;
ComponentModel *m_updaterModel;
+ ComponentSortFilterProxyModel *m_componentSortFilterProxyModel;
QObject *m_guiObject;
QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler;
@@ -331,6 +340,8 @@ private:
QHash<QString, QStringList > m_componentReplaces;
QString m_datFileName;
+ bool m_allowCompressedRepositoryInstall;
+ int m_connectedOperations;
};
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp
index 80b79bf8a..1113908bd 100644
--- a/src/libs/installer/packagemanagercoredata.cpp
+++ b/src/libs/installer/packagemanagercoredata.cpp
@@ -96,7 +96,7 @@ PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &va
if (isInstaller) {
addNewVariable(scTargetDir, replaceVariables(m_settings.targetDir()));
addNewVariable(scTargetConfigurationFile, m_settings.configurationFileName());
- addNewVariable(scStartMenuDir, m_settings.startMenuDir());
+ addNewVariable(scStartMenuDir, replaceVariables(m_settings.startMenuDir()));
} else {
#ifdef Q_OS_MACOS
addNewVariable(scTargetDir, QFileInfo(QCoreApplication::applicationDirPath() + QLatin1String("/../../..")).absoluteFilePath());
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index dd3a30a6e..1f0462eea 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -692,6 +692,22 @@ bool PackageManagerGui::isButtonEnabled(int wb)
}
/*!
+ Sets \a buttonText for button specified by \a buttonId to a installer page \a pageId.
+
+ \note In some pages, installer will change the button text when entering
+ the page. In that case, you need to connect to \c entered() -signal of the
+ page to change the \a buttonText.
+
+ \sa {gui::setWizardPageButtonText}{gui.setWizardPageButtonText}
+*/
+void PackageManagerGui::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText)
+{
+ PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId));
+ if (p)
+ p->setButtonText(static_cast<QWizard::WizardButton>(buttonId), buttonText);
+}
+
+/*!
Sets a validator for the custom page specified by \a name and
\a callbackName requested by \a component.
*/
@@ -1459,16 +1475,8 @@ int PackageManagerPage::nextId() const
if (core->isUninstaller())
return nextNextId; // forcibly hide the license page if we run as uninstaller
core->recalculateAllComponents();
-
- foreach (Component* component, core->orderedComponentsToInstall()) {
- if (core->isMaintainer() && component->isInstalled())
- continue; // package manager or updater, hide as long as the component is installed
-
- // The component is about to be installed and provides a license, so the page needs to
- // be shown.
- if (!component->licenses().isEmpty())
- return next;
- }
+ if (core->hasLicenses())
+ return next;
return nextNextId; // no component with a license or all components with license installed
}
return next; // default, show the next page
@@ -1555,6 +1563,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_errorLabel = new QLabel(this);
m_errorLabel->setWordWrap(true);
+ m_errorLabel->setTextFormat(Qt::RichText);
+ m_errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
boxLayout->addWidget(m_errorLabel);
m_errorLabel->setObjectName(QLatin1String("ErrorLabel"));
@@ -1611,7 +1621,7 @@ bool IntroductionPage::validatePage()
bool isOfflineOnlyInstaller = core->isInstaller() && core->isOfflineOnly();
// If not offline only installer, at least one valid repository needs to be available
- if (!isOfflineOnlyInstaller && !validRepositoriesAvailable()) {
+ if (!isOfflineOnlyInstaller && !core->validRepositoriesAvailable()) {
setErrorMessage(QLatin1String("<font color=\"red\">") + tr("At least one valid and enabled "
"repository required for this action to succeed.") + QLatin1String("</font>"));
return isComplete();
@@ -1828,22 +1838,6 @@ void IntroductionPage::setErrorMessage(const QString &error)
#endif
}
-/*!
- Returns \c true if at least one valid and enabled repository is available.
-*/
-bool IntroductionPage::validRepositoriesAvailable() const
-{
- const PackageManagerCore *const core = packageManagerCore();
- bool valid = false;
-
- foreach (const Repository &repo, core->settings().repositories()) {
- if (repo.isEnabled() && repo.isValid()) {
- valid = true;
- break;
- }
- }
- return valid;
-}
// -- private slots
@@ -1887,9 +1881,9 @@ void IntroductionPage::setPackageManager(bool value)
*/
void IntroductionPage::initializePage()
{
- const bool repositoriesAvailable = validRepositoriesAvailable();
-
PackageManagerCore *core = packageManagerCore();
+ const bool repositoriesAvailable = core->validRepositoriesAvailable();
+
if (core->isPackageManager()) {
m_packageManager->setChecked(true);
} else if (core->isUpdater()) {
@@ -1922,7 +1916,7 @@ void IntroductionPage::onCoreNetworkSettingsChanged()
PackageManagerCore *core = packageManagerCore();
if (core->isUninstaller() || core->isMaintainer()) {
- m_offlineMaintenanceTool = !validRepositoriesAvailable();
+ m_offlineMaintenanceTool = !core->validRepositoriesAvailable();
setMaintainerToolsEnabled(!m_offlineMaintenanceTool);
m_removeAllComponents->setChecked(m_offlineMaintenanceTool);
@@ -2112,7 +2106,7 @@ void LicenseAgreementPage::entering()
*/
bool LicenseAgreementPage::isComplete() const
{
- return m_acceptCheckBox->isChecked();
+ return m_acceptCheckBox->isChecked() && ProductKeyCheck::instance()->hasAcceptedAllLicenses();
}
void LicenseAgreementPage::openLicenseUrl(const QUrl &url)
@@ -2205,7 +2199,7 @@ void ComponentSelectionPage::entering()
QT_TR_NOOP("Please select the components you want to update."),
QT_TR_NOOP("Please select the components you want to install."),
QT_TR_NOOP("Please select the components you want to uninstall."),
- QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated."),
+ QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.<br>Any components already installed will not be updated."),
QT_TR_NOOP("Mandatory components need to be updated first before you can select other components to update.")
};
@@ -2223,13 +2217,7 @@ void ComponentSelectionPage::entering()
d->onModelStateChanged(d->m_currentModel->checkedState());
setModified(isComplete());
- if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly()
- && !core->isUpdater()) {
- d->showCategoryLayout(true);
- core->settings().setAllowUnstableComponents(true);
- } else {
- d->showCategoryLayout(false);
- }
+ d->showCategoryLayout(core->showRepositoryCategories());
d->showCompressedRepositoryButton();
d->showCreateOfflineInstallerButton(true);
@@ -2329,16 +2317,6 @@ void ComponentSelectionPage::deselectComponent(const QString &id)
}
/*!
- Adds the possibility to install a compressed repository on component selection
- page. A new button which opens a file browser is added for compressed
- repository selection.
-*/
-void ComponentSelectionPage::allowCompressedRepositoryInstall()
-{
- d->allowCompressedRepositoryInstall();
-}
-
-/*!
Adds an additional virtual component with the \a name to be installed.
Returns \c true if the virtual component is found and not installed.
@@ -2376,18 +2354,7 @@ bool ComponentSelectionPage::isComplete() const
if (!d->componentsResolved())
return false;
- if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater())
- return d->m_currentModel->checked().count();
-
- if (d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false)
- return true;
-
- const QSet<Component *> uncheckable = d->m_currentModel->uncheckable();
- for (auto &component : uncheckable) {
- if (component->forcedInstallation() && !component->isInstalled())
- return true; // allow installation for new forced components
- }
- return false;
+ return d->m_currentModel->componentsSelected();
}
@@ -2745,11 +2712,10 @@ void ReadyForInstallationPage::entering()
m_taskDetailsBrowser->setVisible(!componentsOk || LoggingHandler::instance().isVerbose());
setComplete(componentsOk);
- QString spaceInfo;
- if (packageManagerCore()->checkAvailableSpace(spaceInfo)) {
- m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), spaceInfo));
+ if (packageManagerCore()->checkAvailableSpace()) {
+ m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), packageManagerCore()->availableSpaceMessage()));
} else {
- m_msgLabel->setText(spaceInfo);
+ m_msgLabel->setText(packageManagerCore()->availableSpaceMessage());
setComplete(false);
}
}
@@ -3156,16 +3122,8 @@ void FinishedPage::leaving()
*/
void FinishedPage::handleFinishClicked()
{
- const QString program =
- packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram));
-
- const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore()
- ->values(scRunProgramArguments));
- if (!m_runItCheckBox->isChecked() || program.isEmpty())
- return;
-
- qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args;
- QProcess::startDetached(program, args);
+ if (m_runItCheckBox->isChecked())
+ packageManagerCore()->runProgram();
}
/*!
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index 655dd9875..d83643005 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -82,6 +82,7 @@ public:
void clickButton(int wizardButton, int delayInMs = 0);
void clickButton(const QString &objectName, int delayInMs = 0) const;
bool isButtonEnabled(int wizardButton);
+ void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText);
void showSettingsButton(bool show);
void requestSettingsButtonByInstaller(bool request);
@@ -261,7 +262,6 @@ private:
void leaving() override;
void showWidgets(bool show);
- bool validRepositoriesAvailable() const;
private:
bool m_updatesFetched;
@@ -330,7 +330,6 @@ public:
Q_INVOKABLE void selectDefault();
Q_INVOKABLE void selectComponent(const QString &id);
Q_INVOKABLE void deselectComponent(const QString &id);
- Q_INVOKABLE void allowCompressedRepositoryInstall();
Q_INVOKABLE bool addVirtualComponentToUninstall(const QString &name);
void setAllowCreateOfflineInstaller(bool allow);
diff --git a/src/libs/installer/packagesource.cpp b/src/libs/installer/packagesource.cpp
index 5bc61f8e4..0f87e0def 100644
--- a/src/libs/installer/packagesource.cpp
+++ b/src/libs/installer/packagesource.cpp
@@ -50,7 +50,7 @@ namespace QInstaller {
*/
/*!
- \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p)
+ \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p, bool pl)
Constructs a package source info object. The object's url is set to \a u, while the priority
is set to \a p.
diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp
index 4911d226e..ed128fa61 100644
--- a/src/libs/installer/productkeycheck.cpp
+++ b/src/libs/installer/productkeycheck.cpp
@@ -114,3 +114,22 @@ bool ProductKeyCheck::hasValidLicense() const
{
return true;
}
+
+bool ProductKeyCheck::hasAcceptedAllLicenses() const
+{
+ return true;
+}
+
+QString ProductKeyCheck::licenseAcceptanceText() const
+{
+ return QString();
+}
+QString ProductKeyCheck::securityWarning() const
+{
+ return QString();
+}
+
+QString ProductKeyCheck::additionalMetaDownloadWarning() const
+{
+ return QString();
+}
diff --git a/src/libs/installer/productkeycheck.h b/src/libs/installer/productkeycheck.h
index 2e6a5f2e6..8e7d6724f 100644
--- a/src/libs/installer/productkeycheck.h
+++ b/src/libs/installer/productkeycheck.h
@@ -70,6 +70,10 @@ public:
QList<int> registeredPages() const;
bool hasValidLicense() const;
+ bool hasAcceptedAllLicenses() const;
+ QString licenseAcceptanceText() const;
+ QString securityWarning() const;
+ QString additionalMetaDownloadWarning() const;
private:
ProductKeyCheck();
diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp
index afcec1908..6413efe28 100644
--- a/src/libs/installer/progresscoordinator.cpp
+++ b/src/libs/installer/progresscoordinator.cpp
@@ -76,7 +76,8 @@ ProgressCoordinator *ProgressCoordinator::instance()
void ProgressCoordinator::reset()
{
- disconnectAllSenders();
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
m_installationLabelText.clear();
m_currentCompletePercentage = 0;
m_currentBasePercentage = 0;
@@ -90,10 +91,11 @@ void ProgressCoordinator::reset()
void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize)
{
Q_ASSERT(sender);
+ Q_ASSERT(!sender->objectName().isEmpty());
Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)")));
Q_ASSERT(partProgressSize <= 1);
- m_senderPartProgressSizeHash.insert(sender, partProgressSize);
+ m_senderPartProgressSizeHash.insert(sender->objectName(), partProgressSize);
bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double)));
Q_UNUSED(isConnected);
Q_ASSERT(isConnected);
@@ -116,16 +118,17 @@ void ProgressCoordinator::partProgressChanged(double fraction)
}
// no fraction no change
- if (fraction == 0)
+ if (fraction == 0 || !sender())
return;
+ QString senderObjectName = sender()->objectName();
// ignore senders sending 100% multiple times
- if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender())
- && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) {
+ if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(senderObjectName)
+ && m_senderPendingCalculatedPercentageHash.value(senderObjectName) == 0) {
return;
}
- double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0);
+ double partProgressSize = m_senderPartProgressSizeHash.value(senderObjectName, 0);
if (partProgressSize == 0) {
qCWarning(QInstaller::lcInstallerInstallLog) << "It seems that this sender was not registered "
"in the right way:" << sender();
@@ -138,7 +141,7 @@ void ProgressCoordinator::partProgressChanged(double fraction)
// allPendingCalculatedPartPercentages has negative values
double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage
- + allPendingCalculatedPartPercentages(sender());
+ + allPendingCalculatedPartPercentages(senderObjectName);
//we can't check this here, because some round issues can make it little bit under 0 or over 100
//Q_ASSERT(newCurrentCompletePercentage >= 0);
@@ -163,9 +166,9 @@ void ProgressCoordinator::partProgressChanged(double fraction)
m_currentCompletePercentage = newCurrentCompletePercentage;
if (fraction == 1) {
m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage;
- m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0);
} else {
- m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage);
}
} else { //if (m_undoMode)
@@ -174,7 +177,7 @@ void ProgressCoordinator::partProgressChanged(double fraction)
//double checkValue = allPendingCalculatedPartPercentages(sender());
double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage
- + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender());
+ + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(senderObjectName);
//we can't check this here, because some round issues can make it little bit under 0 or over 100
//Q_ASSERT(newCurrentCompletePercentage >= 0);
@@ -199,9 +202,9 @@ void ProgressCoordinator::partProgressChanged(double fraction)
if (fraction == 1) {
m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage;
- m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0);
} else {
- m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage);
}
} //if (m_undoMode)
printProgressPercentage(progressInPercentage());
@@ -219,25 +222,13 @@ int ProgressCoordinator::progressInPercentage() const
return currentValue;
}
-void ProgressCoordinator::disconnectAllSenders()
-{
- foreach (QPointer<QObject> sender, m_senderPartProgressSizeHash.keys()) {
- if (!sender.isNull()) {
- bool isDisconnected = sender->disconnect(this);
- Q_UNUSED(isDisconnected);
- Q_ASSERT(isDisconnected);
- }
- }
- m_senderPartProgressSizeHash.clear();
- m_senderPendingCalculatedPercentageHash.clear();
-}
-
void ProgressCoordinator::setUndoMode()
{
Q_ASSERT(!m_undoMode);
m_undoMode = true;
- disconnectAllSenders();
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
m_reachedPercentageBeforeUndo = progressInPercentage();
m_currentBasePercentage = m_reachedPercentageBeforeUndo;
}
@@ -294,10 +285,10 @@ void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text)
qApp->processEvents(); //makes the result available in the ui
}
-double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject)
+double ProgressCoordinator::allPendingCalculatedPartPercentages(const QString &excludeKeyObject)
{
double result = 0;
- QHash<QPointer<QObject>, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
+ QHash<QString, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
while (it != m_senderPendingCalculatedPercentageHash.end()) {
if (it.key() != excludeKeyObject)
result += it.value();
diff --git a/src/libs/installer/progresscoordinator.h b/src/libs/installer/progresscoordinator.h
index 3540b5d16..75d5a5d30 100644
--- a/src/libs/installer/progresscoordinator.h
+++ b/src/libs/installer/progresscoordinator.h
@@ -85,12 +85,12 @@ protected:
explicit ProgressCoordinator(QObject *parent);
private:
- double allPendingCalculatedPartPercentages(QObject *excludeKeyObject = 0);
+ double allPendingCalculatedPartPercentages(const QString &excludeKeyObject = QString());
void disconnectAllSenders();
private:
- QHash<QPointer<QObject>, double> m_senderPendingCalculatedPercentageHash;
- QHash<QPointer<QObject>, double> m_senderPartProgressSizeHash;
+ QHash<QString, double> m_senderPendingCalculatedPercentageHash;
+ QHash<QString, double> m_senderPartProgressSizeHash;
ProgressSpinner *m_progressSpinner;
QString m_installationLabelText;
double m_currentCompletePercentage;
diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp
index 1754b664d..852714dfb 100644
--- a/src/libs/installer/registerfiletypeoperation.cpp
+++ b/src/libs/installer/registerfiletypeoperation.cpp
@@ -153,7 +153,7 @@ bool RegisterFileTypeOperation::undoOperation()
{
#ifdef Q_OS_WIN
ensureOptionalArgumentsRead();
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
QStringList args = arguments();
diff --git a/src/libs/installer/resources/installer.qrc b/src/libs/installer/resources/installer.qrc
index 48a7c65bd..a3855b5c4 100644
--- a/src/libs/installer/resources/installer.qrc
+++ b/src/libs/installer/resources/installer.qrc
@@ -7,5 +7,6 @@
<file>uninstall.png</file>
<file>keepinstalled.png</file>
<file>keepuninstalled.png</file>
+ <file>qt/etc/qt.conf</file>
</qresource>
</RCC>
diff --git a/src/libs/installer/resources/qt/etc/qt.conf b/src/libs/installer/resources/qt/etc/qt.conf
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/libs/installer/resources/qt/etc/qt.conf
diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index 602f88b61..7e3e69eb2 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -189,6 +189,12 @@ bool GuiProxy::isButtonEnabled(int wizardButton)
return m_gui->isButtonEnabled(wizardButton);
}
+void GuiProxy::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText)
+{
+ if (m_gui)
+ m_gui->setWizardPageButtonText(pageId, buttonId, buttonText);
+}
+
void GuiProxy::showSettingsButton(bool show)
{
if (m_gui)
@@ -618,14 +624,19 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths)
SETPROPERTY(desktopServices, TempLocation, QStandardPaths)
SETPROPERTY(desktopServices, HomeLocation, QStandardPaths)
- SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppLocalDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, CacheLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, RuntimeLocation, QStandardPaths)
SETPROPERTY(desktopServices, ConfigLocation, QStandardPaths)
SETPROPERTY(desktopServices, DownloadLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericConfigLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppConfigLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, PublicShareLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, TemplatesLocation, QStandardPaths)
QJSValue object = m_engine.newQObject(new QDesktopServicesProxy(this));
object.setPrototype(desktopServices); // attach the properties
diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h
index 035b68be4..101d4f303 100644
--- a/src/libs/installer/scriptengine_p.h
+++ b/src/libs/installer/scriptengine_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -116,6 +116,7 @@ public:
Q_INVOKABLE void clickButton(int wizardButton, int delayInMs = 0);
Q_INVOKABLE void clickButton(const QString &objectName, int delayInMs = 0) const;
Q_INVOKABLE bool isButtonEnabled(int wizardButton);
+ Q_INVOKABLE void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText);
Q_INVOKABLE void showSettingsButton(bool show);
Q_INVOKABLE void setSettingsButtonEnabled(bool enable);
diff --git a/src/libs/installer/simplemovefileoperation.cpp b/src/libs/installer/simplemovefileoperation.cpp
index 5f3000be0..5bbbdabb4 100644
--- a/src/libs/installer/simplemovefileoperation.cpp
+++ b/src/libs/installer/simplemovefileoperation.cpp
@@ -93,7 +93,7 @@ bool SimpleMoveFileOperation::performOperation()
bool SimpleMoveFileOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
const QString source = arguments().at(0);
const QString target = arguments().at(1);
diff --git a/src/libs/installer/sysinfo_win.cpp b/src/libs/installer/sysinfo_win.cpp
index e74eb1d4e..e5df8e35f 100644
--- a/src/libs/installer/sysinfo_win.cpp
+++ b/src/libs/installer/sysinfo_win.cpp
@@ -64,7 +64,7 @@ VolumeInfo updateVolumeSizeInformation(const VolumeInfo &info)
return update;
}
-/*!
+/*
Returns a list of volume info objects that are mounted as network drive shares.
*/
QList<VolumeInfo> networkVolumeInfosFromMountPoints()
@@ -95,7 +95,7 @@ QList<VolumeInfo> networkVolumeInfosFromMountPoints()
return volumes;
}
-/*!
+/*
Returns a list of volume info objects based on the given \a volumeGUID. The function also solves mounted
volume folder paths. It does not return any network drive shares.
*/
diff --git a/src/libs/installer/systeminfo.cpp b/src/libs/installer/systeminfo.cpp
index 0b9b00500..aed02c569 100644
--- a/src/libs/installer/systeminfo.cpp
+++ b/src/libs/installer/systeminfo.cpp
@@ -63,7 +63,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent)
OS running on a 64-bit CPU is usually unable to determine whether the CPU is actually capable
of running 64-bit programs.
- \sa QSysInfo::currentCpuArchitecture() \sa buildCpuArchitecture()
+ \sa QSysInfo::currentCpuArchitecture() buildCpuArchitecture()
*/
QString SystemInfo::currentCpuArchitecture() const
{
@@ -86,7 +86,7 @@ QString SystemInfo::currentCpuArchitecture() const
there's an emulation layer or if the CPU supports multiple architectures (like x86-64
processors supporting i386 applications). To detect that, use \c installer.currentCpuArchitecture()
- \sa QSysInfo::buildCpuArchitecture() \sa currentCpuArchitecture()
+ \sa QSysInfo::buildCpuArchitecture() currentCpuArchitecture()
*/
QString SystemInfo::buildCpuArchitecture() const
{
@@ -148,7 +148,7 @@ QString SystemInfo::kernelVersion() const
\list
\li "windows"
\li "opensuse" (for the Linux openSUSE distribution)
- \li "osx"
+ \li "macos"
\endlist
\sa QSysInfo::productType()
diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp
index e9e69fc13..af89382a8 100644
--- a/src/libs/kdtools/updateoperation.cpp
+++ b/src/libs/kdtools/updateoperation.cpp
@@ -292,23 +292,18 @@ QStringList UpdateOperation::parsePerformOperationArguments()
}
/*!
- Returns undo operation argument list. If the installation is
- cancelled or failed, returns an empty list so that full undo
- operation can be performed.
+ Returns \c true if operation undo should not be performed.
+ Returns \c false if the installation is cancelled or failed, or
+ \c UNDOOPERATION is not set in operation call.
*/
-QStringList UpdateOperation::parseUndoOperationArguments()
+bool UpdateOperation::skipUndoOperation()
{
//Install has failed, allow a normal undo
if (m_core && (m_core->status() == QInstaller::PackageManagerCore::Canceled
|| m_core->status() == QInstaller::PackageManagerCore::Failure)) {
- return QStringList();
- }
- int index = arguments().indexOf(QLatin1String("UNDOOPERATION"));
- QStringList args;
- if ((index != -1) && (arguments().length() > index + 1)) {
- args = arguments().mid(index + 1);
+ return false;
}
- return args;
+ return arguments().contains(QLatin1String("UNDOOPERATION"));
}
/*!
diff --git a/src/libs/kdtools/updateoperation.h b/src/libs/kdtools/updateoperation.h
index 4a431f107..e25846cd3 100644
--- a/src/libs/kdtools/updateoperation.h
+++ b/src/libs/kdtools/updateoperation.h
@@ -112,7 +112,7 @@ protected:
bool checkArgumentCount(int minArgCount, int maxArgCount, const QString &argDescription = QString());
bool checkArgumentCount(int argCount);
QStringList parsePerformOperationArguments();
- QStringList parseUndoOperationArguments();
+ bool skipUndoOperation();
void setRequiresUnreplacedVariables(bool isRequired);
bool variableReplacement(QString *variableValue);
diff --git a/src/libs/kdtools/updateoperations.cpp b/src/libs/kdtools/updateoperations.cpp
index 9301d4f13..5f6135103 100644
--- a/src/libs/kdtools/updateoperations.cpp
+++ b/src/libs/kdtools/updateoperations.cpp
@@ -127,7 +127,7 @@ QString CopyOperation::sourcePath()
QString CopyOperation::destinationPath()
{
- QString destination = arguments().last();
+ QString destination = arguments().at(1);
// if the target is a directory use the source filename to complete the destination path
if (QFileInfo(destination).isDir())
@@ -135,7 +135,6 @@ QString CopyOperation::destinationPath()
return destination;
}
-
void CopyOperation::backup()
{
QString destination = destinationPath();
@@ -156,8 +155,8 @@ void CopyOperation::backup()
bool CopyOperation::performOperation()
{
// We need two args to complete the copy operation. First arg provides the complete file name of source
- // Second arg provides the complete file name of dest
- if (!checkArgumentCount(2))
+ // Second arg provides the complete file name of dest. Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(2, 4, QLatin1String("<source filename> <destination filename> [UNDOOPERATION, \"\"]")))
return false;
QString source = sourcePath();
@@ -193,6 +192,8 @@ bool CopyOperation::performOperation()
bool CopyOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
QString source = sourcePath();
QString destination = destinationPath();
@@ -270,7 +271,7 @@ MoveOperation::~MoveOperation()
void MoveOperation::backup()
{
- const QString dest = arguments().last();
+ const QString dest = arguments().at(1);
if (!QFile::exists(dest)) {
clearValue(QLatin1String("backupOfExistingDestination"));
return;
@@ -286,9 +287,10 @@ void MoveOperation::backup()
bool MoveOperation::performOperation()
{
- // We need two args to complete the copy operation. // First arg provides the complete file name of
- // source, second arg provides the complete file name of dest
- if (!checkArgumentCount(2))
+ // We need two args to complete the copy operation. First arg provides the complete file name of
+ // source, second arg provides the complete file name of dest. Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(2, 4, QLatin1String("<complete source file name> <complete destination "
+ "file name> [UNDOOPERATION, \"\"]")))
return false;
const QStringList args = arguments();
@@ -318,8 +320,10 @@ bool MoveOperation::performOperation()
bool MoveOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
const QStringList args = arguments();
- const QString dest = args.last();
+ const QString dest = args.at(1);
// first: copy back the destination to source
QFile destF(dest);
if (!destF.copy(args.first())) {
@@ -391,7 +395,8 @@ void DeleteOperation::backup()
bool DeleteOperation::performOperation()
{
// Requires only one parameter. That is the name of the file to remove.
- if (!checkArgumentCount(1))
+ // Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]")))
return false;
return deleteFileNowOrLater(arguments().at(0));
@@ -399,7 +404,7 @@ bool DeleteOperation::performOperation()
bool DeleteOperation::undoOperation()
{
- if (!hasValue(QLatin1String("backupOfExistingFile")))
+ if (skipUndoOperation())
return true;
const QString fileName = arguments().first();
@@ -478,7 +483,8 @@ void MkdirOperation::backup()
bool MkdirOperation::performOperation()
{
// Requires only one parameter. That is the path which should be created
- if (!checkArgumentCount(1))
+ // Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]")))
return false;
const QString dirName = arguments().at(0);
@@ -493,7 +499,8 @@ bool MkdirOperation::performOperation()
bool MkdirOperation::undoOperation()
{
- Q_ASSERT(arguments().count() == 1);
+ if (skipUndoOperation())
+ return true;
QString createdDirValue = value(QLatin1String("createddir")).toString();
if (packageManager()) {
@@ -572,7 +579,8 @@ void RmdirOperation::backup()
bool RmdirOperation::performOperation()
{
// Requires only one parameter. That is the name of the file to remove.
- if (!checkArgumentCount(1))
+ // Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]")))
return false;
const QString firstArg = arguments().at(0);
@@ -597,7 +605,7 @@ bool RmdirOperation::performOperation()
bool RmdirOperation::undoOperation()
{
- if (!value(QLatin1String("removed")).toBool())
+ if (!value(QLatin1String("removed")).toBool() || skipUndoOperation())
return true;
errno = 0;
@@ -633,6 +641,12 @@ AppendFileOperation::AppendFileOperation(QInstaller::PackageManagerCore *core)
setName(QLatin1String("AppendFile"));
}
+AppendFileOperation::~AppendFileOperation()
+{
+ if (skipUndoOperation())
+ deleteFileNowOrLater(value(QLatin1String("backupOfFile")).toString());
+}
+
void AppendFileOperation::backup()
{
const QString filename = arguments().first();
@@ -653,10 +667,10 @@ bool AppendFileOperation::performOperation()
{
// This operation takes two arguments. First argument is the name of the file into which a text has to be
// appended. Second argument is the text to append.
- if (!checkArgumentCount(2))
+ if (!checkArgumentCount(2, 4, QLatin1String("<filename> <text to apply> [UNDOOPERATION, \"\"]")))
return false;
- const QStringList args = this->arguments();
+ const QStringList args = arguments();
const QString fName = args.at(0);
QFile file(fName);
if (!file.open(QFile::Append)) {
@@ -695,6 +709,9 @@ bool AppendFileOperation::performOperation()
bool AppendFileOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
+
// backupOfFile being empty -> file didn't exist before -> no error
const QString filename = arguments().first();
const QString backupOfFile = value(QLatin1String("backupOfFile")).toString();
@@ -746,6 +763,12 @@ PrependFileOperation::PrependFileOperation(QInstaller::PackageManagerCore *core)
setName(QLatin1String("PrependFile"));
}
+PrependFileOperation::~PrependFileOperation()
+{
+ if (skipUndoOperation())
+ deleteFileNowOrLater(value(QLatin1String("backupOfFile")).toString());
+}
+
void PrependFileOperation::backup()
{
const QString filename = arguments().first();
@@ -767,10 +790,10 @@ bool PrependFileOperation::performOperation()
// This operation takes two arguments. First argument is the name
// of the file into which a text has to be appended. Second argument
// is the text to append.
- if (!checkArgumentCount(2))
+ if (!checkArgumentCount(2, 4, QLatin1String("<filename> <text to prepend> [UNDOOPERATION, \"\"]")))
return false;
- const QStringList args = this->arguments();
+ const QStringList args = arguments();
const QString fName = args.at(0);
// Load the file first.
QFile file(fName);
@@ -810,7 +833,9 @@ bool PrependFileOperation::performOperation()
bool PrependFileOperation::undoOperation()
{
- // bockupOfFile being empty -> file didn't exist before -> no error
+ if (skipUndoOperation())
+ return true;
+
const QString filename = arguments().first();
const QString backupOfFile = value(QLatin1String("backupOfFile")).toString();
if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) {
diff --git a/src/libs/kdtools/updateoperations.h b/src/libs/kdtools/updateoperations.h
index c789975e2..adbfc7de1 100644
--- a/src/libs/kdtools/updateoperations.h
+++ b/src/libs/kdtools/updateoperations.h
@@ -109,6 +109,7 @@ class KDTOOLS_EXPORT AppendFileOperation : public UpdateOperation
Q_DECLARE_TR_FUNCTIONS(KDUpdater::AppendFileOperation)
public:
explicit AppendFileOperation(QInstaller::PackageManagerCore *core = 0);
+ ~AppendFileOperation();
void backup() override;
bool performOperation() override;
@@ -121,6 +122,7 @@ class KDTOOLS_EXPORT PrependFileOperation : public UpdateOperation
Q_DECLARE_TR_FUNCTIONS(KDUpdater::PrependFileOperation)
public:
explicit PrependFileOperation(QInstaller::PackageManagerCore *core = 0);
+ ~PrependFileOperation();
void backup() override;
bool performOperation() override;
diff --git a/src/sdk/commandlineinterface.cpp b/src/sdk/commandlineinterface.cpp
index 673744c8e..f575a8bbe 100644
--- a/src/sdk/commandlineinterface.cpp
+++ b/src/sdk/commandlineinterface.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -63,6 +63,7 @@ bool CommandLineInterface::initialize()
return false;
}
// Filter the arguments list by removing any key=value pair occurrences.
+ QString command;
m_positionalArguments = m_parser.positionalArguments();
foreach (const QString &argument, m_positionalArguments) {
if (argument.contains(QLatin1Char('=')))
@@ -76,9 +77,10 @@ bool CommandLineInterface::initialize()
} else {
// Sanity and order of arguments already checked in main(), we should be
// quite safe to assume that command is the first positional argument.
+ command = m_positionalArguments.first();
m_positionalArguments.removeFirst();
}
-
+ m_core->saveGivenArguments(QStringList() << command << m_parser.optionNames());
QString ctrlScript = controlScript();
if (!ctrlScript.isEmpty()) {
m_core->controlScriptEngine()->loadInContext(
diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp
index f9f535ee4..ac2c9d367 100644
--- a/src/sdk/installerbase.cpp
+++ b/src/sdk/installerbase.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -97,6 +97,8 @@ int InstallerBase::run()
if (status != QInstaller::PackageManagerCore::Success)
return status;
+ m_core->saveGivenArguments(m_parser.optionNames());
+
#ifdef ENABLE_SQUISH
if (m_parser.isSet(CommandLineOptions::scSquishPortLong)) {
const int maxSquishPortNumber = 65535;
diff --git a/src/sdk/sdk.pro b/src/sdk/sdk.pro
index fac8f4f49..ee4e8fb1d 100644
--- a/src/sdk/sdk.pro
+++ b/src/sdk/sdk.pro
@@ -40,7 +40,9 @@ exists($$LRELEASE) {
"<RCC>" \
" <qresource prefix=\"/\">"
for (file, IB_TRANSLATIONS) {
- lang = $$replace(file, .*_([^/]*)\\.ts, \\1)
+ lang = $$basename(file)
+ lang = $$replace(lang, .ts, "")
+ lang = $$replace(lang, ifw_, "")
qlang = $${lang}
qfile = $$[QT_INSTALL_TRANSLATIONS]/qtbase_$${lang}.qm
!exists($$qfile) {
diff --git a/src/sdk/settingsdialog.cpp b/src/sdk/settingsdialog.cpp
index acd364b2f..0a16377b3 100644
--- a/src/sdk/settingsdialog.cpp
+++ b/src/sdk/settingsdialog.cpp
@@ -242,8 +242,19 @@ SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent)
connect(m_ui->m_clearPushButton, &QAbstractButton::clicked,
this, &SettingsDialog::clearLocalCacheClicked);
- connect(m_ui->m_clearPushButton, &QAbstractButton::clicked,
- this, [&] { m_cacheCleared = true; });
+ connect(m_ui->m_clearPushButton, &QAbstractButton::clicked, this, [&] {
+ // Disable the button as the new settings will only take effect after
+ // closing the dialog.
+ m_ui->m_clearPushButton->setEnabled(false);
+ m_cacheCleared = true;
+ });
+ connect(m_ui->m_cachePathLineEdit, &QLineEdit::textChanged, this, [&] {
+ if (!m_cacheCleared) {
+ // Disable the button if the path is modified between applying settings
+ m_ui->m_clearPushButton->setEnabled(
+ settings.localCachePath() == m_ui->m_cachePathLineEdit->text());
+ }
+ });
useTmpRepositoriesOnly(settings.hasReplacementRepos());
m_ui->m_useTmpRepositories->setChecked(settings.hasReplacementRepos());
@@ -256,8 +267,9 @@ SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent)
m_ui->m_repositories->setVisible(settings.repositorySettingsPageVisible());
}
- m_ui->m_cachePathLineEdit->setText(settings.localCachePath());
- showClearCacheProgress(false);
+ m_ui->m_cachePathLineEdit->setText(settings.localCachePath());
+ m_ui->m_clearPushButton->setEnabled(m_core->isValidCache());
+ showClearCacheProgress(false);
}
void SettingsDialog::showClearCacheProgress(bool show)
diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp
index 6e348d1fc..8c15243f8 100644
--- a/src/sdk/tabcontroller.cpp
+++ b/src/sdk/tabcontroller.cpp
@@ -173,6 +173,7 @@ void TabController::restartWizard()
void TabController::onSettingsButtonClicked()
{
SettingsDialog dialog(d->m_core);
+ dialog.adjustSize();
connect(&dialog, &SettingsDialog::networkSettingsChanged,
this, &TabController::onNetworkSettingsChanged);
connect(&dialog, &SettingsDialog::clearLocalCacheClicked,
diff --git a/src/sdk/translations/ifw_ar.ts b/src/sdk/translations/ifw_ar.ts
index ed36a367b..e8adb6ebd 100644
--- a/src/sdk/translations/ifw_ar.ts
+++ b/src/sdk/translations/ifw_ar.ts
@@ -628,7 +628,7 @@
<translation>يرجى تحديد المكونات التي تريد إزالتها.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
<translation>حدّد المكونات التي تريد تثبيتها. ألغِ تحديد المكونات المثبتة لإزالتها. أي مكونات مثبتة بالفعل لن يتم تحديثها.</translation>
</message>
<message>
diff --git a/src/sdk/translations/ifw_ca.ts b/src/sdk/translations/ifw_ca.ts
index 7a1466a52..ddd7efc13 100644
--- a/src/sdk/translations/ifw_ca.ts
+++ b/src/sdk/translations/ifw_ca.ts
@@ -914,8 +914,8 @@
<translation>Seleccioneu els components que voleu desinstal·lar.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Seleccioneu els components que voleu instal·lar. Desseleccioneu els components instal·lats per a desinstal·lar-los. No s&apos;actualitzaran els components ja instal·lats.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Seleccioneu els components que voleu instal·lar. Desseleccioneu els components instal·lats per a desinstal·lar-los.&lt;br&gt;No s&apos;actualitzaran els components ja instal·lats.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_da.ts b/src/sdk/translations/ifw_da.ts
index abf1c735f..a6325fb01 100644
--- a/src/sdk/translations/ifw_da.ts
+++ b/src/sdk/translations/ifw_da.ts
@@ -894,8 +894,8 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Vælg de komponenter du vil installere. Fravælg installeret komponenter for at afinstallere dem. Komponenter som allerede er installeret vil ikke blive opdateret.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Vælg de komponenter du vil installere. Fravælg installeret komponenter for at afinstallere dem.&lt;br&gt;Komponenter som allerede er installeret vil ikke blive opdateret.</translation>
</message>
<message>
<source>This component will occupy approximately %1 on your hard disk drive.</source>
diff --git a/src/sdk/translations/ifw_es.ts b/src/sdk/translations/ifw_es.ts
index 5dd03bf78..a0dad98f9 100644
--- a/src/sdk/translations/ifw_es.ts
+++ b/src/sdk/translations/ifw_es.ts
@@ -922,8 +922,8 @@
<translation>Seleccione los componentes que desea desinstalar.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Seleccione los componentes que desea instalar. Anule la selección de los componentes instalados para desinstalarlos. No se actualizarán los componentes ya instalados.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Seleccione los componentes que desea instalar. Anule la selección de los componentes instalados para desinstalarlos.&lt;br&gt;No se actualizarán los componentes ya instalados.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_fr.ts b/src/sdk/translations/ifw_fr.ts
index 5ea069e7d..92d479e04 100644
--- a/src/sdk/translations/ifw_fr.ts
+++ b/src/sdk/translations/ifw_fr.ts
@@ -922,8 +922,8 @@
<translation>Sélectionnez les composants que vous souhaitez désinstaller.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Sélectionnez les composants à installer. Désélectionnez les composants installés pour les désinstaller. Les composants déjà installés ne seront pas mis à jour.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Sélectionnez les composants à installer. Désélectionnez les composants installés pour les désinstaller.&lt;br&gt;Les composants déjà installés ne seront pas mis à jour.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_hr.ts b/src/sdk/translations/ifw_hr.ts
index ea48b0812..d219b17f6 100644
--- a/src/sdk/translations/ifw_hr.ts
+++ b/src/sdk/translations/ifw_hr.ts
@@ -2066,8 +2066,8 @@ Kopiraj program za instaliranje na računalo</translation>
<translation>Odaberi komponente koje želiš deinstalirati.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Odaberi komponente koje želiš instalirati. Odznači instalirane komponente, kako bi se deinstalirale. Već instalirane komponente neće biti aktualizirane.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Odaberi komponente koje želiš instalirati. Odznači instalirane komponente, kako bi se deinstalirale.&lt;br&gt;Već instalirane komponente neće biti aktualizirane.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_hu.ts b/src/sdk/translations/ifw_hu.ts
index 1851b96e6..cd247fd8b 100644
--- a/src/sdk/translations/ifw_hu.ts
+++ b/src/sdk/translations/ifw_hu.ts
@@ -917,8 +917,8 @@
<translation>Kérem válassza ki az eltávolítani kívánt komponenseket.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Válassza ki a telepítendő összetevőket. Törölje a telepített összetevők kijelölését az eltávolításhoz. A már telepített összetevők nem frissülnek.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Válassza ki a telepítendő összetevőket. Törölje a telepített összetevők kijelölését az eltávolításhoz.&lt;br&gt;A már telepített összetevők nem frissülnek.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_it.ts b/src/sdk/translations/ifw_it.ts
index 4bdf06a62..5f08dc1a7 100644
--- a/src/sdk/translations/ifw_it.ts
+++ b/src/sdk/translations/ifw_it.ts
@@ -918,8 +918,8 @@
<translation>Selezionare i componenti che si desidera disinstallare.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Selezionare i componenti da installare. Deselezionare i componenti installati per disinstallarli. I componenti già installati non verranno aggiornati.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Selezionare i componenti da installare. Deselezionare i componenti installati per disinstallarli.&lt;br&gt;I componenti già installati non verranno aggiornati.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_ja.ts b/src/sdk/translations/ifw_ja.ts
index e100cc2ba..5152944d3 100644
--- a/src/sdk/translations/ifw_ja.ts
+++ b/src/sdk/translations/ifw_ja.ts
@@ -917,7 +917,7 @@
<translation>アンインストールするコンポーネントを選択してください。</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
<translation>インストールするコンポーネントを選択します。 コンポーネントをアンインストールするには、インストール済みのコンポーネントを選択解除します。 すでにインストールされているコンポーネントは更新されません。</translation>
</message>
<message>
diff --git a/src/sdk/translations/ifw_ko.ts b/src/sdk/translations/ifw_ko.ts
index ab0f3ead1..2fc5cce29 100644
--- a/src/sdk/translations/ifw_ko.ts
+++ b/src/sdk/translations/ifw_ko.ts
@@ -632,7 +632,7 @@
<translation>설치 제거하려는 구성요소를 선택하십시오.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
<translation>설치할 구성요소를 선택하십시오. 설치 제거할 설치된 구성요소를 선택 해제하십시오. 이미 설치된 모든 구성요소는 업데이트되지 않습니다.</translation>
</message>
<message>
diff --git a/src/sdk/translations/ifw_pl.ts b/src/sdk/translations/ifw_pl.ts
index 94442554e..572ea5ddc 100644
--- a/src/sdk/translations/ifw_pl.ts
+++ b/src/sdk/translations/ifw_pl.ts
@@ -923,8 +923,8 @@
<translation>Wybierz elementy, które chcesz odinstalować.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Wybierz elementy do zainstalowania. Anuluj zaznaczenie zainstalowanych elementów, aby je dezinstalować. Elementy, które są już zainstalowane, nie zostaną zaktualizowane.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Wybierz elementy do zainstalowania. Anuluj zaznaczenie zainstalowanych elementów, aby je dezinstalować.&lt;br&gt;Elementy, które są już zainstalowane, nie zostaną zaktualizowane.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_pt.ts b/src/sdk/translations/ifw_pt.ts
index fc54bcca3..13b491af8 100644
--- a/src/sdk/translations/ifw_pt.ts
+++ b/src/sdk/translations/ifw_pt.ts
@@ -2037,8 +2037,8 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Por favor, selecione os componentes que você deseja desinstalar.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Selecione componentes para os instalar. Deselecione componentes instalados para desinstalá-los. Os componentes previamente instalados não serão atualizados.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Selecione componentes para os instalar. Deselecione componentes instalados para desinstalá-los.&lt;br&gt;Os componentes previamente instalados não serão atualizados.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_pt_BR.ts b/src/sdk/translations/ifw_pt_BR.ts
index 654d42561..bae3a695c 100644
--- a/src/sdk/translations/ifw_pt_BR.ts
+++ b/src/sdk/translations/ifw_pt_BR.ts
@@ -2032,8 +2032,8 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation>Por favor, selecione os componentes que você deseja desinstalar.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Selecione os componentes para instalar. Desmarque os componentes instalados para desinstalá-los. Quaisquer componentes já instalados não serão atualizados.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Selecione os componentes para instalar. Desmarque os componentes instalados para desinstalá-los.&lt;br&gt;Quaisquer componentes já instalados não serão atualizados.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_ru.ts b/src/sdk/translations/ifw_ru.ts
index 6270c726e..33d4c4c37 100644
--- a/src/sdk/translations/ifw_ru.ts
+++ b/src/sdk/translations/ifw_ru.ts
@@ -919,8 +919,8 @@
<translation>Пожалуйста, выберите компоненты, которые вы хотите удалить.</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
- <translation>Выберите компоненты для установки. Для удаления уже установленных компонентов снимите отметки выбора. Уже установленные компоненты не будут обновлены.</translation>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
+ <translation>Выберите компоненты для установки. Для удаления уже установленных компонентов снимите отметки выбора.&lt;br&gt;Уже установленные компоненты не будут обновлены.</translation>
</message>
<message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
diff --git a/src/sdk/translations/ifw_zh_CN.ts b/src/sdk/translations/ifw_zh_CN.ts
index cb95c2484..a86711492 100644
--- a/src/sdk/translations/ifw_zh_CN.ts
+++ b/src/sdk/translations/ifw_zh_CN.ts
@@ -917,7 +917,7 @@
<translation>请选择要卸载的组件。</translation>
</message>
<message>
- <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source>
+ <source>Select the components to install. Deselect installed components to uninstall them.&lt;br&gt;Any components already installed will not be updated.</source>
<translation>选择要安装的组件。 取消选择已安装组件以卸载它们。 所有已安装的组件均不会更新。</translation>
</message>
<message>
diff --git a/tests/auto/installer/appendfileoperation/tst_appendfileoperation.cpp b/tests/auto/installer/appendfileoperation/tst_appendfileoperation.cpp
index 9eb1e2401..743e4c625 100644
--- a/tests/auto/installer/appendfileoperation/tst_appendfileoperation.cpp
+++ b/tests/auto/installer/appendfileoperation/tst_appendfileoperation.cpp
@@ -81,8 +81,7 @@ private slots:
QVERIFY(!op.performOperation());
QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
- QCOMPARE(op.errorString(), QString("Invalid arguments in AppendFile: "
- "0 arguments given, exactly 2 arguments expected."));
+ QCOMPARE(op.errorString(), QString("Invalid arguments in AppendFile: 0 arguments given, 2 to 4 arguments expected in the form: <filename> <text to apply> [UNDOOPERATION, \"\"]."));
op.setArguments(QStringList() << "" << "");
QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::open: No file name specified");
@@ -98,10 +97,14 @@ private slots:
QTest::addColumn<QString>("source");
QTest::addColumn<QString>("append");
QTest::addColumn<QString>("expected");
+ QTest::addColumn<bool>("overrideUndo");
QTest::newRow("newline") << "Line1\nLine2\nLine3\n" << "AppendedText"
- << "Line1\nLine2\nLine3\nAppendedText";
+ << "Line1\nLine2\nLine3\nAppendedText" << false;
QTest::newRow("no newline") << "Lorem ipsum " << "dolore sit amet"
- << "Lorem ipsum dolore sit amet";
+ << "Lorem ipsum dolore sit amet" << false;
+
+ QTest::newRow("no undo") << "Lorem ipsum " << "dolore sit amet"
+ << "Lorem ipsum dolore sit amet" << true;
}
void testAppendText()
@@ -109,6 +112,7 @@ private slots:
QFETCH(QString, source);
QFETCH(QString, append);
QFETCH(QString, expected);
+ QFETCH(bool, overrideUndo);
QFile file(m_testFilePath);
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
@@ -117,20 +121,29 @@ private slots:
stream << source << Qt::flush;
file.close();
- AppendFileOperation op;
- op.setArguments(QStringList() << m_testFilePath << append);
+ AppendFileOperation *op = new AppendFileOperation();
+ op->setArguments(QStringList() << m_testFilePath << append);
+ if (overrideUndo)
+ op->setArguments(op->arguments() << QLatin1String("UNDOOPERATION"));
- op.backup();
- QVERIFY(QFileInfo(op.value("backupOfFile").toString()).exists());
+ op->backup();
+ QVERIFY(QFileInfo(op->value("backupOfFile").toString()).exists());
- QVERIFY2(op.performOperation(), op.errorString().toLatin1());
+ QVERIFY2(op->performOperation(), op->errorString().toLatin1());
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
QCOMPARE(stream.readAll(), expected);
file.close();
- QVERIFY2(op.undoOperation(), op.errorString().toLatin1());
+ QVERIFY2(op->undoOperation(), op->errorString().toLatin1());
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
- QCOMPARE(stream.readAll(), source);
+ if (overrideUndo)
+ QCOMPARE(stream.readAll(), expected);
+ else
+ QCOMPARE(stream.readAll(), source);
+ QString backupFileName = op->value("backupOfFile").toString();
+ delete op;
+ QVERIFY(!QFileInfo::exists(backupFileName));
+
file.close();
QVERIFY(file.remove());
diff --git a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
index 948768f8d..4dfadbad6 100644
--- a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
+++ b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,7 @@
#include <QLoggingCategory>
#include <QTest>
#include <QRegularExpression>
+#include <QSignalSpy>
using namespace QInstaller;
@@ -54,7 +55,6 @@ public:
void ignoreInstallPackageFailsMessages(const QRegularExpression &regExp)
{
QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
- QTest::ignoreMessage(QtDebugMsg, "Loading component scripts...");
QTest::ignoreMessage(QtDebugMsg, regExp);
}
@@ -159,8 +159,6 @@ private slots:
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("B.subcomponent")));
- ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install MissingComponent. "
- "Component not found.\n"));
QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
<< QLatin1String("MissingComponent")));
QCOMPARE(PackageManagerCore::Canceled, core->status());
@@ -224,6 +222,57 @@ private slots:
QVERIFY(dir.removeRecursively());
}
+ void testCategoryInstall_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+
+
+ QTest::newRow("Category installation")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentE")
+ << PackageManagerCore::Success;
+ }
+
+ void testCategoryInstall()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+
+
+ QString loggingRules = (QLatin1String("ifw.* = true\n"));
+ QLoggingCategory::setFilterRules(loggingRules);
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir));
+
+ RepositoryCategory category;
+ category.setEnabled(false);
+ category.setDisplayName(QLatin1String("category"));
+
+ Repository repo = Repository::fromUserInput(repository);
+ category.addRepository(repo);
+
+ QSet<RepositoryCategory> categories;
+ categories.insert(category);
+
+ core->settings().addRepositoryCategories(categories);
+
+ QSignalSpy spy(core.data(), &PackageManagerCore::statusChanged);
+ QCOMPARE(core->installSelectedComponentsSilently(QStringList() << installComponents), status);
+
+ QList<int> statusArguments;
+
+ for (qsizetype i = 0; i < spy.size(); ++i) {
+ QList<QVariant> tempList = spy.at(i);
+ statusArguments << tempList.at(0).toInt();
+ }
+
+ QVERIFY(statusArguments.contains(PackageManagerCore::NoPackagesFound));
+ QVERIFY(statusArguments.contains(PackageManagerCore::Success));
+ }
+
void testInstall_data()
{
QTest::addColumn<QString>("repository");
diff --git a/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp b/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp
index d41853eee..692fd86d9 100644
--- a/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp
+++ b/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp
@@ -87,37 +87,48 @@ private slots:
QVERIFY(!op.performOperation());
QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
- QCOMPARE(op.errorString(), QString("Invalid arguments in Copy: "
- "0 arguments given, exactly 2 arguments expected."));
-
+ QCOMPARE(op.errorString(), QString("Invalid arguments in Copy: 0 arguments given, "
+ "2 to 4 arguments expected in the form: <source filename> <destination filename> [UNDOOPERATION, \"\"]."));
}
void testCopySomething_data()
{
- QTest::addColumn<QString>("source");
- QTest::addColumn<QString>("destination");
- QTest::newRow("full path syntax") << qApp->applicationFilePath() << m_testDestinationFilePath;
- QTest::newRow("short destination syntax") << qApp->applicationFilePath() << m_testDestinationPath;
- QTest::newRow("short destination syntax with ending separator") << qApp->applicationFilePath()
- << m_testDestinationPath + QDir::separator();
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QString>("destination");
+ QTest::addColumn<bool>("overrideUndo");
+ QTest::newRow("full path syntax") << qApp->applicationFilePath() << m_testDestinationFilePath << false;
+ QTest::newRow("short destination syntax") << qApp->applicationFilePath() << m_testDestinationPath << false;
+ QTest::newRow("short destination syntax with ending separator") << qApp->applicationFilePath()
+ << m_testDestinationPath + QDir::separator() << false;
+ QTest::newRow("override undo") << qApp->applicationFilePath() << m_testDestinationFilePath << true;
}
void testCopySomething()
{
QFETCH(QString, source);
QFETCH(QString, destination);
+ QFETCH(bool, overrideUndo);
- QVERIFY2(QFileInfo(source).exists(), QString("Source file \"%1\" does not exist.").arg(source).toLatin1());
- CopyOperation op;
- op.setArguments(QStringList() << source << destination);
- op.backup();
- QVERIFY2(op.performOperation(), op.errorString().toLatin1());
+ QVERIFY2(QFileInfo::exists(source), QString("Source file \"%1\" does not exist.").arg(source).toLatin1());
+ CopyOperation *op = new CopyOperation();
+ op->setArguments(QStringList() << source << destination);
+ if (overrideUndo)
+ op->setArguments(op->arguments() << QLatin1String("UNDOOPERATION"));
+ op->backup();
+ QVERIFY2(op->performOperation(), op->errorString().toLatin1());
- QVERIFY2(QFileInfo(m_testDestinationFilePath).exists(), QString("Copying from \"%1\" to \"%2\" was "
+ QVERIFY2(QFileInfo::exists(m_testDestinationFilePath), QString("Copying from \"%1\" to \"%2\" was "
"not working: '%3' does not exist").arg(source, destination, m_testDestinationFilePath).toLatin1());
- QVERIFY2(op.undoOperation(), op.errorString().toLatin1());
- QVERIFY2(!QFileInfo(m_testDestinationFilePath).exists(), QString("Undo of copying from \"%1\" to "
- "\"%2\" was not working.").toLatin1());
+ QVERIFY2(op->undoOperation(), op->errorString().toLatin1());
+ if (!overrideUndo) {
+ QVERIFY2(!QFileInfo::exists(m_testDestinationFilePath), QString("Undo of copying from \"%1\" to "
+ "\"%2\" was not working.").toLatin1());
+ } else {
+ QVERIFY(QFileInfo::exists(m_testDestinationFilePath));
+ }
+ QString backupFileName = op->value("backupOfExistingDestination").toString();
+ delete op;
+ QVERIFY(!QFileInfo::exists(backupFileName));
}
void testCopyIfDestinationExist_data()
diff --git a/tests/auto/installer/createoffline/tst_createoffline.cpp b/tests/auto/installer/createoffline/tst_createoffline.cpp
index 123f37a5b..607f9b7cc 100644
--- a/tests/auto/installer/createoffline/tst_createoffline.cpp
+++ b/tests/auto/installer/createoffline/tst_createoffline.cpp
@@ -93,7 +93,7 @@ private slots:
<< PackageManagerCore::Canceled;
QTest::newRow("Invalid repository")
<< ":///data/repository-invalid" << "a.dummy.component"
- << PackageManagerCore::Failure;
+ << PackageManagerCore::Canceled;
}
void testCreateOfflineInstaller()
@@ -136,13 +136,13 @@ private slots:
<< true << PackageManagerCore::Canceled;
QTest::newRow("Disallow unstable | Missing dependency with selected component")
<< ":///data/repository-missingdependency" << "example.with.unstable.dependency"
- << false << PackageManagerCore::Failure;
+ << false << PackageManagerCore::Canceled;
QTest::newRow("Allow unstable | Missing dependency with other component")
<< ":///data/repository-missingdependency" << "example.without.unstable.dependency"
<< true << PackageManagerCore::Success;
QTest::newRow("Disallow unstable | Missing dependency with other component")
<< ":///data/repository-missingdependency" << "example.without.unstable.dependency"
- << false << PackageManagerCore::Failure;
+ << false << PackageManagerCore::Canceled;
}
void testCreateOfflineWithUnstableComponent()
diff --git a/tests/auto/installer/deleteoperation/tst_deleteoperation.cpp b/tests/auto/installer/deleteoperation/tst_deleteoperation.cpp
index 807d03498..43ea52407 100644
--- a/tests/auto/installer/deleteoperation/tst_deleteoperation.cpp
+++ b/tests/auto/installer/deleteoperation/tst_deleteoperation.cpp
@@ -75,8 +75,7 @@ private slots:
QVERIFY(!op.performOperation());
QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
- QCOMPARE(op.errorString(), QString("Invalid arguments in Delete: "
- "0 arguments given, exactly 1 arguments expected."));
+ QCOMPARE(op.errorString(), QString("Invalid arguments in Delete: 0 arguments given, 1 to 3 arguments expected in the form: <file to remove> [UNDOOPERATION, \"\"]."));
op.setArguments(QStringList() << "");
QTest::ignoreMessage(QtWarningMsg, "QFile::copy: Empty or null file name");
@@ -92,13 +91,16 @@ private slots:
void testDeleteRestore_data()
{
QTest::addColumn<QString>("path");
- QTest::newRow("relative") << "test";
- QTest::newRow("absolute") << qApp->applicationDirPath() + QDir::toNativeSeparators("/test");
+ QTest::addColumn<bool>("overrideUndo");
+ QTest::newRow("relative") << "test" << false;
+ QTest::newRow("absolute") << qApp->applicationDirPath() + QDir::toNativeSeparators("/test") << false;
+ QTest::newRow("no undo") << "test" << true;
}
void testDeleteRestore()
{
QFETCH(QString, path);
+ QFETCH(bool, overrideUndo);
QByteArray testString("Generated by QTest\n");
QFile testFile(path);
@@ -107,23 +109,32 @@ private slots:
out << testString;
testFile.close();
- QVERIFY(QFileInfo(path).exists());
+ QVERIFY(QFileInfo::exists(path));
QByteArray testFileHash = QInstaller::calculateHash(path, QCryptographicHash::Sha1);
- DeleteOperation op;
- op.setArguments(QStringList() << path);
-
- op.backup();
- QVERIFY(QFileInfo(op.value("backupOfExistingFile").toString()).exists());
-
- QVERIFY2(op.performOperation(), op.errorString().toLatin1());
- QVERIFY(!QFileInfo(path).exists());
-
- QVERIFY2(op.undoOperation(), op.errorString().toLatin1());
- QByteArray restoredFileHash = QInstaller::calculateHash(path, QCryptographicHash::Sha1);
- QVERIFY(testFileHash == restoredFileHash);
-
- QVERIFY(QFile(path).remove());
+ DeleteOperation *op = new DeleteOperation();
+ op->setArguments(QStringList() << path);
+ if (overrideUndo)
+ op->setArguments(op->arguments() << QLatin1String("UNDOOPERATION"));
+
+ op->backup();
+ QVERIFY(QFileInfo::exists(op->value("backupOfExistingFile").toString()));
+
+ QVERIFY2(op->performOperation(), op->errorString().toLatin1());
+ QVERIFY(!QFileInfo::exists(path));
+
+ QVERIFY2(op->undoOperation(), op->errorString().toLatin1());
+ if (!overrideUndo) {
+ QByteArray restoredFileHash = QInstaller::calculateHash(path, QCryptographicHash::Sha1);
+ QVERIFY(testFileHash == restoredFileHash);
+ } else {
+ QVERIFY(!QFileInfo::exists(path));
+ }
+
+ QString backupFileName = op->value("backupOfExistingFile").toString();
+ delete op;
+ QVERIFY(!QFileInfo::exists(backupFileName));
+ QCOMPARE(QFile(path).remove(), !overrideUndo);
}
void testDeleteFromScript()
diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro
index a5f5b3a2e..b2eb57790 100644
--- a/tests/auto/installer/installer.pro
+++ b/tests/auto/installer/installer.pro
@@ -15,6 +15,7 @@ SUBDIRS += \
scriptengine \
consumeoutputoperationtest \
mkdiroperationtest \
+ rmdiroperationtest \
copyoperationtest \
solver \
binaryformat \
@@ -29,6 +30,7 @@ SUBDIRS += \
linereplaceoperation \
metadatajob \
appendfileoperation \
+ prependfileoperation \
simplemovefileoperation \
deleteoperation \
copydirectoryoperation \
diff --git a/tests/auto/installer/metadatacache/data/existing-cache/manifest.json b/tests/auto/installer/metadatacache/data/existing-cache/manifest.json
index 0091d8a20..7f30e0fc0 100644
--- a/tests/auto/installer/metadatacache/data/existing-cache/manifest.json
+++ b/tests/auto/installer/metadatacache/data/existing-cache/manifest.json
@@ -3,5 +3,5 @@
"placeholder_sha1"
],
"type": "Metadata",
- "version": "1.1.0"
+ "version": "1.2.0"
}
diff --git a/tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp b/tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp
index f52c27d0f..e41ba8049 100644
--- a/tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp
+++ b/tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -60,32 +60,42 @@ private slots:
QVERIFY(!op.performOperation());
QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
- QCOMPARE(op.errorString(), QString("Invalid arguments in Mkdir: "
- "0 arguments given, exactly 1 arguments expected."));
-
+ QCOMPARE(op.errorString(), QString("Invalid arguments in Mkdir: 0 arguments given, 1 to 3 "
+ "arguments expected in the form: <file to remove> [UNDOOPERATION, \"\"]."));
}
void testCreateDirectory_data()
{
- QTest::addColumn<QString>("directory");
- QTest::newRow("/test") << "/test";
- QTest::newRow("/test/test") << "/test/test";
- QTest::newRow("/test/test/test") << "/test/test/test";
+ QTest::addColumn<QString>("directory");
+ QTest::addColumn<bool>("overrideUndo");
+ QTest::newRow("/test") << "/test" << false;
+ QTest::newRow("/test/test") << "/test/test" << false;
+ QTest::newRow("/test/test/test") << "/test/test/test" << false;
+ QTest::newRow("no undo") << "/test" << true;
}
void testCreateDirectory()
{
QFETCH(QString, directory);
+ QFETCH(bool, overrideUndo);
+
QString path = QDir::current().path() + QDir::toNativeSeparators(directory);
QVERIFY2(!QDir(path).exists(), path.toLatin1());
MkdirOperation op;
op.setArguments(QStringList() << path);
+ if (overrideUndo)
+ op.setArguments(op.arguments() << QLatin1String("UNDOOPERATION"));
op.backup();
QVERIFY2(op.performOperation(), op.errorString().toLatin1());
QVERIFY2(QDir(path).exists(), path.toLatin1());
QVERIFY2(op.undoOperation(), op.errorString().toLatin1());
- QVERIFY2(!QDir(path).exists(), path.toLatin1());
+ if (overrideUndo) {
+ QVERIFY2(QDir(path).exists(), path.toLatin1());
+ QVERIFY(QDir(path).removeRecursively());
+ } else {
+ QVERIFY2(!QDir(path).exists(), path.toLatin1());
+ }
}
void testCreateDirectory_customFile_data()
diff --git a/tests/auto/installer/moveoperation/tst_moveoperation.cpp b/tests/auto/installer/moveoperation/tst_moveoperation.cpp
index 136eb1e45..bb391efee 100644
--- a/tests/auto/installer/moveoperation/tst_moveoperation.cpp
+++ b/tests/auto/installer/moveoperation/tst_moveoperation.cpp
@@ -86,8 +86,8 @@ private slots:
QVERIFY(!op.performOperation());
QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
- QCOMPARE(op.errorString(), QString("Invalid arguments in Move: "
- "0 arguments given, exactly 2 arguments expected."));
+ QCOMPARE(op.errorString(), QString("Invalid arguments in Move: 0 arguments given, 2 to 4 arguments "
+ "expected in the form: <complete source file name> <complete destination file name> [UNDOOPERATION, \"\"]."));
}
void testMoveFile()
diff --git a/tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1content.7z b/tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1content.7z
new file mode 100644
index 000000000..543aab656
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1meta.7z b/tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1meta.7z
new file mode 100644
index 000000000..a59f269e7
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/data/repository/B/1.0.2-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/prependfileoperation/data/repository/Updates.xml b/tests/auto/installer/prependfileoperation/data/repository/Updates.xml
new file mode 100644
index 000000000..e61d2445f
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/data/repository/Updates.xml
@@ -0,0 +1,16 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <Script>script.qs</Script>
+ <SHA1>750eda14d867849aeb2f47d620f6e5f32134f375</SHA1>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/prependfileoperation/data/xmloperationrepository/C/1.0.0content.7z b/tests/auto/installer/prependfileoperation/data/xmloperationrepository/C/1.0.0content.7z
new file mode 100644
index 000000000..d936db354
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/data/xmloperationrepository/C/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/prependfileoperation/data/xmloperationrepository/Updates.xml b/tests/auto/installer/prependfileoperation/data/xmloperationrepository/Updates.xml
new file mode 100644
index 000000000..379fe865f
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/data/xmloperationrepository/Updates.xml
@@ -0,0 +1,20 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2020-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile CompressedSize="224" OS="Any" UncompressedSize="74"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <Operations>
+ <Operation name="PrependFile">
+ <Argument>@TargetDir@/C.txt</Argument>
+ <Argument>Prepended text: </Argument>
+ </Operation>
+ </Operations>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/prependfileoperation/prependfileoperation.pro b/tests/auto/installer/prependfileoperation/prependfileoperation.pro
new file mode 100644
index 000000000..5f68385fe
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/prependfileoperation.pro
@@ -0,0 +1,10 @@
+include(../../qttest.pri)
+
+QT -= gui
+QT += testlib
+
+SOURCES += tst_prependfileoperation.cpp
+
+RESOURCES += \
+ settings.qrc \
+ ../shared/config.qrc
diff --git a/tests/auto/installer/prependfileoperation/settings.qrc b/tests/auto/installer/prependfileoperation/settings.qrc
new file mode 100644
index 000000000..deaeb257f
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/settings.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/repository/Updates.xml</file>
+ <file>data/repository/B/1.0.2-1content.7z</file>
+ <file>data/repository/B/1.0.2-1meta.7z</file>
+ <file>data/xmloperationrepository/Updates.xml</file>
+ <file>data/xmloperationrepository/C/1.0.0content.7z</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/prependfileoperation/tst_prependfileoperation.cpp b/tests/auto/installer/prependfileoperation/tst_prependfileoperation.cpp
new file mode 100644
index 000000000..12f178cb4
--- /dev/null
+++ b/tests/auto/installer/prependfileoperation/tst_prependfileoperation.cpp
@@ -0,0 +1,180 @@
+/**************************************************************************
+**
+** Copyright (C) 2024 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 <updateoperations.h>
+#include <packagemanagercore.h>
+
+#include <QFile>
+#include <QTest>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+class tst_prependfileoperation : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ m_testFilePath = qApp->applicationDirPath() + QDir::toNativeSeparators("/test");
+ }
+
+ void testMissingArguments()
+ {
+ PrependFileOperation op;
+
+ QVERIFY(op.testOperation());
+ QVERIFY(!op.performOperation());
+
+ QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
+ QCOMPARE(op.errorString(), QString("Invalid arguments in PrependFile: 0 arguments given, 2 to 4 arguments expected in the form: <filename> <text to prepend> [UNDOOPERATION, \"\"]."));
+
+ op.setArguments(QStringList() << "" << "");
+ QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::open: No file name specified");
+ QVERIFY(!op.performOperation());
+
+ QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::UserDefinedError);
+ QCOMPARE(op.errorString(), QString("Cannot open file \"\" for reading: No file name specified"));
+ }
+
+ void testPrependText_data()
+ {
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QString>("prepend");
+ QTest::addColumn<QString>("expected");
+ QTest::addColumn<bool>("overrideUndo");
+ QTest::newRow("newline") << "Line1\nLine2\nLine3\n" << "PrependedText"
+ << "PrependedTextLine1\nLine2\nLine3\n" << false;
+ QTest::newRow("no newline") << "dolore sit amet" << "Lorem ipsum "
+ << "Lorem ipsum dolore sit amet" << false;
+
+ QTest::newRow("no undo")<< "dolore sit amet" << "Lorem ipsum "
+ << "Lorem ipsum dolore sit amet" << true;
+ }
+
+ void testPrependText()
+ {
+ QFETCH(QString, source);
+ QFETCH(QString, prepend);
+ QFETCH(QString, expected);
+ QFETCH(bool, overrideUndo);
+
+ QFile file(m_testFilePath);
+ QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
+
+ QTextStream stream(&file);
+ stream << source << Qt::flush;
+ file.close();
+
+ PrependFileOperation *op = new PrependFileOperation();
+ op->setArguments(QStringList() << m_testFilePath << prepend);
+ if (overrideUndo)
+ op->setArguments(op->arguments() << QLatin1String("UNDOOPERATION"));
+
+ op->backup();
+ QVERIFY(QFileInfo(op->value("backupOfFile").toString()).exists());
+
+ QVERIFY2(op->performOperation(), op->errorString().toLatin1());
+ QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
+ QCOMPARE(stream.readAll(), expected);
+ file.close();
+
+ QVERIFY2(op->undoOperation(), op->errorString().toLatin1());
+ QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
+ if (overrideUndo)
+ QCOMPARE(stream.readAll(), expected);
+ else
+ QCOMPARE(stream.readAll(), source);
+ file.close();
+
+ QVERIFY(file.remove());
+
+ QString backupFileName = op->value("backupOfFile").toString();
+ delete op;
+ QVERIFY(!QFileInfo::exists(backupFileName));
+ }
+
+ void testPrependFromCLI_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QString>("componentName");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("operationFromScript")
+ << (":///data/repository")
+ << ("B.txt")
+ << ("B")
+ << ("Prepended text: lorem ipsum");
+
+ QTest::newRow("operationFromXML")
+ << (":///data/xmloperationrepository")
+ << ("C.txt")
+ << ("C")
+ << ("Prepended text: lorem ipsum");
+ }
+
+ void testPrependFromCLI()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QString, fileName);
+ QFETCH(QString, componentName);
+ QFETCH(QString, expected);
+
+ QString installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(installDir));
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (installDir, repository));
+ core->installSelectedComponentsSilently(QStringList() << componentName);
+
+ QFile file(installDir + QDir::separator() + fileName);
+ QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
+ QTextStream stream(&file);
+ QCOMPARE(stream.readAll(), expected);
+ file.close();
+
+ core->setPackageManager();
+ core->commitSessionOperations();
+ // We cannot check the file contents here as it will be deleted on
+ // undo Extract, but at least check that the uninstallation succeeds.
+ QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently
+ (QStringList()<< componentName));
+
+ QDir dir(installDir);
+ QVERIFY(dir.removeRecursively());
+ }
+
+private:
+ QString m_testFilePath;
+};
+
+QTEST_MAIN(tst_prependfileoperation)
+
+#include "tst_prependfileoperation.moc"
diff --git a/tests/auto/installer/rmdiroperationtest/rmdiroperationtest.pro b/tests/auto/installer/rmdiroperationtest/rmdiroperationtest.pro
new file mode 100644
index 000000000..72e61ad80
--- /dev/null
+++ b/tests/auto/installer/rmdiroperationtest/rmdiroperationtest.pro
@@ -0,0 +1,6 @@
+include(../../qttest.pri)
+
+QT -= gui
+QT += testlib
+
+SOURCES = tst_rmdiroperationtest.cpp
diff --git a/tests/auto/installer/rmdiroperationtest/tst_rmdiroperationtest.cpp b/tests/auto/installer/rmdiroperationtest/tst_rmdiroperationtest.cpp
new file mode 100644
index 000000000..bfdd5921e
--- /dev/null
+++ b/tests/auto/installer/rmdiroperationtest/tst_rmdiroperationtest.cpp
@@ -0,0 +1,111 @@
+/**************************************************************************
+**
+** Copyright (C) 2024 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 "init.h"
+#include "updateoperations.h"
+
+#include <QDir>
+#include <QObject>
+#include <QTest>
+#include <QFile>
+#include <QTextStream>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+class tst_rmdiroperationtest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QInstaller::init();
+ QString path = QDir::current().path() + QDir::toNativeSeparators("/test");
+ if (QDir(path).exists()) {
+ QFAIL("Remove test folder first!");
+ }
+ }
+
+ void testMissingArguments()
+ {
+ RmdirOperation op;
+
+ QVERIFY(op.testOperation());
+ QVERIFY(!op.performOperation());
+
+ QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments);
+ QCOMPARE(op.errorString(), QLatin1String("Invalid arguments in Rmdir: 0 arguments given, 1 to 3 "
+ "arguments expected in the form: <file to remove> [UNDOOPERATION, \"\"]."));
+ }
+
+ void testRemoveDirectory_data()
+ {
+ QTest::addColumn<QString>("directory");
+ QTest::addColumn<bool>("overrideUndo");
+ QTest::newRow("/test") << "/test" << false;
+ QTest::newRow("/test/test") << "/test/test" << false;
+ QTest::newRow("/test/test/test") << "/test/test/test" << false;
+ QTest::newRow("no undo") << "/test/test/test/test" << true;
+ }
+
+ void testRemoveDirectory()
+ {
+ QFETCH(QString, directory);
+ QFETCH(bool, overrideUndo);
+
+ QString path = QDir::current().path() + QDir::toNativeSeparators(directory);
+ //Create first the directories utilizing MkdirOperation
+ MkdirOperation op;
+ op.setArguments(QStringList() << path);
+ op.setArguments(op.arguments() << QLatin1String("UNDOOPERATION"));
+ op.backup();
+ QVERIFY2(op.performOperation(), op.errorString().toLatin1());
+ QVERIFY2(QDir(path).exists(), path.toLatin1());
+
+ RmdirOperation rmOp;
+ rmOp.setArguments(QStringList() << path);
+ if (overrideUndo)
+ rmOp.setArguments(op.arguments() << QLatin1String("UNDOOPERATION"));
+ rmOp.backup();
+ rmOp.performOperation();
+ QVERIFY2(!QDir(path).exists(), path.toLatin1());
+
+ QVERIFY2(rmOp.undoOperation(), rmOp.errorString().toLatin1());
+ if (overrideUndo) {
+ QVERIFY2(!QDir(path).exists(), path.toLatin1());
+ } else {
+ QVERIFY2(QDir(path).exists(), path.toLatin1());
+ QVERIFY(QDir(path).removeRecursively());
+ }
+ }
+};
+
+QTEST_MAIN(tst_rmdiroperationtest)
+
+#include "tst_rmdiroperationtest.moc"