summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.qmake.conf2
-rw-r--r--Changelog159
-rw-r--r--INSTALL37
-rw-r--r--README4
-rw-r--r--coin/dependencies.yaml32
-rw-r--r--coin/instructions/common_environment.yaml8
-rw-r--r--coin/instructions/make_instructions.yaml61
-rw-r--r--coin/instructions/make_win_docs_instructions.yaml2
-rw-r--r--coin/instructions/qmake_module_build.yaml8
-rw-r--r--coin/product_dependencies.yaml3
-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.qdocconf19
-rw-r--r--doc/doc.pri41
-rw-r--r--doc/doc.pro12
-rw-r--r--doc/doc_targets.pri89
-rw-r--r--doc/examples/aliases.xml30
-rw-r--r--doc/examples/config.xml1
-rw-r--r--doc/images/ifw-overview.pngbin31363 -> 27901 bytes
-rw-r--r--doc/images/ifw-settings-cache.pngbin0 -> 14486 bytes
-rw-r--r--doc/images/ifw-settings-network.pngbin8607 -> 12288 bytes
-rw-r--r--doc/images/ifw-settings-repositories.pngbin11796 -> 17203 bytes
-rw-r--r--doc/includes/IFWDoc123
-rw-r--r--doc/includes/installerfw-examples-configuring.qdocinc22
-rw-r--r--doc/includes/installerfw-examples-generating.qdocinc2
-rw-r--r--doc/includes/installerfw-examples-packaging.qdocinc10
-rw-r--r--doc/installerfw-getting-started.qdoc100
-rw-r--r--doc/installerfw-overview.qdoc88
-rw-r--r--doc/installerfw-reference.qdoc1
-rw-r--r--doc/installerfw-using.qdoc95
-rw-r--r--doc/installerfw.qdoc291
-rw-r--r--doc/noninteractive.qdoc29
-rw-r--r--doc/operations.qdoc33
-rw-r--r--doc/scripting-api/console.qdoc2
-rw-r--r--doc/scripting-api/gui.qdoc37
-rw-r--r--doc/scripting-api/packagemanagercore.qdoc72
-rw-r--r--doc/scripting-api/qdesktopservices.qdoc8
-rw-r--r--doc/scripting-api/qfiledialog.qdoc9
-rw-r--r--doc/scripting-api/qsettings.qdoc57
-rw-r--r--doc/scripting.qdoc26
-rw-r--r--doc/systeminfo.qdoc23
-rw-r--r--doc/tutorial.qdoc100
-rw-r--r--examples/componentalias/README5
-rw-r--r--examples/componentalias/componentalias.pro13
-rw-r--r--examples/componentalias/config/aliases.xml28
-rw-r--r--examples/componentalias/config/config.xml10
-rw-r--r--examples/componentalias/packages/componentA/data/installcontentA.txt2
-rw-r--r--examples/componentalias/packages/componentA/meta/package.xml8
-rw-r--r--examples/componentalias/packages/componentB/data/installcontentB.txt2
-rw-r--r--examples/componentalias/packages/componentB/meta/package.xml8
-rw-r--r--examples/componentalias/packages/componentC/data/installcontentC.txt2
-rw-r--r--examples/componentalias/packages/componentC/meta/package.xml8
-rw-r--r--examples/componentalias/packages/componentD/data/installcontentD.txt2
-rw-r--r--examples/componentalias/packages/componentD/meta/package.xml8
-rw-r--r--examples/componentalias/packages/componentE/data/installcontentE.txt2
-rw-r--r--examples/componentalias/packages/componentE/meta/package.xml8
-rw-r--r--examples/doc/changeuserinterface.qdoc26
-rw-r--r--examples/doc/componentalias.qdoc160
-rw-r--r--examples/doc/online.qdoc2
-rw-r--r--examples/doc/treename.qdoc8
-rw-r--r--examples/examples.pro1
-rw-r--r--installerfw.pri32
-rw-r--r--installerfw.pro3
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp2
-rw-r--r--src/libs/3rdparty/libarchive/archive.h6
-rw-r--r--src/libs/3rdparty/libarchive/archive_digest.c76
-rw-r--r--src/libs/3rdparty/libarchive/archive_digest_private.h10
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry.c14
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry.h4
-rw-r--r--src/libs/3rdparty/libarchive/archive_getdate.c119
-rw-r--r--src/libs/3rdparty/libarchive/archive_hmac.c34
-rw-r--r--src/libs/3rdparty/libarchive/archive_hmac_private.h9
-rw-r--r--src/libs/3rdparty/libarchive/archive_openssl_evp_private.h3
-rw-r--r--src/libs/3rdparty/libarchive/archive_platform.h3
-rw-r--r--src/libs/3rdparty/libarchive/archive_random.c35
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_data_into_fd.c7
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_posix.c35
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_windows.c90
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_open_file.c4
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c4
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c8
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c2
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c2
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c14
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c208
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_cab.c14
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c14
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_lha.c29
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c32
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_rar.c31
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c11
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_tar.c20
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_warc.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_xar.c12
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_zip.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_string.c10
-rw-r--r--src/libs/3rdparty/libarchive/archive_util.c57
-rw-r--r--src/libs/3rdparty/libarchive/archive_windows.c35
-rw-r--r--src/libs/3rdparty/libarchive/archive_write.c39
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c6
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c2
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c8
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c237
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_posix.c76
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_windows.c85
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_private.h1
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c10
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c32
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_pax.c29
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_warc.c25
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_xar.c14
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_zip.c19
-rw-r--r--src/libs/3rdparty/libarchive/config/mac/config.h10
-rw-r--r--src/libs/3rdparty/libarchive/config_freebsd.h7
-rw-r--r--src/libs/3rdparty/libarchive/filter_fork_posix.c2
-rw-r--r--src/libs/3rdparty/libarchive/filter_fork_windows.c9
-rw-r--r--src/libs/3rdparty/libarchive/libarchive.pro4
-rw-r--r--src/libs/3rdparty/libarchive/qt_attribution.json2
-rw-r--r--src/libs/3rdparty/libarchive/xxhash.c4
-rw-r--r--src/libs/ifwtools/binarycreator.cpp140
-rw-r--r--src/libs/ifwtools/binarycreator.h6
-rw-r--r--src/libs/ifwtools/rcc/rcc.cpp12
-rw-r--r--src/libs/ifwtools/repositorygen.cpp191
-rw-r--r--src/libs/ifwtools/repositorygen.h3
-rw-r--r--src/libs/installer/abstracttask.h2
-rw-r--r--src/libs/installer/binaryformat.cpp11
-rw-r--r--src/libs/installer/binaryformat.h6
-rw-r--r--src/libs/installer/binaryformatengine.cpp25
-rw-r--r--src/libs/installer/binaryformatengine.h7
-rw-r--r--src/libs/installer/calculatorbase.cpp76
-rw-r--r--src/libs/installer/calculatorbase.h85
-rw-r--r--src/libs/installer/commandlineparser.cpp80
-rw-r--r--src/libs/installer/commandlineparser.h8
-rw-r--r--src/libs/installer/component.cpp339
-rw-r--r--src/libs/installer/component.h16
-rw-r--r--src/libs/installer/component_p.cpp10
-rw-r--r--src/libs/installer/component_p.h6
-rw-r--r--src/libs/installer/componentalias.cpp649
-rw-r--r--src/libs/installer/componentalias.h160
-rw-r--r--src/libs/installer/componentmodel.cpp81
-rw-r--r--src/libs/installer/componentmodel.h14
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp391
-rw-r--r--src/libs/installer/componentselectionpage_p.h34
-rw-r--r--src/libs/installer/concurrentoperationrunner.cpp2
-rw-r--r--src/libs/installer/constants.h86
-rw-r--r--src/libs/installer/copydirectoryoperation.cpp2
-rw-r--r--src/libs/installer/copyfiletask.cpp4
-rw-r--r--src/libs/installer/createdesktopentryoperation.cpp14
-rw-r--r--src/libs/installer/createlinkoperation.cpp2
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp2
-rw-r--r--src/libs/installer/createshortcutoperation.cpp4
-rw-r--r--src/libs/installer/customcombobox.cpp54
-rw-r--r--src/libs/installer/customcombobox.h48
-rw-r--r--src/libs/installer/directoryguard.cpp4
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp77
-rw-r--r--src/libs/installer/downloadarchivesjob.h14
-rw-r--r--src/libs/installer/downloadfiletask.cpp22
-rw-r--r--src/libs/installer/downloadfiletask_p.h2
-rw-r--r--src/libs/installer/elevatedexecuteoperation.cpp66
-rw-r--r--src/libs/installer/environmentvariablesoperation.cpp21
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp45
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.cpp6
-rw-r--r--src/libs/installer/fileutils.cpp86
-rw-r--r--src/libs/installer/fileutils.h11
-rw-r--r--src/libs/installer/genericdatacache.cpp695
-rw-r--r--src/libs/installer/genericdatacache.h122
-rw-r--r--src/libs/installer/globals.cpp71
-rw-r--r--src/libs/installer/globals.h19
-rw-r--r--src/libs/installer/globalsettingsoperation.cpp6
-rw-r--r--src/libs/installer/installer.pro21
-rw-r--r--src/libs/installer/installer_global.h8
-rw-r--r--src/libs/installer/installercalculator.cpp292
-rw-r--r--src/libs/installer/installercalculator.h62
-rw-r--r--src/libs/installer/installiconsoperation.cpp4
-rw-r--r--src/libs/installer/lib7z_create.h3
-rw-r--r--src/libs/installer/lib7z_facade.cpp2
-rw-r--r--src/libs/installer/libarchivearchive.cpp8
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp16
-rw-r--r--src/libs/installer/licenseoperation.cpp17
-rw-r--r--src/libs/installer/link.cpp2
-rw-r--r--src/libs/installer/loggingutils.cpp45
-rw-r--r--src/libs/installer/loggingutils.h4
-rw-r--r--src/libs/installer/messageboxhandler.cpp5
-rw-r--r--src/libs/installer/metadata.cpp410
-rw-r--r--src/libs/installer/metadata.h81
-rw-r--r--src/libs/installer/metadatacache.cpp67
-rw-r--r--src/libs/installer/metadatacache.h46
-rw-r--r--src/libs/installer/metadatajob.cpp669
-rw-r--r--src/libs/installer/metadatajob.h61
-rw-r--r--src/libs/installer/metadatajob_p.h122
-rw-r--r--src/libs/installer/observer.cpp6
-rw-r--r--src/libs/installer/observer.h2
-rw-r--r--src/libs/installer/packagemanagercore.cpp1476
-rw-r--r--src/libs/installer/packagemanagercore.h96
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp891
-rw-r--r--src/libs/installer/packagemanagercore_p.h62
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp26
-rw-r--r--src/libs/installer/packagemanagercoredata.h5
-rw-r--r--src/libs/installer/packagemanagergui.cpp349
-rw-r--r--src/libs/installer/packagemanagergui.h13
-rw-r--r--src/libs/installer/packagesource.cpp6
-rw-r--r--src/libs/installer/packagesource.h8
-rw-r--r--src/libs/installer/performinstallationform.cpp6
-rw-r--r--src/libs/installer/permissionsettings.cpp26
-rw-r--r--src/libs/installer/permissionsettings.h17
-rw-r--r--src/libs/installer/productkeycheck.cpp29
-rw-r--r--src/libs/installer/productkeycheck.h9
-rw-r--r--src/libs/installer/progresscoordinator.cpp51
-rw-r--r--src/libs/installer/progresscoordinator.h6
-rw-r--r--src/libs/installer/protocol.h6
-rw-r--r--src/libs/installer/qinstallerglobal.cpp8
-rw-r--r--src/libs/installer/qinstallerglobal.h3
-rw-r--r--src/libs/installer/qprocesswrapper.cpp86
-rw-r--r--src/libs/installer/qprocesswrapper.h9
-rw-r--r--src/libs/installer/qsettingswrapper.cpp110
-rw-r--r--src/libs/installer/qsettingswrapper.h54
-rw-r--r--src/libs/installer/qtpatch.cpp6
-rw-r--r--src/libs/installer/registerfiletypeoperation.cpp8
-rw-r--r--src/libs/installer/remoteclient_p.h5
-rw-r--r--src/libs/installer/remotefileengine.cpp34
-rw-r--r--src/libs/installer/remotefileengine.h16
-rw-r--r--src/libs/installer/remoteobject.cpp11
-rw-r--r--src/libs/installer/remoteobject.h110
-rw-r--r--src/libs/installer/remoteserverconnection.cpp263
-rw-r--r--src/libs/installer/remoteserverconnection.h26
-rw-r--r--src/libs/installer/remoteserverconnection_p.h2
-rw-r--r--src/libs/installer/repository.cpp64
-rw-r--r--src/libs/installer/repository.h14
-rw-r--r--src/libs/installer/repositorycategory.cpp10
-rw-r--r--src/libs/installer/repositorycategory.h7
-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/runextensions.h374
-rw-r--r--src/libs/installer/scriptengine.cpp107
-rw-r--r--src/libs/installer/scriptengine.h8
-rw-r--r--src/libs/installer/scriptengine_p.h35
-rw-r--r--src/libs/installer/settings.cpp73
-rw-r--r--src/libs/installer/settings.h17
-rw-r--r--src/libs/installer/settingsoperation.cpp33
-rw-r--r--src/libs/installer/simplemovefileoperation.cpp2
-rw-r--r--src/libs/installer/sysinfo_win.cpp8
-rw-r--r--src/libs/installer/systeminfo.cpp30
-rw-r--r--src/libs/installer/systeminfo.h5
-rw-r--r--src/libs/installer/uninstallercalculator.cpp199
-rw-r--r--src/libs/installer/uninstallercalculator.h44
-rw-r--r--src/libs/installer/utils.cpp8
-rw-r--r--src/libs/installer/utils.h2
-rw-r--r--src/libs/kdtools/filedownloader.cpp88
-rw-r--r--src/libs/kdtools/filedownloader.h4
-rw-r--r--src/libs/kdtools/filedownloader_p.h8
-rw-r--r--src/libs/kdtools/kdsysinfo_win.cpp12
-rw-r--r--src/libs/kdtools/localpackagehub.cpp1
-rw-r--r--src/libs/kdtools/lockfile_win.cpp2
-rw-r--r--src/libs/kdtools/sysinfo_x11.cpp8
-rw-r--r--src/libs/kdtools/updatefinder.cpp406
-rw-r--r--src/libs/kdtools/updatefinder.h80
-rw-r--r--src/libs/kdtools/updateoperation.cpp99
-rw-r--r--src/libs/kdtools/updateoperation.h3
-rw-r--r--src/libs/kdtools/updateoperations.cpp65
-rw-r--r--src/libs/kdtools/updateoperations.h2
-rw-r--r--src/libs/kdtools/updatesinfo.cpp258
-rw-r--r--src/libs/kdtools/updatesinfo_p.h6
-rw-r--r--src/libs/kdtools/updatesinfodata_p.h14
-rw-r--r--src/sdk/commandlineinterface.cpp53
-rw-r--r--src/sdk/commandlineinterface.h1
-rw-r--r--src/sdk/installerbase.cpp22
-rw-r--r--src/sdk/main.cpp64
-rw-r--r--src/sdk/sdk.pro6
-rw-r--r--src/sdk/sdkapp.h98
-rw-r--r--src/sdk/settingsdialog.cpp38
-rw-r--r--src/sdk/settingsdialog.h6
-rw-r--r--src/sdk/settingsdialog.ui96
-rw-r--r--src/sdk/tabcontroller.cpp59
-rw-r--r--src/sdk/tabcontroller.h1
-rw-r--r--src/sdk/translations/ifw_ar.ts649
-rw-r--r--src/sdk/translations/ifw_ca.ts675
-rw-r--r--src/sdk/translations/ifw_da.ts689
-rw-r--r--src/sdk/translations/ifw_de.ts529
-rw-r--r--src/sdk/translations/ifw_es.ts516
-rw-r--r--src/sdk/translations/ifw_fr.ts655
-rw-r--r--src/sdk/translations/ifw_hr.ts690
-rw-r--r--src/sdk/translations/ifw_hu.ts609
-rw-r--r--src/sdk/translations/ifw_it.ts697
-rw-r--r--src/sdk/translations/ifw_ja.ts650
-rw-r--r--src/sdk/translations/ifw_ko.ts521
-rw-r--r--src/sdk/translations/ifw_pl.ts479
-rw-r--r--src/sdk/translations/ifw_pt_BR.ts650
-rw-r--r--src/sdk/translations/ifw_pt_PT.ts (renamed from src/sdk/translations/ifw_pt.ts)658
-rw-r--r--src/sdk/translations/ifw_ru.ts638
-rw-r--r--src/sdk/translations/ifw_zh_CN.ts636
-rw-r--r--tests/auto/installer/appendfileoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/appendfileoperation/tst_appendfileoperation.cpp35
-rw-r--r--tests/auto/installer/binaryformat/tst_binaryformat.cpp47
-rw-r--r--tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp7
-rw-r--r--tests/auto/installer/clientserver/BLACKLIST2
-rw-r--r--tests/auto/installer/clientserver/tst_clientserver.cpp7
-rw-r--r--tests/auto/installer/cliinterface/tst_cliinterface.cpp602
-rw-r--r--tests/auto/installer/commandlineinstall/commandlineinstall.pro9
-rw-r--r--tests/auto/installer/commandlineinstall/data/components.xml (renamed from tests/auto/installer/cliinterface/data/components.xml)0
-rw-r--r--tests/auto/installer/commandlineinstall/data/componentsFromInstallPackagesRepository.xml (renamed from tests/auto/installer/cliinterface/data/componentsFromInstallPackagesRepository.xml)0
-rw-r--r--tests/auto/installer/commandlineinstall/data/config.xml (renamed from tests/auto/installer/cliinterface/data/config.xml)0
-rw-r--r--tests/auto/installer/commandlineinstall/data/filequeryrepository/A/1.0.2-1meta.7z (renamed from tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z)bin945 -> 945 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/filequeryrepository/Updates.xml (renamed from tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml)2
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/Updates.xml (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/Updates.xml)0
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentA/2.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentA/2.0.0content.7z)bin259 -> 259 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentB/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentB/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentC/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentC/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentD/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentD/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentF/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentF/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentH/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentH/1.0.0content.7z)bin209 -> 209 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/Updates.xml)12
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentA/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentA/1.0.0content.7z)bin259 -> 259 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentB/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentB/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentC/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentC/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentD/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentD/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentE/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentE/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent1/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent1/1.0.0content.7z)bin249 -> 249 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z)bin249 -> 249 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z)bin249 -> 249 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z)bin249 -> 249 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentF/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentG/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentG/1.0.0content.7z)bin241 -> 241 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentG/1.0.0meta.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentG/1.0.0meta.7z)bin970 -> 970 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentH/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentH/1.0.0content.7z)bin209 -> 209 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentI/1.0.0content.7z (renamed from tests/auto/installer/cliinterface/data/installPackagesRepository/componentI/1.0.0content.7z)bin209 -> 209 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7zbin0 -> 209 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7zbin0 -> 1010 bytes
-rw-r--r--tests/auto/installer/commandlineinstall/data/repository/Updates.xml (renamed from tests/auto/installer/cliinterface/data/repository/Updates.xml)0
-rw-r--r--tests/auto/installer/commandlineinstall/data/uninstallableComponentsRepository/Updates.xml (renamed from tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml)0
-rw-r--r--tests/auto/installer/commandlineinstall/settings.qrc (renamed from tests/auto/installer/cliinterface/settings.qrc)2
-rw-r--r--tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp813
-rw-r--r--tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml16
-rw-r--r--tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7zbin0 -> 98 bytes
-rw-r--r--tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7zbin0 -> 98 bytes
-rw-r--r--tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml32
-rw-r--r--tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7zbin0 -> 106 bytes
-rw-r--r--tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7zbin0 -> 106 bytes
-rw-r--r--tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7zbin0 -> 90 bytes
-rw-r--r--tests/auto/installer/commandlineupdate/settings.qrc6
-rw-r--r--tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp101
-rw-r--r--tests/auto/installer/componentalias/componentalias.pro8
-rw-r--r--tests/auto/installer/componentalias/data/aliases-optional.xml36
-rw-r--r--tests/auto/installer/componentalias/data/aliases-priority.xml36
-rw-r--r--tests/auto/installer/componentalias/data/aliases-versions.xml36
-rw-r--r--tests/auto/installer/componentalias/data/aliases.json38
-rw-r--r--tests/auto/installer/componentalias/data/repository/Updates.xml43
-rw-r--r--tests/auto/installer/componentalias/metadata/installer-config/aliases.xml52
-rw-r--r--tests/auto/installer/componentalias/metadata/installer-config/config.xml6
-rw-r--r--tests/auto/installer/componentalias/settings.qrc11
-rw-r--r--tests/auto/installer/componentalias/tst_componentalias.cpp269
-rw-r--r--tests/auto/installer/componentmodel/data/updates.xml2
-rw-r--r--tests/auto/installer/componentmodel/tst_componentmodel.cpp21
-rw-r--r--tests/auto/installer/consumeoutputoperationtest/data/repository/Updates.xml1
-rw-r--r--tests/auto/installer/contentsha1check/contentsha1check.pro (renamed from tests/auto/installer/cliinterface/cliinterface.pro)2
-rw-r--r--tests/auto/installer/contentsha1check/data/config.xml8
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml27
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7zbin0 -> 148 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha11
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml27
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7zbin0 -> 175 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7zbin0 -> 91 bytes
-rw-r--r--tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml27
-rw-r--r--tests/auto/installer/contentsha1check/settings.qrc18
-rw-r--r--tests/auto/installer/contentsha1check/tst_contentsha1check.cpp181
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml28
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml27
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7zbin0 -> 243 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7zbin0 -> 227 bytes
-rw-r--r--tests/auto/installer/contentshaupdate/settings.qrc6
-rw-r--r--tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp28
-rw-r--r--tests/auto/installer/copydirectoryoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/copydirectoryoperation/data/xmloperationrepository/Updates.xml2
-rw-r--r--tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp47
-rw-r--r--tests/auto/installer/createdesktopentryoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/createoffline/tst_createoffline.cpp6
-rw-r--r--tests/auto/installer/createshortcutoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp5
-rw-r--r--tests/auto/installer/deleteoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/deleteoperation/tst_deleteoperation.cpp49
-rw-r--r--tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp10
-rw-r--r--tests/auto/installer/environmentvariableoperation/data/repository/Updates.xml1
-rw-r--r--tests/auto/installer/extractarchiveoperationtest/data.qrc4
-rw-r--r--tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7zbin0 -> 98 bytes
-rw-r--r--tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7zbin0 -> 114 bytes
-rw-r--r--tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml13
-rw-r--r--tests/auto/installer/extractarchiveoperationtest/data/subdirs.7zbin0 -> 18715 bytes
-rw-r--r--tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp85
-rw-r--r--tests/auto/installer/installer.pro9
-rw-r--r--tests/auto/installer/installiconsoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/licenseagreement/data/repository/Updates.xml1
-rw-r--r--tests/auto/installer/messageboxhandler/data/invalidoperation/Updates.xml2
-rw-r--r--tests/auto/installer/messageboxhandler/data/messagebox/Updates.xml2
-rw-r--r--tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp5
-rw-r--r--tests/auto/installer/metadatacache/data/existing-cache/manifest.json7
-rw-r--r--tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml19
-rw-r--r--tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt1
-rw-r--r--tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt1
-rw-r--r--tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml17
-rw-r--r--tests/auto/installer/metadatacache/metadatacache.pro8
-rw-r--r--tests/auto/installer/metadatacache/settings.qrc9
-rw-r--r--tests/auto/installer/metadatacache/tst_metadatacache.cpp381
-rw-r--r--tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7zbin0 -> 1057 bytes
-rw-r--r--tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7zbin0 -> 931 bytes
-rw-r--r--tests/auto/installer/metadatajob/settings.qrc2
-rw-r--r--tests/auto/installer/metadatajob/tst_metadatajob.cpp45
-rw-r--r--tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp28
-rw-r--r--tests/auto/installer/moveoperation/data/repository/Updates.xml1
-rw-r--r--tests/auto/installer/moveoperation/tst_moveoperation.cpp4
-rw-r--r--tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp53
-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/registerfiletypeoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/replaceoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/repository/data/compressedRepository/compressedRepository.7zbin542 -> 590 bytes
-rw-r--r--tests/auto/installer/repository/data/repository/A/1.0.2meta.7zbin0 -> 979 bytes
-rw-r--r--tests/auto/installer/repository/data/repository/B/1.0.0meta.7zbin0 -> 998 bytes
-rw-r--r--tests/auto/installer/repository/data/repository/C/1.0.0meta.7zbin0 -> 994 bytes
-rw-r--r--tests/auto/installer/repository/data/repository/Updates.xml22
-rw-r--r--tests/auto/installer/repository/settings.qrc3
-rw-r--r--tests/auto/installer/repository/tst_repository.cpp102
-rw-r--r--tests/auto/installer/rmdiroperationtest/rmdiroperationtest.pro6
-rw-r--r--tests/auto/installer/rmdiroperationtest/tst_rmdiroperationtest.cpp111
-rw-r--r--tests/auto/installer/scriptengine/tst_scriptengine.cpp62
-rw-r--r--tests/auto/installer/settings/data/full_config.xml1
-rw-r--r--tests/auto/installer/settings/tst_settings.cpp1
-rw-r--r--tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7zbin0 -> 967 bytes
-rw-r--r--tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7zbin0 -> 958 bytes
-rw-r--r--tests/auto/installer/settingsoperation/data/repository/Updates.xml22
-rw-r--r--tests/auto/installer/settingsoperation/settings.qrc2
-rw-r--r--tests/auto/installer/settingsoperation/tst_settingsoperation.cpp66
-rw-r--r--tests/auto/installer/shared/packagemanager.h1
-rw-r--r--tests/auto/installer/shared/verifyinstaller.h23
-rw-r--r--tests/auto/installer/simplemovefileoperation/data/repository/Updates.xml2
-rw-r--r--tests/auto/installer/solver/tst_solver.cpp65
-rw-r--r--tests/auto/installer/treename/tst_treename.cpp4
-rw-r--r--tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml4
-rw-r--r--tests/auto/tools/repotest/tst_repotest.cpp2
-rw-r--r--tools/binarycreator/binarycreator.pro2
-rw-r--r--tools/binarycreator/main.cpp13
-rw-r--r--tools/devtool/devtool.pro3
-rw-r--r--tools/repogen/repogen.cpp2
-rw-r--r--tools/repogen/repogen.pro3
465 files changed, 22000 insertions, 7910 deletions
diff --git a/.gitignore b/.gitignore
index 26cbc3220..2f5e116d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
core
.qmake.cache
.qmake.vars
+.qmake.stash
*.prl
tags
.DS_Store
@@ -90,4 +91,8 @@ doc-build
.metadata
*.qtc_clangd
+doc/codeattributions.qdoc
+/doc/qdoc_wrapper.sh
+/src/sdk/installerbase.qrc
/src/sdk/translations/untranslated.ts
+/src/sdk/translations/ifw_en.ts
diff --git a/.qmake.conf b/.qmake.conf
index d21ceb209..37d46dd43 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,2 +1,2 @@
-VERSION=4.4.0
+VERSION=5.0.0
CONFIG=prepare_docs qt_docs_targets $$CONFIG
diff --git a/Changelog b/Changelog
index d908fa19d..822310af7 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,162 @@
+4.8.0
+- Fix occasional crash in install phase (QTIFW-3314)
+- Allow overwriting undo in some operations (QTIFW-3333, QTIFW-393)
+- Add possibility to add extra log when meta fetch fails (QTIFW-3012)
+- Fix http authorization login (QTIFW-3301)
+- Fix QDesktopServices properties (QTIFW-3334)
+- Fix maintenance tool register path parameters
+- Fix productType function return value in macos (QTIFW-3309)
+- Speed up cache validation (QTIFW-3328)
+- Fix QFileDialog documentation (QTIFW-3320)
+- Disable 'clear cache' button until new cache settings are applied (QTIFW-3252)
+- Add detailed warning when hash verification fails
+- Add security warning possibility when using additional repositories (QTIFW-3305)
+- Add possibility to add additional license text to CLI (QTIFW-3319)
+- Include minimal qt.conf file (QTIFW-3292)
+- Add Chinese and Portuquese languages (QTIFW-3325)
+- Fix translations missing in Linux and macOS (QTIFW-3310)
+- CLI: Perform commands primarily from default repositories (QTIFW-3251)
+- Add possibility to change button text (QTIFW-711)
+
+4.7.0
+- Fix CLI fail on huge amount of Updates.xml download (QTIFW-3249)
+- Prepare for Linux on ARM support (QTIFW-3073)
+- Add support for component aliases (QTIFW-2978)
+- Enable building with Qt6.2 (QTIFW-3083)
+- Enable building with Qt6.5 (QTIFW-1829)
+- Build IFW binaries with Qt6.6
+- 3rdparty: update libarchive sources to 3.7.1 release
+- Use directory separators in path asking for process to be stopped
+- Allow generating offline installers from Gui (QTIFW-3072)
+- Show progress on Welcome screen (QTIFW-3068)
+- Fix page title text on the last page (QTIFW-3060)
+- Prevent uninstalling components when error occurs (QTIFW-3069)
+- Consider full length of 'key=value' string in user arguments
+- Fix admin rights handling during installer/updater/uninstaller runs (QTIFW-2929)
+- Fix documentation about network share (QTIFW-2933)
+
+4.6.1
+- Fix crash when filtering categories (QTIFW-3085)
+- Allow setting temporary repositories for offline-only installers (QTIFW-3078)
+- DownloadArchivesJob: allow download retries for command line runs (QTIFW-3098)
+
+4.6.0
+- Unify handling of processes to close for 'updating' components (QTIFW-2927,QTIFW-3009)
+- Fix wrong extract content (QTIFW-3010)
+- Verify meta file integrity from cache (QTIFW-3023)
+- Match title and message text on the first wizard page (SQUISH-9672)
+- Allow shortcuts for https links in Windows (QTIFW-964)
+- Metadata cache: register items by renaming instead of copying (QTIFW-2971)
+- Center PackageManagerPage title and subtitle texts (QTIFW-2995)
+- Fix showSettingsButton functionality (QTIFW-810)
+- Add more verbose when archive download fails (QTBUG-11068)
+- Fix custom error message in Execute operation (QTIFW-3007)
+- Add more detailed error messages (QTIFW-2998,QTIFW-2883)
+- Make messagebox texts selectable (QTIFW-3005)
+- Fix invalid cache state (QTIFW-2998)
+- UI update (QTIFW-2943)
+- Add support for skipping fetching already cached Updates.xml files (QTIFW-2873)
+- Performance optimizations (QTIFW-2805)
+- 3rdparty: update libarchive sources to 3.6.2 release
+- Fix building with Squish version 7.0.x (SQUISH-15697)
+- Workaround possible stalls on single core systems
+- Metadata cache: clear cache in a separate thread (QTIFW-2815)
+- Fix enabled navigation buttons while metadata fetch is still in progress (QTIFW-2849)
+- Add possibility to post load install scripts (QTIFW-2820)
+- ExtractOp: fix leftover empty directories when 'targetDir' arg is used (QTIFW-2764)
+
+4.5.2
+- Fix freezing UI while searching components (QTIFW-2886)
+- Show check box for AutoDependOn components in updater view (QTIFW-2855)
+- Update OpenSSL version to 1.1.1s in prebuilt binaries
+
+4.5.1
+- Make Settings operation to support _OLD and placeholders (QTIFW-2882)
+- ExtractOp: fix leftover empty directories when 'targetDir' arg is used (QTIFW-2764)
+- Fix errors occurring in full uninstall on macOS (QTIFW-2875)
+- Fix updater view behavior for non-checkable components (QTIFW-836)
+- Execute operation: fix overwritten error string for crashed processes (QTIFW-2875)
+- MetadataJob: fix removing compressed repositories after extracting (QTIFW-2876)
+- Fix replaced removal on update (QTIFW-2887)
+- Fix user set binary marker not having any effect on maintenance tool (QTIFW-2884)
+
+4.5.0
+- Fix required virtual components still uninstalled in some occasions
+- Update translations (QTIFW-2814)
+- macOS: support updating maintenance tool with an app bundle (QTIFW-2750)
+- Fix possible uncaught exceptions while loading package data
+- libarchive: support linking with zlib compiled into QtCore (QTIFW-2803)
+- Add new '--cache-path' and 'clear-cache' options for CLI (QTIFW-2810)
+- Add persistent metadata file cache (QTIFW-2621)
+- Metadata evaluation optimizations (QTIFW-2790)
+- Windows: fix placeholder version in "Apps & features" (QTIFW-2267)
+- Fix installer stalling when there's only one CPU core (QTIFW-2786)
+- Adjust the 'ready to install' message to avoid repeating the app name (SQUISH-9672)
+- CLI: add support for hiding values of printed options (QTIFW-2756)
+- Replace .vbs hack to update maintenance tool binary on Windows (QTIFW-2625)
+- Disable package manager and updater for offline maintenance tool (QTIFW-2627)
+- Display progress for loading component install scripts (QTIFW-2701)
+- Fix separators for localInstallerBinaryUsed() (QTIFW-2700)
+- Allow searching components also in the updater view (QTIFW-2667)
+- Add list of components to uninstall to installation log (QTIFW-2666)
+
+4.4.2
+- Fix uninstallation of needed virtual components
+- Attach to squish only when the port is separately given (QTIFW-2746)
+- Windows: fix installation error with concurrent Extract operations (QTIFW-2752)
+- Uninstaller remove target directory if it is empty (QTIFW-884)
+- Uninstaller remove maintenancetool's data files (QTIFW-884)
+- Do not convert newline characters in license files (QTIFW-903)
+- Set encoding to UTF-8 when writing license file (QTIFW-1436)
+
+4.4.1
+- Fix bug when all requested packages are not installed (QTIFW-2708)
+
+4.4.0
+- Fix installer crash if already installed virtual components are replaced (QTIFW-2672)
+- macOS: make creating maintenance tool alias optional (QTIFW-2665)
+- Updated translation files (QTIFW-2622)
+- RemoteObject: require server reply for all client commands (QTIFW-999)
+- Allow plugin import in binarycreator (QTIFW-266)
+- Windows: Allow asking installer value with different formats (QTIFW-2657)
+
+4.4.0-beta
+- Build Windows installer with 64bit MSVC2019 (QTIFW-2544)
+- Workaround QSettings / QDateTime incompatibility with Qt 6 applications (QTIFW-2610)
+- Swap priority of remote and binary resource package sources (QTIFW-2242)
+- Do not install new dependencies for installed components (QTIFW-2624)
+- Fix updating autodependency components (QTIFW-2595)
+- Save sorting priority of components to local package hub (QTIFW-2631)
+- Do not recalculate local installed components (QTIFW-2522)
+- Remove unnecessary unstable calculation (QTIFW-2626)
+- Do not emit dataChanged when model check state changes (QTIFW-2623)
+- macOS: Create alias of maintenance tool to Applications directory (QTIFW-2535, QTIFW-2478)
+- AspectRatioLabel: center the pixmap by default
+- Speed up component selection in component selection pages (QTIFW-2522)
+- Print uninstallation progress on archive level with normal verbosity (QTIFW-2615)
+- Use fixed width font for the license text browser (QTIFW-2613)
+- Fix rollback of extract when archive lists file entries first (QTIFW-2594)
+- Add option for specifying maximum concurrent unpack operations (QTIFW-2586)
+- Show additional progress information on PerformInstallationPage (QTIFW-2513)
+- Use archive sizes for sorting and calculating progress of Extract ops (QTIFW-2431)
+- Add support for parallel extraction of component archives (QTIFW-2566)
+- Speed up Next button click in tree view (QTIFW-2598, QTIFW-694)
+- AspectRatioLabel: prevent setting non-scaled pixmaps (QTIFW-2612)
+- Fix wrong ApplicationsDir value on macOS (QTIFW-1011)
+- Add missing mnemonic for the "Settings" button (QTIFW-2206)
+- Update libarchive sources to 3.6.1 release
+- Print archive and compression library versions with --version option (QTIFW-2507)
+- 3rdparty: deprecate usage of LZMA SDK
+- Add a signal which is emitted when the downloads are finished (QTIFW-2269)
+- binarycreator: do not require -p option when -rcc option is used
+- Repogen: allow usage of --update-new-components with unified metadata (QTIFW-2558)
+- Doc: Add instructions for promoting maintenance tool updates (QTIFW-2440)
+- CLI: Add error message for trying to install unstable components (QTIFW-2591)
+- Doc: Remove -qt-xcb from recommended configure options (QTIFW-2557)
+- Add install reason why component is uninstalled (QTIFW-2581)
+- Add click option for product image (QTIFW-2361)
+- Build commandline tools without plugins
+
4.3.0
- Fix extracting entries with file paths that contain unicode characters (QTIFW-2506)
- Update Japanese translation file
diff --git a/INSTALL b/INSTALL
index e9e865262..dd7d78cd8 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,15 +15,15 @@ http://code.qt.io/cgit/installer-framework/installer-framework.git/
Build a static Qt
---------------------
-Building the Qt Installer Framework from sources requires at least Qt version 5.15.2.
-Supported compilers are MSVC 2015 or newer, GCC 5 or newer,
-and Clang 11.0.0 or newer. Currently, the tested combination for Windows is Qt 5.15.2 with MSVC 2015 (Windows 10).
+Building the Qt Installer Framework from sources requires at least Qt version 6.6.0.
+Supported compilers are MSVC 2019 or newer, GCC 9 or newer,
+and Clang 13.0.0 or newer. Currently, the tested combination for Windows is Qt 6.6.0 with MSVC 2019 (Windows 10).
If you want to ship your installer as a single file you have to build
Qt and the Qt Installer Framework statically.
See the Qt documentation for the prerequisites and steps to build Qt from sources.
-Please read SSL Import and Export Restrictions from http://doc.qt.io/qt-5/ssl.html if
+Please read SSL Import and Export Restrictions from http://doc.qt.io/qt-6/ssl.html if
you are statically linking against OpenSSL libraries.
### Windows
@@ -32,21 +32,26 @@ Recommended configuration options for Microsoft Windows:
configure -prefix %CD%\qtbase -release -static -static-runtime -accessibility -no-icu -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests
Build Qt:
-nmake (or 'mingw32-make') module-qtbase module-qtdeclarative module-qttools module-qttranslations module-qtwinextras
+cmake --build . --parallel
+cmake --install .
### Linux
Recommended configuration options for Linux:
-configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev
+configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-feature-gssapi -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev -bundled-xcb-xinput -qt-harfbuzz -qt-doubleconversion
+
Build Qt:
-make module-qtbase module-qtdeclarative module-qttools module-qttranslations
+cmake --build . --parallel
+cmake --install .
### macOS
Recommended configuration options for macOS:
-configure -prefix $PWD/qtbase -release -static -no-securetransport -accessibility -qt-zlib -qt-libpng -qt-libjpeg -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -no-freetype
+configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -no-freetype
+
Build Qt:
-make module-qtbase module-qtdeclarative module-qttools module-qttranslations
+cmake --build . --parallel
+cmake --install .
Third party dependencies
@@ -64,10 +69,14 @@ files, with gzip, bzip2, and xz as available compression methods.
The IFW_ZLIB_LIBRARY, IFW_BZIP2_LIBRARY, IFW_LZMA_LIBRARY, and IFW_ICONV_LIBRARY variables
can be used to specify the exact library files if required.
-If you omit the feature, the builtin LZMA SDK library will be used as a fallback and
-installation of the additional dependencies can be skipped, but created installers will
-only support the 7zip format. Note that building IFW with LZMA SDK is deprecated and may not
-be available in future versions.
+If the Qt version used to build the Qt Installer Framework was configured with -qt-zlib and
+IFW_ZLIB_LIBRARY variable is empty, libarchive will attempt to use the zlib library compiled
+into the QtCore module, which removes the need for an external library.
+
+If you do not enable libarchive support, the builtin LZMA SDK library will be used as a
+fallback and installation of the additional dependencies can be skipped, but created
+installers will only support the 7zip format. Note that building IFW with LZMA SDK is
+deprecated and may not be available in future versions.
### Windows
@@ -113,5 +122,5 @@ a configuration header file respective to your platform, which can be found from
Build the Framework
---------------------
-Run 'qmake && make' (or 'mingw32-make', 'nmake' ...) to build the Qt Installer
+Run 'qmake && make' (or 'nmake' ...) to build the Qt Installer
Framework. The documentation can be generated by 'make docs'.
diff --git a/README b/README
index cd57790dd..8ba25e7bc 100644
--- a/README
+++ b/README
@@ -13,8 +13,8 @@ doc directory. The documentation is also available online at
Notes
--------------------------
-To build an installer, it is advised to use a statically linked Qt (5.15.2 or
-newer). The tested and supported Qt version is 5.15.2.
+To build an installer, it is advised to use a statically linked Qt (6.6.0 or
+newer). The tested and supported Qt version is 6.6.0
See the documentation at
https://doc.qt.io/qtinstallerframework/ifw-getting-started.html
diff --git a/coin/dependencies.yaml b/coin/dependencies.yaml
index 0df7ac2a5..4b7ff4b29 100644
--- a/coin/dependencies.yaml
+++ b/coin/dependencies.yaml
@@ -1,13 +1,19 @@
-
-product_dependency:
- ../../qt/qt5:
- ref: "ifw-5.15.2"
-dependency_source: supermodule
-dependencies: [
- "../../qt/qtbase",
- "../../qt/qtsvg",
- "../../qt/qtdeclarative",
- "../../qt/qttools",
- "../../qt/qttranslations",
- "../../qt/qtwinextras"
- ]
+dependencies:
+ ../../qt/qtbase.git:
+ ref: "33f5e985e480283bb0ca9dea5f82643e825ba87c"
+ required: true
+ ../../qt/qtsvg.git:
+ ref: "da7e04eaa56c54d5486e39d2e0bd0ddb2a62b74b"
+ required: true
+ ../../qt/qtdeclarative.git:
+ ref: "e559d5cf2b66c4a973f83f173d57676a21d287ef"
+ required: true
+ ../../qt/qttools.git:
+ ref: "d7c950e5cfcda823544d3a0092a2bf435015f94d"
+ required: true
+ ../../qt/qttranslations.git:
+ ref: "94e46555acd71eb7d14dc340b623c13876101c00"
+ required: true
+ ../../qt/qt5compat.git:
+ ref: "e1fe0c83bc547ba34142c2ab06fdfea0bced7308"
+ required: true
diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml
index 7b6a06402..f0739ee3e 100644
--- a/coin/instructions/common_environment.yaml
+++ b/coin/instructions/common_environment.yaml
@@ -76,7 +76,7 @@ instructions:
- type: PrependToEnvironmentVariable
variableName: INCLUDE
- variableValue: "C:\\Utils\\bzip2-1.0.8-x64;C:\\Utils\\zlib-1.2.12-x64;C:\\Utils\\xz-5.2.5-x64\\src\\liblzma\\api;"
+ variableValue: "C:\\Utils\\bzip2-1.0.8-x64;C:\\Utils\\xz-5.2.5-x64\\src\\liblzma\\api;"
enable_if:
condition: property
property: host.os
@@ -84,7 +84,7 @@ instructions:
- type: PrependToEnvironmentVariable
variableName: LIB
- variableValue: "C:\\Utils\\bzip2-1.0.8-x64;C:\\Utils\\zlib-1.2.12-x64;C:\\Utils\\xz-5.2.5-x64\\windows\\vs2019\\ReleaseMT\\x64\\liblzma;"
+ variableValue: "C:\\Utils\\bzip2-1.0.8-x64;C:\\Utils\\xz-5.2.5-x64\\windows\\vs2019\\ReleaseMT\\x64\\liblzma;"
enable_if:
condition: property
property: target.compiler
@@ -105,5 +105,5 @@ instructions:
- type: Group
instructions:
- type: EnvironmentVariable
- variableName: PACKAGING_KEYS_CONFIG_URL
- variableValue: "http://ci-files01-hki.intra.qt.io/input/semisecure/packaging/packaging_secure.ini"
+ variableName: CI
+ variableValue: "true"
diff --git a/coin/instructions/make_instructions.yaml b/coin/instructions/make_instructions.yaml
index c1a18dbcb..dc29df9c4 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.4.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-5.0.0.run"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.4.0.run"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.4.0.run"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-5.0.0.run"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-5.0.0.run"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -91,32 +91,67 @@ instructions:
- condition: property
property: host.os
equals_value: Linux
+ - condition: property
+ property: target.arch
+ equals_value: X86_64
+
+ - type: Group
+ instructions:
+ - type: ChangeDirectory
+ directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}"
+ - type: ExecuteCommand
+ command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-linux-aarch64-5.0.0.run"
+ maxTimeInSeconds: 36000
+ maxTimeBetweenOutput: 3600
+ userMessageOnFailure: "Failed to create ifw installer."
+ - type: Rename
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-aarch64-5.0.0.run"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-aarch64-5.0.0.run"
+ userMessageOnFailure: "Failed to copy installer."
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: target.arch
+ equals_value: AARCH64
- type: Group
instructions:
- type: ChangeDirectory
directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}"
- type: ExecuteCommand
- command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-macOS-x64-4.4.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-5.0.0.app"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to create ifw installer."
+ - type: ChangeDirectory
+ directory: "{{.AgentWorkingDir}}/qtsdk"
+ - type: ExecuteCommand
+ command: "python3 tqtc-qtsdk/jenkins-templates/jenkins/scripts/pkg_bootstrap.py"
+ maxTimeInSeconds: 1800
+ maxTimeBetweenOutput: 600
+ userMessageOnFailure: "pkg_bootstrap.py failed"
- type: EnvironmentVariable
- variableName: QT_CODESIGN_IDENTITY_KEY
- variableValue: "A5GTH44LYL"
+ variableName: PKG_NODE_ROOT
+ variableValue: "{{.AgentWorkingDir}}/qtsdk"
+ - type: ChangeDirectory
+ directory: "{{.AgentWorkingDir}}/qtsdk/tqtc-qtsdk/packaging_tools"
- type: ExecuteCommand
- command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.0.app"
+ command: "python3 -m pipenv run python sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-5.0.0.app"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to sign the ifw installer"
- type: ExecuteCommand
- command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/notarize.py --dmg={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.0.dmg"
+ command: "python3 -m pipenv run python notarize.py --path={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-5.0.0.dmg"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to notarize the ifw installer"
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.0.dmg"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.4.0.dmg"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-5.0.0.dmg"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-5.0.0.dmg"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: and
@@ -130,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.4.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-5.0.0"
maxTimeInSeconds: 1200
maxTimeBetweenOutput: 1200
userMessageOnFailure: "Failed to create ifw installer."
@@ -151,8 +186,8 @@ instructions:
equals_value: Windows
- type: Rename
- sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.4.0.exe"
- targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.4.0.exe"
+ sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-5.0.0.exe"
+ targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-5.0.0.exe"
userMessageOnFailure: "Failed to copy installer."
enable_if:
condition: property
diff --git a/coin/instructions/make_win_docs_instructions.yaml b/coin/instructions/make_win_docs_instructions.yaml
index 5d5f28f67..ab281501a 100644
--- a/coin/instructions/make_win_docs_instructions.yaml
+++ b/coin/instructions/make_win_docs_instructions.yaml
@@ -3,7 +3,7 @@ instructions:
- type: Group
instructions:
- type: ChangeDirectory
- directory: "{{.SourceDir}}\\doc"
+ directory: "{{.SourceDir}}"
maxTimeInSeconds: 300
maxTimeBetweenOutput: 120
userMessageOnFailure: "Failed to change dir"
diff --git a/coin/instructions/qmake_module_build.yaml b/coin/instructions/qmake_module_build.yaml
index 023927567..3a7801b20 100644
--- a/coin/instructions/qmake_module_build.yaml
+++ b/coin/instructions/qmake_module_build.yaml
@@ -8,10 +8,10 @@ instructions:
- type: InstallSourceArchive
maxTimeInSeconds: 600
maxTimeBetweenOutput: 600
- project: qtsdk/qtsdk
- ref: master
- directory: qtsdk/qtsdk
- userMessageOnFailure: "Could not install qtsdk/qtsdk source archive. Please investigate why."
+ project: qtsdk/tqtc-qtsdk
+ ref: production
+ directory: qtsdk/tqtc-qtsdk
+ userMessageOnFailure: "Could not install qtsdk/tqtc-qtsdk source archive. Please investigate why."
- type: SetBuildDirectory
directory: "{{.SourceDir}}"
diff --git a/coin/product_dependencies.yaml b/coin/product_dependencies.yaml
new file mode 100644
index 000000000..b9ccf5020
--- /dev/null
+++ b/coin/product_dependencies.yaml
@@ -0,0 +1,3 @@
+dependencies:
+ ../../qt/tqtc-qt5.git:
+ ref: "tqtc/6.6.0"
diff --git a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml
index 84aea9c3a..c11d5c9c2 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.4.0</Version>
- <ReleaseDate>2022-02-07</ReleaseDate>
+ <Version>5.0.0</Version>
+ <ReleaseDate>2024-05-07</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 94562c56b..b6ae9298c 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.4.0</Version>
- <ReleaseDate>2022-02-07</ReleaseDate>
+ <Version>5.0.0</Version>
+ <ReleaseDate>2024-05-07</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 fe9e5a498..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
@@ -50,6 +48,9 @@ qhp.InstallerFramework.subprojects.manual.title = Qt Installer Framework Manual
qhp.InstallerFramework.subprojects.manual.indexTitle = Qt Installer Framework Manual
qhp.InstallerFramework.subprojects.manual.type = manual
+macro.IFW = "Qt Installer Framework"
+macro.MT = "maintenance tool"
+
# For docs, QT_VERSION resolves to IFW version
macro.ifwversion = $QT_VERSION
diff --git a/doc/doc.pri b/doc/doc.pri
new file mode 100644
index 000000000..4f6a07d6f
--- /dev/null
+++ b/doc/doc.pri
@@ -0,0 +1,41 @@
+QT += widgets concurrent network qml xml
+
+DOC_TARGETDIR = html
+INSTALL_DOC_PATH = $$IFW_BUILD_TREE/doc/$$DOC_TARGETDIR
+
+build_online_docs: \
+ DOC_FILES = $$PWD/ifw-online.qdocconf
+else: \
+ DOC_FILES = $$PWD/ifw.qdocconf
+
+qtdocs.name = QT_INSTALL_DOCS
+qtdocs.value = $$[QT_INSTALL_DOCS/src]
+qdocindex.name = QDOC_INDEX_DIR
+qdocindex.value = $$[QT_INSTALL_DOCS]
+qtver.name = QT_VERSION
+qtver.value = $$VERSION
+qtvertag.name = QT_VERSION_TAG
+qtvertag.value = $$replace(VERSION, \.,)
+QDOC_ENV += \
+ qtdocs \
+ qdocindex \
+ qtver \
+ qtvertag
+
+DOC_HTML_INSTALLDIR = $$INSTALL_DOC_PATH
+DOC_QCH_OUTDIR = $$IFW_BUILD_TREE/doc
+DOC_QCH_INSTALLDIR = $$INSTALL_DOC_PATH
+
+for (include_path, INCLUDEPATH): \
+ DOC_INCLUDES += -I $$shell_quote($$include_path)
+for (module, QT) {
+ MOD = $$replace(module, \-,_)
+ MOD_INCLUDES = $$eval(QT.$${MOD}.includes)
+ for (include_path, MOD_INCLUDES): \
+ DOC_INCLUDES += -I $$shell_quote($$include_path)
+}
+for (include_path, QMAKE_DEFAULT_INCDIRS): \
+ DOC_INCLUDES += -I $$shell_quote($$include_path)
+macos: DOC_INCLUDES += -F $$shell_quote($$[QT_INSTALL_LIBS])
+
+include(doc_targets.pri)
diff --git a/doc/doc.pro b/doc/doc.pro
deleted file mode 100644
index b22f139f0..000000000
--- a/doc/doc.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-TEMPLATE = aux
-
-CONFIG += force_qt
-QT += core-private widgets concurrent network qml xml
-
-CONFIG += force_independent
-QMAKE_DOCS_TARGETDIR = html
-
-build_online_docs: \
- QMAKE_DOCS = $$PWD/ifw-online.qdocconf
-else: \
- QMAKE_DOCS = $$PWD/ifw.qdocconf
diff --git a/doc/doc_targets.pri b/doc/doc_targets.pri
new file mode 100644
index 000000000..274cf616a
--- /dev/null
+++ b/doc/doc_targets.pri
@@ -0,0 +1,89 @@
+# Creates targets for building documentation
+# (adapted from qt_docs.prf)
+#
+# Usage: Define variables (details below) and include this pri file afterwards.
+#
+# QDOC_ENV - environment variables to set for the qdoc call (see example below)
+# DOC_INDEX_PATHS - list of paths where qdoc should search for index files of dependent
+# modules (Qt index path is included by default)
+# DOC_FILES - list of qdocconf files
+# DOC_OUTDIR_POSTFIX - html is generated in $$OUT_PWD/<qdocconf_name>$$DOC_OUTDIR_POSTFIX
+# DOC_HTML_INSTALLDIR - path to install the directory of html files
+# DOC_QCH_OUTDIR - path to generate the qch files
+# DOC_QCH_INSTALLDIR - path to install the qch files
+#
+# Example for QDOC_ENV:
+# ver.name = VERSION
+# ver.value = 1.0.2
+# foo.name = FOO
+# foo.value = foo
+# QDOC_ENV = ver foo
+
+isEmpty(DOC_FILES): error("Set DOC_FILES before including doc_targets.pri")
+isEmpty(DOC_HTML_INSTALLDIR): error("Set DOC_HTML_INSTALLDIR before including doc_targets.pri")
+isEmpty(DOC_QCH_OUTDIR): error("Set DOC_QCH_OUTDIR before including doc_targets.pri")
+isEmpty(DOC_QCH_INSTALLDIR): error("Set DOC_QCH_INSTALLDIR before including doc_targets.pri")
+
+QT_TOOL_ENV = $$QDOC_ENV
+qtPrepareTool(QDOC, qdoc)
+QT_TOOL_ENV =
+
+!build_online_docs: qtPrepareLibExecTool(QHELPGENERATOR, qhelpgenerator)
+qtPrepareLibExecTool(QTATTRIBUTIONSSCANNER, qtattributionsscanner)
+
+DOCS_BASE_OUTDIR = $$OUT_PWD/doc
+DOC_INDEXES += -indexdir $$shell_quote($$[QT_INSTALL_DOCS])
+for (index_path, DOC_INDEX_PATHS): \
+ DOC_INDEXES += -indexdir $$shell_quote($$index_path)
+
+for (doc_file, DOC_FILES) {
+ !exists($$doc_file): error("Cannot find documentation specification file $$doc_file")
+ DOC_TARGET = $$replace(doc_file, ^(.*/)?(.*)\\.qdocconf$, \\2)
+ isEmpty(DOC_TARGETDIR): DOC_TARGETDIR = $$DOC_TARGET
+ DOC_OUTPUTDIR = $${DOCS_BASE_OUTDIR}/$${DOC_TARGETDIR}$${DOC_OUTDIR_POSTFIX}
+
+ html_docs_$${DOC_TARGET}.commands = $$QDOC -outputdir $$shell_quote($$DOC_OUTPUTDIR) $$doc_file $$DOC_INDEXES $$DOC_INCLUDES
+ QMAKE_EXTRA_TARGETS += html_docs_$${DOC_TARGET}
+
+ !isEmpty(html_docs.commands): html_docs.commands += &&
+ html_docs.commands += $$eval(html_docs_$${DOC_TARGET}.commands)
+
+ inst_html_docs.files += $$DOC_OUTPUTDIR
+
+ !build_online_docs {
+ qch_docs_$${DOC_TARGET}.commands = $$QHELPGENERATOR $$shell_quote($$DOC_OUTPUTDIR/$${DOC_TARGET}.qhp) -o $$shell_quote($$DOC_QCH_OUTDIR/$${DOC_TARGET}.qch)
+ qch_docs_$${DOC_TARGET}.depends = html_docs_$${DOC_TARGET}
+ QMAKE_EXTRA_TARGETS += qch_docs_$${DOC_TARGET}
+
+ !isEmpty(qch_docs.commands): qch_docs.commands += &&
+ qch_docs.commands += $$eval(qch_docs_$${DOC_TARGET}.commands)
+
+ inst_qch_docs.files += $$DOC_QCH_OUTDIR/$${DOC_TARGET}.qch
+ }
+}
+
+qtattributionsscanner.target = qtattributionsscanner
+qtattributionsscanner.commands = $$QTATTRIBUTIONSSCANNER $$shell_quote($$IFW_SOURCE_TREE) \
+ --filter "QDocModule=ifw" -o $$shell_quote($$OUT_PWD/doc/codeattributions.qdoc)
+qtattributionsscanner.CONFIG += phony
+QMAKE_EXTRA_TARGETS += qtattributionsscanner
+html_docs.depends = qtattributionsscanner
+
+!build_online_docs {
+ qch_docs.depends = html_docs
+ inst_qch_docs.path = $$DOC_QCH_INSTALLDIR
+ inst_qch_docs.CONFIG += no_check_exist no_default_install no_build
+ install_docs.depends = install_inst_qch_docs
+ docs.depends = qch_docs
+ INSTALLS += inst_qch_docs
+ QMAKE_EXTRA_TARGETS += qch_docs install_docs
+} else {
+ docs.depends = html_docs
+}
+
+inst_html_docs.path = $$DOC_HTML_INSTALLDIR
+inst_html_docs.CONFIG += no_check_exist no_default_install directory
+INSTALLS += inst_html_docs
+install_docs.depends += install_inst_html_docs
+
+QMAKE_EXTRA_TARGETS += html_docs docs
diff --git a/doc/examples/aliases.xml b/doc/examples/aliases.xml
new file mode 100644
index 000000000..1ce962f1e
--- /dev/null
+++ b/doc/examples/aliases.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<Aliases>
+ <Alias>
+ <Name>package-full</Name>
+ <DisplayName>Full installation package</DisplayName>
+ <Description>Complete installation of the product</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>package-essential</RequiredAliases>
+ <RequiredComponents>com.vendor.root.extras</RequiredComponents>
+ <OptionalAliases>package-optional</OptionalAliases>
+ </Alias>
+ <Alias>
+ <Name>package-essential</Name>
+ <DisplayName>Essential components</DisplayName>
+ <Description>Essential components for the product</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>com.vendor.root.essential</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>package-optional</Name>
+ <DisplayName>Optional components</DisplayName>
+ <Description>Optional components for the product</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <OptionalComponents>com.vendor.root.optional1,com.vendor.root.optional2</OptionalComponents>
+ </Alias>
+</Aliases>
+
diff --git a/doc/examples/config.xml b/doc/examples/config.xml
index ef4598425..b04a413ec 100644
--- a/doc/examples/config.xml
+++ b/doc/examples/config.xml
@@ -28,4 +28,5 @@
<Url>http://www.your-repo-location/packages/</Url>
</Repository>
</RemoteRepositories>
+ <AliasDefinitionsFile>aliases.xml</AliasDefinitionsFile>
</Installer>
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/images/ifw-settings-cache.png b/doc/images/ifw-settings-cache.png
new file mode 100644
index 000000000..d8678153a
--- /dev/null
+++ b/doc/images/ifw-settings-cache.png
Binary files differ
diff --git a/doc/images/ifw-settings-network.png b/doc/images/ifw-settings-network.png
index 25e1c9266..11e27ec6a 100644
--- a/doc/images/ifw-settings-network.png
+++ b/doc/images/ifw-settings-network.png
Binary files differ
diff --git a/doc/images/ifw-settings-repositories.png b/doc/images/ifw-settings-repositories.png
index df533fd98..46808eb01 100644
--- a/doc/images/ifw-settings-repositories.png
+++ b/doc/images/ifw-settings-repositories.png
Binary files differ
diff --git a/doc/includes/IFWDoc b/doc/includes/IFWDoc
deleted file mode 100644
index fd86176ea..000000000
--- a/doc/includes/IFWDoc
+++ /dev/null
@@ -1,123 +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 "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 "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 "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/includes/installerfw-examples-configuring.qdocinc b/doc/includes/installerfw-examples-configuring.qdocinc
index d89ace6a5..afbeef6a8 100644
--- a/doc/includes/installerfw-examples-configuring.qdocinc
+++ b/doc/includes/installerfw-examples-configuring.qdocinc
@@ -4,18 +4,18 @@
specifies the text and default values used in the installer:
\list
- \li The \c <Name> element specifies the application name that is added
+ \li The \c <Name> element sets the application name and adds it
to the page name and introduction text.
- \li The \c <Version> element specifies the application version number.
- \li The \c <Title> element specifies the installer name displayed on the
- title bar.
- \li The \c <Publisher> element specifies the publisher of the software
+ \li The \c <Version> element sets the application version number.
+ \li The \c <Title> element sets the installer name and displays it on
+ the title bar.
+ \li The \c <Publisher> element sets the publisher of the software
(as shown in the Windows Control Panel, for example).
- \li The \c <StartMenuDir> element specifies the name of the default
+ \li The \c <StartMenuDir> element sets the name of the default
program group for the product in the Windows \gui Start menu.
- \li The \c <TargetDir> element specifies that the default target
- directory is located in the \c IfwExamples directory in the home directory
- of the current user (because the predefined variable\c @HomeDir@ is
- used as a part of the value). For more information, see
- \l{Predefined Variables}.
+ \li The \c <TargetDir> element sets the default target directory
+ location to be within the \c IfwExamples directory in the home
+ directory of the current user (because it uses the pre-existing
+ variable \c, @HomeDir@, as part of the value). For more information,
+ see \l{Predefined Variables}.
\endlist
diff --git a/doc/includes/installerfw-examples-generating.qdocinc b/doc/includes/installerfw-examples-generating.qdocinc
index 65ef313b5..12b0d20b5 100644
--- a/doc/includes/installerfw-examples-generating.qdocinc
+++ b/doc/includes/installerfw-examples-generating.qdocinc
@@ -14,4 +14,4 @@
\endcode
\endlist
- The installer is created in the current directory.
+ This creates the installer to the current directory.
diff --git a/doc/includes/installerfw-examples-packaging.qdocinc b/doc/includes/installerfw-examples-packaging.qdocinc
index f8111629a..260b7e23e 100644
--- a/doc/includes/installerfw-examples-packaging.qdocinc
+++ b/doc/includes/installerfw-examples-packaging.qdocinc
@@ -4,12 +4,12 @@
directory specifies the components that are available for installation:
\list
- \li The \c <DisplayName> element specifies the human-readable name of
+ \li The \c <DisplayName> element sets the human-readable name of
the component.
- \li The \c <Description> element specifies the human-readable
+ \li The \c <Description> element sets the human-readable
description of the component.
- \li The \c <Version> element specifies the version number of the
+ \li The \c <Version> element sets the version number of the
component.
- \li The \c <ReleaseDate> element specifies the date when this component
- version was released.
+ \li The \c <ReleaseDate> element sets the date of release for this
+ component version.
\endlist
diff --git a/doc/installerfw-getting-started.qdoc b/doc/installerfw-getting-started.qdoc
index 6841ae91b..1a86953a7 100644
--- a/doc/installerfw-getting-started.qdoc
+++ b/doc/installerfw-getting-started.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,22 +32,17 @@
\title Getting Started
- Qt Installer Framework is developed as part of the Qt project. The
- framework itself uses Qt. However, it can be used to install all kind of
- applications, including (but not limited to) applications built with Qt.
+ You can use the Qt Installer Framework to create installation programs for
+ all kinds of applications, including (but not limited to) applications built
+ with Qt.
\section1 Supported Platforms
You can use the Qt Installer Framework to create installers for all
- platforms supported by \l[QtDoc]{Supported Platforms}{desktop Qt}.
+ platforms supported by \l{https://doc.qt.io/qt-6/supported-platforms.html}{desktop Qt}.
- The installers have been tested on the following platforms:
-
- \list
- \li Microsoft Windows 7, and later
- \li Ubuntu Linux 18.04, and later
- \li macOS 10.13, and later
- \endlist
+ If you use Linux, install also
+ \l {https://doc.qt.io/qt-6/linux-requirements.html} {Platform Plugin dependencies}.
\section1 Building from Sources
@@ -57,88 +52,90 @@
\section2 Supported Compilers
- The Qt Installer Framework can be compiled with Microsoft Visual Studio
- 2015 and newer, GCC 5 and newer, and Clang 11.0.0 and newer. Currently, the
- tested combination for Windows is Qt 5.15.2 with MSVC 2015 (Windows 10).
+ You can compile the Qt Installer Framework with Microsoft Visual Studio
+ 2019 and newer, GCC 9 and newer, and Clang 13.0.0 and newer. Currently, the
+ tested combination for Windows is Qt 6.6.0 with MSVC 2019 (Windows 10).
\section2 Configuring Qt
If you use a statically built Qt to build the Qt Installer Framework
you do not have to deliver Qt libraries, which enables you to distribute
- installers as one file. Please read SSL Import and Export Restrictions
- from http://doc.qt.io/qt-5/ssl.html if you are statically linking against
- OpenSSL libraries.
+ installers as one file. For more information about statically linking
+ against OpenSSL libraries, see \l{http://doc.qt.io/qt-6/ssl.html}{SSL
+ Import and Export Restrictions}.
+
+ The supported Qt version is 6.6.0.
- The supported Qt version is 5.15.2.
+ Get Qt sources:
+ \code
+ \l{https://wiki.qt.io/Building_Qt_6_from_Git}{Get Qt sources from git}.
+ Call init-repository with --module-subset=qt5compat, qtbase, qtdeclarative, qttools, qttranslations
+ \endcode
\section3 Configuring Qt for Windows
- We recommend that you use the following options when you configure Qt for
- Windows:
+ Use the following configuration options for Windows:
\code
configure -prefix %CD%\qtbase -release -static -static-runtime -accessibility -no-icu -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests
\endcode
- Build Qt:
- \code
- nmake (or 'mingw32-make') module-qtbase module-qtdeclarative module-qttools module-qttranslations module-qtwinextras
- \endcode
-
\section3 Configuring Qt for Linux
- We recommend that you use the following configuration options for Linux:
-
- \code
- configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev
- \endcode
+ Use the following configuration options for Linux:
- Build Qt:
\code
- make module-qtbase module-qtdeclarative module-qttools module-qttranslations
+ configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -no-glib -no-cups -no-sql-sqlite -no-feature-gssapi -no-qml-debug -no-opengl -no-egl -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -no-libudev -bundled-xcb-xinput -qt-harfbuzz -qt-doubleconversion
\endcode
\section3 Configuring Qt for macOS
- We recommend that you use the following configuration options for macOS:
+ Use the following configuration options for macOS:
\code
- configure -prefix $PWD/qtbase -release -static -no-securetransport -accessibility -qt-zlib -qt-libpng -qt-libjpeg -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -no-freetype
+ configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -no-freetype
\endcode
Build Qt:
\code
- make module-qtbase module-qtdeclarative module-qttools module-qttranslations
+ cmake --build . --parallel
+ cmake --install .
\endcode
\section2 Third Party Dependencies
The Qt Installer Framework sources contain a redistribution of parts of the
- libarchive compression and archive library, which requires you to link against
- additional libraries; \c liblzma, \c zlib, \c libbzip2, and on macOS, \c libiconv.
+ \c libarchive compression and archive library, which requires you to link
+ against the following libraries; \c liblzma, \c zlib, \c libbzip2, and on
+ macOS, \c libiconv.
- The usage of libarchive is recommended and can be enabled by adding the libarchive
- configuration feature to the list of values specified by the \c CONFIG variable. Installers
- created with this configuration support the (de)compression of 7zip, zip, and tar archive
- files, with gzip, bzip2, and xz as available compression methods.
+ To enable the use of \c libarchive, add the \c libarchive configuration
+ feature to the list of values specified by the \c CONFIG variable.
+ Installers created with this configuration support the creating and
+ extracting of 7zip, zip, and tar archive files, with \c gzip, bzip2, and
+ \c xz as available compression methods.
\code
qmake CONFIG+=libarchive
\endcode
- The \c IFW_ZLIB_LIBRARY, \c IFW_BZIP2_LIBRARY, \c IFW_LZMA_LIBRARY, and \c IFW_ICONV_LIBRARY
- variables can be used to specify the exact library files if required.
+ You can use the \c IFW_ZLIB_LIBRARY, \c IFW_BZIP2_LIBRARY, \c IFW_LZMA_LIBRARY, and \c IFW_ICONV_LIBRARY
+ variables to specify the exact library files.
- If you omit the feature, the builtin LZMA SDK library will be used as a fallback and
- installation of the additional dependencies can be skipped, but created installers will
+ If you add the \c{-qt-zlib} configuration to the Qt version used to build the Qt Installer Framework, and
+ \c IFW_ZLIB_LIBRARY variable is empty, \c libarchive will try to use the \c zlib library compiled
+ into the QtCore module, which removes the need for an external library.
+
+ If you do not enable \c libarchive support, the builtin LZMA SDK library will act as a fallback and
+ installation of the extra dependencies will not occur, but created installers will
only support the 7zip format.
\note Building IFW with LZMA SDK is deprecated and may not be available in future versions.
\section3 Installing Dependencies for Windows
- The source archives for the dependencies can be downloaded from:
+ You can download the source archives for the dependencies from:
\list
\li \l https://tukaani.org/xz/
@@ -167,7 +164,7 @@
The easiest way to install the missing libraries is with a third party
package manager solution, like Homebrew or MacPorts. On macOS 10.15 you
- should only need to additionally install the liblzma library.
+ should only need to additionally install the \c liblzma library.
On Homebrew this would be:
@@ -177,9 +174,10 @@
\section3 Troubleshooting
- For libarchive related compilation errors, you may need to edit the definitions in
- a configuration header file respective to your platform, which can be found from the
- 'src/libs/3rdparty/libarchive/config/' directory of the Installer Framework sources.
+ For \c libarchive related compilation errors, you may need to edit the definitions in
+ a configuration header file respective to your platform. You can find this file in
+ the \c src/libs/3rdparty/libarchive/config/ directory of the Installer Framework sources.
+
\section2 Setting up Qt Installer Framework
diff --git a/doc/installerfw-overview.qdoc b/doc/installerfw-overview.qdoc
index f5b86e679..b1ad69dc5 100644
--- a/doc/installerfw-overview.qdoc
+++ b/doc/installerfw-overview.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.
@@ -32,59 +32,91 @@
\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 on the platform where they are run: Linux, Microsoft
- Windows, and macOS.
+ 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
+ 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 provide the installable content and specify information about
+ process. You supply the installable content and specify information about
it, such as the name of the product and the installer and the text for the
license agreement.
You can customize the installers by adding widgets to the predefined pages
- or by adding whole pages to provide users with additional options. You can
- 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
- You can provide end users with an \e offline or \e online installer, or
+ You can offer end users an \e offline or \e online installer, or
both, depending on your use cases.
\image ifw-overview.png
- Both installers install a \e {maintenance tool} that can later be used to
- add, update, and remove components. Offline installers contain all the
- installable components and do not require network connections during the
- installation. Online installers only install the maintenance tool that then
- 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 dowloading and running an offline installer if the end users do not
- install all the available components.
-
- End users can use the maintenance tool to install additional components from
+ 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 published on the server.
+ updates to content as soon as the updates are available on the server.
However, this works for an offline installation only if you specify a
repository address in the offline installer configuration or if end users
- specify the repository address themselves in the maintenance tool settings.
+ 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 provide an update is to recreate
+ install your product. The easiest way to offer an update is to recreate
the repository and upload it to the web server. For large repositories, you
can update only the changed components.
diff --git a/doc/installerfw-reference.qdoc b/doc/installerfw-reference.qdoc
index 9a57d905e..f129f1218 100644
--- a/doc/installerfw-reference.qdoc
+++ b/doc/installerfw-reference.qdoc
@@ -39,6 +39,7 @@
\li \l{Command Line Interface}
\li \l{Configuration File}
\li \l{Package Directory}
+ \li \l{Alias Definition File}
\li \l{Controller Scripting}
\li \l{Component Scripting}
\li \l{Operations}
diff --git a/doc/installerfw-using.qdoc b/doc/installerfw-using.qdoc
index a2aac9046..3cc52fb1a 100644
--- a/doc/installerfw-using.qdoc
+++ b/doc/installerfw-using.qdoc
@@ -33,12 +33,12 @@
\title End User Workflows
The end user experience is similar for offline and online installers.
- Along with your application, the installers install a maintenance tool that
+ Along with your application, the installers install a \MT that
consists of a package manager, an updater, and an uninstaller. End users can
- use the maintenance tool to add, update, and remove components. The
- maintenance tool connects to an external repository to fetch the components
+ use the \MT to add, update, and remove components. The
+ \MT connects to an external repository to fetch the components
to add or update. You can specify the repository in the configuration file
- or end users can specify it in the maintenance tool settings.
+ or end users can specify it in the \MT settings.
You can support the following end user workflows:
@@ -177,7 +177,7 @@
If end users did not select all the components available for installation
during the initial installation, they can use the package manager to install
the remaining components from a repository later. The package manager is
- part of a maintenance tool that is installed together with the application
+ part of a \MT that is installed together with the application
during the initial installation. This only works if a repository that
contains the components is available either locally or externally.
@@ -186,14 +186,14 @@
\image ifw-user-flow-adding.png "Add components workflow"
- This section uses the \e {Maintenance Tool} installed by the Qt 5 installer
+ This section uses the \e {\MT} installed by the Qt 5 installer
run on macOS as an example implementation of how end users can add components
- after the initial installation. The Maintenance Tool contains the package
+ after the initial installation. The \MT contains the package
manager, updater, and uninstaller.
\section1 Starting Package Manager
- When end users start the Maintenance Tool, the introduction page opens:
+ When end users start the \MT, the introduction page opens:
\image ifw-add-components-introduction.png "Introduction page"
@@ -242,12 +242,12 @@
\image ifw-user-flow-removing.png "Remove components workflow"
- This section uses the Qt 5 Maintenance Tool run on macOS as an example
+ This section uses the Qt 5 \MT run on macOS as an example
implementation of how end users can remove all or selected components.
\section1 Removing All Components
- When end users start the Maintenance Tool, the introduction page opens:
+ When end users start the \MT, the introduction page opens:
\image ifw-add-components-introduction.png "Introduction page"
@@ -294,12 +294,12 @@
\image ifw-user-flow-updating.png "Updating workflow"
- This section uses the Qt 5 Maintenance Tool run on macOS as an example
+ This section uses the Qt 5 \MT run on macOS as an example
implementation of how end users can update installed components.
\section1 Starting Updater
- When end users start the Maintenance Tool, the introduction page opens:
+ When end users start the \MT, the introduction page opens:
\image ifw-updating-introduction.png "Introduction page"
@@ -339,9 +339,9 @@
\title Specifying Settings
- Settings pages enable end users to specify proxy settings or install add-on
- components. End users select \gui Settings on the introduction page to
- specify the settings.
+ Settings pages enable end users to specify proxy settings, install add-on
+ components, and modify local cache parameters. End users select \gui Settings
+ on the introduction page to specify the settings.
\section1 Specifying Proxy Settings
@@ -366,6 +366,14 @@
After the installation, only default and user-defined repositories will be
available.
+ \section1 Specifying Local Cache Settings
+
+ The installers use a local cache for the meta information fetched from remote
+ repositories. This improves loading times when the same metadata is accessed
+ multiple times. End users can configure the location of the metadata cache and
+ clear the contents of an existing cache.
+
+ \image ifw-settings-cache.png "Local Cache tab on Settings page"
*/
/*!
@@ -383,7 +391,7 @@
\section1 Installing Components
- Both the installer and the maintenance tool support installation of new components from
+ Both the installer and the \MT support installation of new components from
command line. The following will install the components given as an argument and
their respective dependencies:
@@ -400,10 +408,17 @@
installer.exe --root "C:\Users\MyUser\MyInstallation" install
\endcode
+ The install command can be also used for installing component aliases. If component
+ aliases are specified, the aliased components are selected for installation:
+
+ \code
+ maintenancetool.exe install alias1 alias2
+ \endcode
+
\section1 Checking for Available Updates
To print information about available component updates, run the \c check-updates
- command with the maintenance tool:
+ command with the \MT:
\code
maintenancetool.exe check-updates
@@ -436,7 +451,7 @@
\section1 Listing Installed Components
To get a list and print additional information about currently installed components, run the
- \c list command with the maintenance tool. The command also accepts an optional regular
+ \c list command with the \MT. The command also accepts an optional regular
expression argument to filter the shown component list.
\code
@@ -448,17 +463,36 @@
The \c search command can be used to search components from available repositories, or
from integrated binary content in case of an offline installer. It can be used with
no arguments to list all available components or with a regular expression to get a list
- of only components matching the pattern. The \c --filter-packages option can be
- used to specify additional filters for the search operation. For a list of usable
- information elements with the option, refer to \l{Summary of Package Information File Elements}.
+ of only components matching the pattern.
+
+ The \c --filter-packages option can be used to specify additional filters for the search
+ operation. For a list of usable information elements with the option, refer to
+ \l{Summary of Package Information File Elements}.
+
+ When the value of the \c{--type} option is set to \c package, the search command will
+ search for available components only:
+
+ \code
+ installer.exe --type package --filter-packages "DisplayName=MyComponent, Version=1.0" search "expression"
+ \endcode
+
+ When the \c{--type} option is omitted, the search command will first search for matching
+ component aliases, and if none are found, component names:
+
+ \code
+ installer.exe search "expression"
+ \endcode
+
+ When the value of the \c{--type} option is set to \c alias, the search command will
+ search for available component aliases only:
\code
- installer.exe --filter-packages "DisplayName=MyComponent, Version=1.0" search "expression"
+ installer.exe --type alias search "expression"
\endcode
\section1 Performing Full Uninstallation
- To uninstall all components and remove the program directory, including maintenance tool,
+ To uninstall all components and remove the program directory, including \MT,
run \c purge command:
\code
@@ -479,6 +513,19 @@
installer.exe --root "C:\TargetFolder" --offline-installer-name "MyInstaller" create-offline componentA componentB
\endcode
+ \section1 Clearing the Local Cache
+
+ Online installers and maintenance tools created with the Qt Installer Framework cache the
+ meta information downloaded from remote repositories to local disk. This improves loading
+ times for subsequent metadata downloads.
+
+ The cache may grow in size over time. To clear the contents of the entire cache,
+ use the \c clear-cache command:
+
+ \code
+ maintenancetool.exe clear-cache
+ \endcode
+
\section1 Unattended Usage
By default, the generated installers may ask for additional information during installation,
@@ -497,7 +544,7 @@
\c {--auto-answer OverwriteTargetDirectory=Yes}. Automatic answers are shown on the
console output and installation log.
- By default, the installer and maintenance tool will print a summary of components to be
+ By default, the installer and \MT will print a summary of components to be
affected by the command and then ask for permission to continue performing the action,
to prevent accidental changes. For unattended usage, this can be skipped by using the
\c --confirm-command option.
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc
index e4927f316..4c55c6d43 100644
--- a/doc/installerfw.qdoc
+++ b/doc/installerfw.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -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}.
@@ -72,6 +77,7 @@
\li \l{Command Line Interface}
\li \l{Configuration File}
\li \l{Package Directory}
+ \li \l{Alias Definition File}
\li \l{Controller Scripting}
\li \l{Component Scripting}
\li \l{Operations}
@@ -90,7 +96,7 @@
\title Creating Installers
- The following steps are needed to create offline and online installers:
+ To create offline and online installers, do the following:
\list 1
@@ -98,13 +104,13 @@
For more information, see \l{Package Directory}.
\li Create a configuration file called \c config.xml in the \c config
- directory. It contains information about how to build the installer
+ directory. It has information about how to build the installer
binaries and online repositories. For more information about the
file format and available settings, see
\l{Configuration File}.
\li Create a package information file called \c package.xml in the
- \c {packages\{component}\meta} directory. It contains settings for deployment and
+ \c {packages\{component}\meta} directory. It has settings for deployment and
the installation process. For more information, see
\l{Meta Directory}.
@@ -112,7 +118,7 @@
For more information, see \l{Data Directory}.
\li For online installers, use the \c repogen tool to create the
- repository that contains the installable content and upload the
+ repository that has the installable content and upload the
repository to a web server.
\li Use the \c binarycreator tool to create the installer. For more
@@ -239,7 +245,7 @@
\li --sv, --show-virtual-components
\li Show virtual components in the installer and the package manager.
\row
- \li -i, --install-compressed-repository <URI,...>
+ \li -i, --install-compressed-repository <file,...>
\li Installs a QBSP or a 7z file. The QBSP (Board Support Package) file must be a .7z file
which contains a valid repository.
\row
@@ -252,6 +258,13 @@
search command. The keys can be any of the possible package information elements, like
\c DisplayName and \c Description.
\row
+ \li --cp, --cache-path <path>
+ \li Sets the path used for local metadata cache. The path must be writable by the current user.
+ \row
+ \li --type package|alias
+ \li [CLI] Sets the type of the given arguments for commands supporting multiple argument types,
+ like search. Defaults to alias.
+ \row
\li --am, --accept-messages
\li [CLI] Accepts all message queries without user input.
\row
@@ -310,11 +323,11 @@
\li Command
\li Usage
\row
- \li in, install <pkg ...>
- \li Install packages given as an argument. If no packages are given, install the default package set.
+ \li in, install <pkg|alias ...>
+ \li Install packages and aliases given as an argument. If no arguments are given, install the default package set.
\row
\li ch, check-updates
- \li Show information about available updates on the maintenance tool.
+ \li Show information about available updates on the \MT.
\row
\li up, update <pkg ...>
\li Update packages given as an argument. If no packages are given, install all available updates.
@@ -322,18 +335,24 @@
\li rm, remove <pkg ...>
\li Uninstall selected packages and their child components.
\row
- \li li, list <regexp>
+ \li li, list <regexp for pkg>
\li List information about currently installed packages.
\row
- \li se, search <regexp>
- \li Search available packages. If no search pattern is given, show all available packages.
+ \li se, search <regexp for pkg|alias>
+ \li Search available aliases or packages. If no search pattern is given, show all available packages.
\note The \c --filter-packages option can be used to specify additional filters for
the search operation. See \l{Summary of Options}.
+
+ \note The \c --type option can be used to specify the content type to search.
+ See \l{Summary of Options}.
\row
\li co, create-offline <pkg ...>
\li Create offline installer from selected packages.
\row
+ \li cc, clear-cache
+ \li Clear contents of the local metadata cache.
+ \row
\li pr, purge
\li Uninstall all packages and remove the program directory.
\endtable
@@ -494,6 +513,18 @@
rights. Only available on Linux, where you usually do not want
to install in the administrator user's home directory.
\row
+ \li LocalCacheDir
+ \li Directory name for storing the metadata cache. This does not include
+ the leading directories, which are determined automatically based on a suitable
+ platform specific location for storing cache files. The user may override the path
+ from the installer settings. The default value is a UUID generated from the
+ name of the product being installed.
+ \row
+ \li PersistentLocalCache
+ \li Set to \c false if the fetched metadata should be removed from the local cache when
+ the installer exits. Otherwise the contents of the cache are kept to speed up
+ subsequent fetches. Defaults to \c true.
+ \row
\li RemoteRepositories
\li List of remote repositories. This element can contain several \c <Repository> child
elements that each contain the \c <Url> child element that specifies the URL to
@@ -504,17 +535,17 @@
For more information, see \l{Configuring Repository Categories}.
\row
\li MaintenanceToolName
- \li Filename of the generated maintenance tool. Defaults to
+ \li Filename of the generated \MT. Defaults to
\e maintenancetool. The platform-specific executable file extension is
appended.
\row
\li MaintenanceToolIniFile
- \li Filename for the configuration of the generated maintenance tool. Defaults to
+ \li Filename for the configuration of the generated \MT. Defaults to
\e {MaintenanceToolName}.ini.
\row
\li MaintenanceToolAlias
- \li Filename for an alias of the maintenance tool that will be created to the
- Applications directory. Defaults to \e {MaintenanceToolName}. Only used on macOS.
+ \li Filename for an alias of the \MT that will be created to the
+ Applications directory. Optional. Only used on macOS.
\row
\li RemoveTargetDir
\li Set to \c false if the target directory should not be deleted when uninstalling.
@@ -535,6 +566,11 @@
\li RepositorySettingsPageVisible
\li Set to \c false to hide the repository settings page inside the settings dialog.
\row
+ \li AllowRepositoriesForOfflineInstaller
+ \li Set to \c false to disable usage of any temporary or user configured repositories
+ set for offline installers. The maintenance tool written by an offline installer
+ can still access the repositories. Defaults to \c true.
+ \row
\li AllowSpaceInPath
\li Set to \c false if the installation path cannot contain space characters.
\row
@@ -546,6 +582,11 @@
\li TargetConfigurationFile
\li Filename for the configuration file on the target. Default is components.xml.
\row
+ \li AliasDefinitionsFile
+ \li Filename for a XML document containing the definitions for component aliases.
+ For more information about how to declare component aliases in the file,
+ see \l{Alias Definition File}.
+ \row
\li Translations
\li List of translation files to be used for translating the user interface. To add
several translation files, specify several \c <Translation> child elements that
@@ -590,6 +631,83 @@
*/
/*!
+ \previouspage ifw-globalconfig.html
+ \page ifw-aliasconfig.html
+ \nextpage noninteractive.html
+
+ \title Alias Definition File
+
+ The alias definition file defines the available component aliases and their
+ properties. The file is typically called \c aliases.xml and located in the
+ \c config directory.
+
+ The component names of the Qt Installer Framework follow a domain-like
+ identifier syntax, for example \c com.vendor.root, \c com.vendor.root.subcomponent,
+ and so on. While this allows an easy way to construct a tree from the components when
+ running the installer in graphical mode, the names can be unintuitive for command line usage,
+ where the components are not displayed in a tree view.
+
+ Instead of relying on the domain-like names for CLI usage, the packager can also define component
+ aliases for existing components. An alias is another name for a single component or a collection
+ of components. It can be used to declare alternative names for existing components that are
+ easier to type and combine multiple components under the same alias name, for easier selection.
+
+ The following example shows a possible alias definition file:
+
+ \quotefile examples/aliases.xml
+
+ \section1 Summary of Alias Definition File Elements
+
+ The following table summarizes the elements in the alias definition file.
+
+ \table
+ \header
+ \li Element
+ \li Description
+ \row
+ \li Name
+ \li Name of component alias.
+ \row
+ \li DisplayName
+ \li Human-readable name of the component alias.
+ \row
+ \li Description
+ \li Human-readable description of the component alias.
+ \row
+ \li Version
+ \li Version number of the component alias.
+ \row
+ \li Virtual
+ \li Set to \c true to hide the component alias from the installer. This also
+ makes the alias unselectable by the user.
+ \row
+ \li RequiredComponents
+ \li Comma-separated list of identifiers of components that this
+ component alias requires. The components are selected for installation
+ when the component alias is selected. Note that the components must be
+ selectable by the user, so virtual or otherwise unselectable components
+ cannot be listed as a requirement.
+ \row
+ \li RequiredAliases
+ \li Comma-separated list of aliases that this component alias requires. The
+ required aliases are selected for installation when this component alias
+ is selected.
+ \row
+ \li OptionalComponents
+ \li Comma-separated list of identifiers of components that this component alias
+ optionally depends on. The components are selected for installation when the
+ component alias is selected, if the components exists and are user selectable.
+ Even if the components cannot be found in the installer, this alias is not marked unstable.
+ \row
+ \li OptionalAliases
+ \li Comma-separated list of aliases that this component alias optionally depends on. The
+ listed aliases are selected for installation when this component alias is selected,
+ if the aliases exist. Even if the aliases don't exists in the installer,
+ this alias is not marked unstable.
+ \endtable
+*/
+
+/*!
\previouspage ifw-updates.html
\page ifw-customizing-installers.html
\nextpage Qt Installer Framework Examples
@@ -621,7 +739,7 @@
\l{Component Scripting}.
A control script is associated with the whole installer by specifying it
- in the \c ControlScript element of the control.xml file of the installer.
+ in the \c ControlScript element of the config.xml file of the installer.
Control scripts can be part of the installer resources or be passed on the
command line. They can be used to modify the installer pages that are
presented to users before components are loaded. Also, you can use them to
@@ -763,12 +881,32 @@
it to a custom resource called \c :/translations_new. This custom resource can be added to
the installer using \c binarycreator option \c -r. For more information, see
\l{Summary of binarycreator Parameters}.
+
+ If the translated language is not already part of the existing translations
+ of Qt Installer Framework, you need to also include the Qt Base translation for that language
+ in the resource file. For this you need to point to the \c qtbase_*.qm for your language from
+ the location of \c QT_INSTALL_TRANSLATIONS and alias it in the resource file.
+
+ For example, for the Czech translation a custom \c translations_new.qrc should look
+ like this:
+
+ \code
+ <!DOCTYPE RCC><RCC version="1.0">
+ <qresource prefix="/translations_new">
+ <file>ifw_cs.qm</file>
+ <file alias="qt_cs.qm">%QT_INSTALL_TRANSLATIONS%/qtbase_cs.qm</file>
+ </qresource>
+ </RCC>
+ \endcode
+
+ The path for replacing \c QT_INSTALL_TRANSLATIONS can be retrieved from the
+ output of \c{qmake -query} of your Qt installation.
*/
/*!
\previouspage ifw-globalconfig.html
\page ifw-component-description.html
- \nextpage noninteractive.html
+ \nextpage ifw-aliasconfig.html
\title Package Directory
@@ -869,8 +1007,12 @@
The component is installed if and only if
all of the specified dependencies are fulfilled.
If a component has an automatic dependency on other components,
- the check box will not be visible next to the component in the component tree.
- The selection will be performed automatically.
+ the check box will not be visible next to the component in the component tree,
+ but this does not change the visibility of the check box in the updater view
+ where the end user may still manually select the component for update.
+
+ When running an installer or a \MT in package manager
+ mode, the selection will be performed automatically.
If the component was not installed before, it will
be selected for installation only when all components
from this list are also selected for installation.
@@ -881,8 +1023,9 @@
For more information, see \l{Component Dependencies}.
\row
\li Virtual
- \li Set to \c true to hide the component from the installer.
- Note that setting this on a root component does not work.
+ \li Set to \c true to hide the component from the installer. This also
+ hides any child components this component may have, including their
+ descendants. Note that setting this on a root component does not work.
\row
\li SortingPriority
\li Priority of the component in the tree. The tree is sorted from
@@ -906,7 +1049,15 @@
\row
\li Script
\li File name of a script being loaded. Optional.
- For more information, see \l{Adding Operations}.
+ Specifying the \c {postLoad="true"} attribute will cause the script
+ to be loaded only to the component that is selected for update or
+ install. With the attribute, the script is loaded right before the
+ component installation starts. This will speed up the installer
+ if there are large amounts of components with install scripts in the
+ repository. Make sure the script does not contain anything that needs
+ to be evaluated before the install tree view is shown.
+ For more information, see \l{Adding Operations} and
+ \l{Using postLoad in component script}.
\row
\li UserInterfaces
\li List of pages to load. To add several pages, add several
@@ -944,7 +1095,8 @@
\row
\li ForcedInstallation
\li Determines that the package must always be installed. End users
- cannot deselect it in the installer.
+ cannot deselect it in the installer. When updating components, the
+ component can still be deselected from an update.
\row
\li ForcedUpdate
\li Marks the package as \c ForcedUpdate to force a restart of the
@@ -970,7 +1122,9 @@
\row
\li Checkable
\li Set to \c false if you want to hide the checkbox for an item. This is useful
- when only a few subcomponents should be selected instead of all. Optional.
+ when only a few subcomponents should be selected instead of all. When updating
+ components, the checkbox is still visible to allow toggling the component for
+ update. Optional.
\row
\li ExpandedByDefault
\li Set to \c true if you want this item to be expanded by default. Optional.
@@ -1395,9 +1549,9 @@
\section1 devtool
- You can use \c devtool to update an existing installer or maintenance tool
+ You can use \c devtool to update an existing installer or \MT
with a new installer base, to dump binary content from an installer or
- maintenance tool to a target, and to execute operations. For a summary of
+ \MT to a target, and to execute operations. For a summary of
available operations, see \l {Operations}.
\c devtool expects the following parameters in the following order:
@@ -1426,12 +1580,12 @@
\li Display additional information.
\row
\li update <binary> <installerbase>
- \li Update an existing installer or maintenance tool with a new
+ \li Update an existing installer or \MT with a new
installer base.
\row
\li dump <binary> <folder>
\li Dump the binary content that belongs to an installer or
- maintenance tool into the target.
+ \MT into the target.
\row
\li operation <mode,name,args,...>
\li Execute an operation with a list of arguments.
@@ -1504,7 +1658,7 @@
\endcode
The installer works only if it can access the repository. If the repository is
- accessed after the installation, the maintenance tool rejects installation.
+ accessed after the installation, the \MT rejects installation.
However, uninstallation is still possible.
A repository can be enabled or disabled by default.
For repositories requiring authentication, the details can also be set here,
@@ -1726,7 +1880,7 @@
If \c{url} is itself relative, it will be resolved against the base URL of the current document.
\c{displayname} specifies how the repository should be named in the \gui Settings page
- of the Maintenance Tool.
+ of the \MT.
\c{name} and \c{password} optionally specify credentials for a protected repository.
@@ -1782,22 +1936,22 @@
\section1 Promoting Updates for the Maintenance Tool
Without additional configuration, both online and offline installers install the
- \e {maintenance tool}, that can be later used to add, update, and remove components.
- Online installers also have an option to install the maintenance tool from an online repository.
- This makes it possible to promote updates for the maintenance tool to take the advantage of latest
+ \e {\MT}, that can be later used to add, update, and remove components.
+ Online installers also have an option to install the \MT from an online repository.
+ This makes it possible to promote updates for the \MT to take the advantage of latest
new features and fixes to the Qt Installer Framework.
You should download the latest release of the Installer Framework distribution that
includes new versions of \l{binarycreator} and \l{installerbase} tools. However, to only
update vendor specific configuration like \c <Name>, \c <Title>, and \c <Publisher> to the new
- maintenance tool, you can use the tools that were used to create the original installer.
+ \MT, you can use the tools that were used to create the original installer.
\section2 Creating the Component Directory Structure for Maintenance Tool
- To make the maintenance tool update installable for end users, you need to prepare an installer
- component for the maintenance tool and create a repository for that component.
+ To make the \MT update installable for end users, you need to prepare an installer
+ component for the \MT and create a repository for that component.
- \note If you already have a component for the maintenance tool available in a repository, you
+ \note If you already have a component for the \MT available in a repository, you
can skip the instructions in this section.
A common convention is to create a \c packages directory containing the metadata and data for
@@ -1808,10 +1962,10 @@
\section2 Compiling the Update Resource
If you want to apply configuration changes, like updating the title, publisher, or product URL
- when the end user updates the maintenance tool, you need to create an update resource file.
+ when the end user updates the \MT, you need to create an update resource file.
Otherwise this step is optional.
- First, you need to compile the resource file that will contain the new maintenance tool
+ First, you need to compile the resource file that will contain the new \MT
configuration and related files:
\code
@@ -1821,26 +1975,43 @@
The command outputs the result into \c update.rcc in the current path.
The \c packages directory argument refers to the previously created directory for the maintenance
- tool component. \c config.xml contains the maintenance tool configuration. This can be the same
- file that was used for creating the online installer that is going to consume the maintenance tool
+ tool component. \c config.xml contains the \MT configuration. This can be the same
+ file that was used for creating the online installer that is going to consume the \MT
repository, or you could make modifications to change some configuration elements like the window
title and product version.
For full reference of elements supported by the configuration file, see \l{Configuration File}.
+ \section2 Getting the Maintenance Tool
+
+ The \MT for Linux and Windows is the same as the \c installerbase
+ executable located in your Qt Installer Framework's installation \c bin folder.
+ For macOS the \MT app bundle can be created using the \c binarycreator
+ tool with the command line switch \c --mt or \c --create-maintenancetool. The
+ name of the macOS app bundle can be configured in config.xml using
+ the \c <MaintenanceToolName> element. The app bundle can be later signed and
+ notarized if needed.
+
+ \code
+ binarycreator -c config/config.xml --mt
+ \endcode
+
\section2 Populating the Maintenance Tool Component
- The \c installerbase executable from Qt Installer Framework's install folder and \c update.rcc
- generated in the \l{Compiling the Update Resource} step should be copied to the component's data
- directory. The meta directory should contain a \c package.xml file with the package information
- elements of your choice. However it is a good idea to mark the component \c <Essential> so that
- it gets automatically installed when running the updater. You may also want to mark the component
+ In Linux and in Windows the \c installerbase executable from Qt Installer
+ Framework's installation folder, or in macOS the \MT app bundle,
+ should be copied to the component's data directory. If you generated the
+ \c update.rcc in the \l{Compiling the Update Resource} step, copy that to
+ the data directory as well. The meta directory should contain a \c package.xml
+ file with the package information elements of your choice. However, it is a
+ good idea to mark the component \c <Essential> so that it gets automatically
+ installed when running the updater. You may also want to mark the component
\c <Virtual> to hide it from the component selection.
For further information about the \c package.xml file, see
\l{Summary of Package Information File Elements}.
- \note If you are providing an update for an existing maintenance tool component, copy and overwrite the
+ \note If you are providing an update for an existing \MT component, copy and overwrite the
existing files with the updated content to the package directory and increase the value of the
\c <Version> element in the \c package.xml file.
@@ -1857,11 +2028,21 @@
Component.prototype.onInstallationStarted = function()
{
if (component.updateRequested() || component.installationRequested()) {
- if (installer.value("os") == "win")
+ if (installer.value("os") == "win") {
component.installerbaseBinaryPath = "@TargetDir@/installerbase.exe";
- else if (installer.value("os") == "x11" || installer.value("os") == "mac")
+ } else if (installer.value("os") == "x11") {
component.installerbaseBinaryPath = "@TargetDir@/installerbase";
-
+ } else if (installer.value("os") == "mac") {
+ // In macOs maintenance tool can be either installerbase from Qt Installer
+ // Framework's install folder, or app bundle created by binarycreator
+ // with --create-maintenancetool switch. "MaintenanceTool.app" -name
+ // may differ depending on what has been defined in config.xml while
+ // creating the maintenance tool.
+ // Use either of the following (not both):
+
+ // component.installerbaseBinaryPath = "@TargetDir@/installerbase";
+ component.installerbaseBinaryPath = "@TargetDir@/MaintenanceTool.app";
+ }
installer.setInstallerBaseBinary(component.installerbaseBinaryPath);
var updateResourceFilePath = installer.value("TargetDir") + "/update.rcc";
@@ -1878,7 +2059,7 @@
After preparation the component should be uploaded to an existing or a new online repository
to make it available for end users. The instructions on how to create repositories on
this page and \l{Creating Repositories} apply also for the component containing the
- maintenance tool update.
+ \MT update.
*/
/*!
diff --git a/doc/noninteractive.qdoc b/doc/noninteractive.qdoc
index 2b5f9b964..d22a17800 100644
--- a/doc/noninteractive.qdoc
+++ b/doc/noninteractive.qdoc
@@ -80,6 +80,21 @@
For more information about the JavaScript global objects that you can use
in control scripts, see \l{Scripting API}.
+ In addition to the predefined global objects, the scripting API supports working
+ with other objects derived from \c QObject. In the above code example the
+ \c{gui.currentPageWidget()} method returns a widget of type \c QWidget.
+
+ The scripting API makes use of Qt's object trees. Widgets derived from \c QObject export
+ their named child objects as properties for the JavaScript object, where the name of the
+ property is the same as the child object's \c{QObject::objectName}. The default properties
+ and their access functions of objects derived from \c QObject can also be used in the scripts.
+
+ For example, in the above code the \c MessageLabel object from class \c QLabel is a child
+ of the \c widget. The \c setText() is the setter access function for its \c QLabel::text property.
+
+ In addition to properties, the signals and public slots of objects derived from \c QObject
+ can be used in both controller and component scripts.
+
\section1 Predefined Installer Pages
The QInstaller JavaScript object provides access to the following predefined
@@ -126,7 +141,7 @@
\row
\li \c MessageLabel
\li Displays a message. By default, it displays the
- "Welcome to the \l{ProductNameTarget}{<Name>} Setup Wizard" message.
+ "Welcome to the \l{ProductNameTarget}{<Name>} Setup" message.
\row
\li \c InformationLabel
@@ -140,15 +155,15 @@
\row
\li \c PackageManagerRadioButton
- \li The package manager radio button shown on the page while running as maintenance tool.
+ \li The package manager radio button shown on the page while running as \MT.
\row
\li \c UpdaterRadioButton
- \li The updater radio button shown on the page while running as maintenance tool.
+ \li The updater radio button shown on the page while running as \MT.
\row
\li \c UninstallerRadioButton
- \li The uninstaller radio button shown on the page while running as maintenance tool.
+ \li The uninstaller radio button shown on the page while running as \MT.
Selected by default.
\endtable
@@ -169,10 +184,10 @@
\row
\li \c packageManagerCoreTypeChanged()
- \li Connect to this signal if you want to be notified when the type of maintenance tool
+ \li Connect to this signal if you want to be notified when the type of \MT
changes.
\note The signal is only emitted when the user has started the binary as so called
- maintenance tool (after the installation) and switches between the radio buttons.
+ \MT (after the installation) and switches between the radio buttons.
\endtable
Example code:
@@ -552,7 +567,7 @@
\row
\li \c WriteError
\li OK
- \li An error occurred while writing the maintenance tool.
+ \li An error occurred while writing the \MT.
\row
\li \c ElevationError
diff --git a/doc/operations.qdoc b/doc/operations.qdoc
index c61e2af0c..e148d6fc1 100644
--- a/doc/operations.qdoc
+++ b/doc/operations.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,6 +40,10 @@
the installer and an \e UNDO step that contains instructions for the
uninstaller.
+ Some operations especially in Windows might need native separators. Use
+ \c installer.toNativeSeparators() to convert separators to ones that are
+ appropriate for the underlying operating system.
+
\section1 Summary of Operations
The following table summarizes the available operations and their syntax in component
@@ -65,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
@@ -81,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
@@ -101,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
@@ -149,12 +176,13 @@
\c linkname.
On Windows, this creates a .lnk file which can have
\c arguments. Furthermore, \c filename can be
- an HTTP or FTP URL in which case a URL shortcut is created.
+ an HTTP, HTTPS or FTP URL in which case a URL shortcut is created.
\note If the \c @AllUsersStartMenuProgramsPath@ is used for
placing the shortcut, then due to a problem on Windows, it will
drop any arguments to the target and any description set. In
this case, you should place the shortcut elsewhere and copy it
to the location desired.
+ \note Creating a shortcut to a network share is not supported.
The operation is currently not implemented on other platforms.
\row
\li CreateDesktopEntry
@@ -286,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/console.qdoc b/doc/scripting-api/console.qdoc
index 51825de9e..7785d9223 100644
--- a/doc/scripting-api/console.qdoc
+++ b/doc/scripting-api/console.qdoc
@@ -37,7 +37,7 @@
\l{console::log()}{log} method and \l installer object
\l{installer::isUpdater()}, \l{installer::isUninstaller()}, and
\l{installer::isPackageManager()} methods to display a message that
- indicates whether the maintenance tool is currently being used to update,
+ indicates whether the \MT is currently being used to update,
remove, or add components.
\code
diff --git a/doc/scripting-api/gui.qdoc b/doc/scripting-api/gui.qdoc
index 7047cfe0e..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.
@@ -58,6 +58,13 @@
*/
/*!
+ \qmlsignal gui::aboutApplicationClicked()
+
+ This signal is emitted when the about application menu and dialog
+ on macOS are shown.
+*/
+
+/*!
\qmlsignal gui::settingsButtonClicked()
This signal is emitted when the \uicontrol Settings button is clicked.
@@ -119,9 +126,35 @@
*/
/*!
+ \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.
+ Shows the \uicontrol Settings button if \a show is \c true. This function
+ overrides the visibility of the \uicontrol Settings button in all pages. Be
+ careful when showing the settings button so that users cannot change network
+ settings while downloading data in the background. Changing the
+ settings will restart the wizard and switch back to the introduction page.
*/
/*!
diff --git a/doc/scripting-api/packagemanagercore.qdoc b/doc/scripting-api/packagemanagercore.qdoc
index 3466c70f6..ecf4b1eaa 100644
--- a/doc/scripting-api/packagemanagercore.qdoc
+++ b/doc/scripting-api/packagemanagercore.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -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()
*/
/*!
@@ -325,15 +307,25 @@
*/
/*!
+ \qmlmethod boolean installer::recalculateAllComponents()
+
+ Recalculates all components to install and uninstall. Returns \c true
+ on success, \c false otherwise.
+*/
+
+/*!
\qmlmethod void installer::componentsToInstallNeedsRecalculation()
+ \deprecated [4.5] Use recalculateAllComponents() instead.
Ensures that component dependencies are re-calculated.
*/
/*!
\qmlmethod void installer::clearComponentsToInstallCalculated()
-
- Forces a recalculation of components to install.
+ \deprecated [4.5] Installer framework recalculates components each time the calculation
+ of components to install is requested, so there is no need to call this anymore, and the
+ method does nothing. On previous versions calling this forced a recalculation of
+ components to install.
*/
/*!
@@ -605,6 +597,18 @@
*/
/*!
+ \qmlmethod bool installer::hasAdminRights()
+
+ Returns \c true if the installer has admin rights. For example, if the installer
+ was started with a root account, or the internal admin rights elevation is active.
+
+ Returns \c false otherwise.
+
+ \sa gainAdminRights()
+ \sa dropAdminRights()
+*/
+
+/*!
\qmlmethod boolean installer::isProcessRunning(string name)
Returns \c true if a process with \a name is running. On Windows, the comparison
@@ -623,15 +627,23 @@
/*!
\qmlmethod void installer::setAllowedRunningProcesses(stringlist processes)
+ \deprecated [4.6] The \MT no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
Sets additional \a processes that can run when
- updating with the maintenance tool.
+ updating with the \MT.
*/
/*!
\qmlmethod stringlist installer::allowedRunningProcesses()
+ \deprecated [4.6] The \MT no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
Returns processes that are allowed to run when updating with
- the maintenance tool.
+ the \MT.
*/
/*!
@@ -746,7 +758,7 @@
/*!
\qmlmethod void installer::setInstallerBaseBinary(string path)
- Sets the \c installerbase binary to use when writing the maintenance tool.
+ Sets the \c installerbase binary to use when writing the \MT.
Set the \a path if an update to the binary is available.
If not set, the executable segment of the running installer or uninstaller
@@ -754,10 +766,16 @@
*/
/*!
- \qmlmethod string installer::value(string key, string defaultValue = "")
+ \qmlmethod string installer::value(string key, string defaultValue = "", int format = 0)
- Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is
- returned. Additionally, on Windows, \a key can be a registry key.
+ Returns the installer value for \a key. If \a key is not known to the system,
+ \a defaultValue is returned. Additionally, on Windows, \a key can be a
+ registry key. Optionally, you can specify the \a format of the registry key.
+ By default the \a format is QSettings::NativeFormat. For accessing
+ the 32-bit system registry from a 64-bit application running on 64-bit
+ Windows, use QSettings::Registry32Format. For accessing the 64-bit system
+ registry from a 32-bit application running on 64-bit Windows, use
+ QSettings::Registry64Format.
\sa setValue, containsValue, valueChanged
*/
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/scripting-api/qsettings.qdoc b/doc/scripting-api/qsettings.qdoc
new file mode 100644
index 000000000..04a0fe674
--- /dev/null
+++ b/doc/scripting-api/qsettings.qdoc
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \qmltype QSettings
+ \inqmlmodule scripting
+
+ \brief Provides enums for accessing QSettings format in Windows.
+
+ On Windows, a registry key can be asked using \l{installer::value}.
+ For accessing the 32-bit system registry from a 64-bit application running
+ on 64-bit Windows, use \l{QSettings::Registry32Format}{QSettings.Registry32Format}.
+ For accessing the 64-bit system registry from a 32-bit application running
+ on 64-bit Windows, use \l{QSettings::Registry64Format}{QSettings.Registry64Format}.
+ By default the \l{QSettings::NativeFormat}{QSettings.NativeFormat}is used.
+*/
+
+/*!
+ \qmlproperty enumeration QSettings::Format
+
+ The \l{installer::value} method can take one of the following enums as an argument:
+
+ \list
+ \li \l{QSettings::NativeFormat}{QSettings.NativeFormat}
+ \li \l{QSettings::Registry32Format}{QSettings.Registry32Format}
+ \li \l{QSettings::Registry64Format}{QSettings.Registry64Format}
+ \li \l{QSettings::IniFormat}{QSettings.IniFormat}
+ \li \l{QSettings::InvalidFormat}{QSettings.InvalidFormat}
+ \endlist
+
+ The enum values correspond to the values of the
+ \l{QSettings::Format} enum with the same names.
+*/
diff --git a/doc/scripting.qdoc b/doc/scripting.qdoc
index bb547a0d6..1dde6576a 100644
--- a/doc/scripting.qdoc
+++ b/doc/scripting.qdoc
@@ -94,7 +94,7 @@
\li Reference to the \l QInstaller of the component
\row
\li component
- \li Reference to the \l Component of the component
+ \li Reference to the \l{https://doc.qt.io/qtinstallerframework/qinstaller-component.html}{Component}. of the component
\endtable
\section1 Message Boxes
@@ -375,4 +375,28 @@
\li ApplicationsDirX64
\li \c {C:\Program Files}
\endtable
+
+ \section1 Using postLoad in Component Script
+ By default, component scripts are evaluated before the install tree view
+ is shown. This can have performance cost if there is a huge amount of
+ components with component scripts. The \c postLoad attribute introduces a way
+ to evaluate the component script right before installation starts, only for
+ the components that are selected for installation or update:
+ \code
+ <Script postLoad="true">my_install_script.qs</Script>
+ \endcode
+ Whether \c postLoad can be set to \c true must be considered case by case,
+ depending on the contents of the script. For example, if the script contents
+ affect the install tree view, like setting \c <Default> to \c true, setting
+ new dependencies, or adding new wizard pages, \c postLoad must not be used or
+ it must be set to \c false. If the script contains only methods
+ that are run during the installation, \c postLoad can be set to \c true. For
+ example, all overridden \c operation functions are run during installation.
+ For more information, see \l {Adding Operations to Components}. If you are not
+ sure when to use \c postLoad, then don't use it. The performance cost is
+ huge only when there are thousands of scripts to be evaluated.
+
+ Both \c <Script postLoad="true"> and \c <Script> tags can be used at the same time.
+ This means that one component can have one script that is evaluated when the installation
+ starts and another script that is evaluated before the install tree view is shown.
*/
diff --git a/doc/systeminfo.qdoc b/doc/systeminfo.qdoc
index 1f260abdb..3695dbc07 100644
--- a/doc/systeminfo.qdoc
+++ b/doc/systeminfo.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -52,6 +52,25 @@
*/
/*!
+ \qmlproperty string systemInfo::buildCpuArchitecture
+
+ The architecture of the CPU that the application was compiled for, in text format.
+
+ Possible values include:
+ \list
+ \li "i386"
+ \li "x86_64"
+ \li "arm64"
+ \endlist
+
+ Note that this may not match the actual CPU that the application is running on if
+ 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() currentCpuArchitecture
+*/
+
+/*!
\qmlproperty string systemInfo::kernelType
The type of the operating system kernel the installer was compiled for. It is also the
@@ -98,7 +117,7 @@
\list
\li "windows"
\li "opensuse" (for the Linux openSUSE distribution)
- \li "osx"
+ \li "macos"
\endlist
\sa QSysInfo::productType()
diff --git a/doc/tutorial.qdoc b/doc/tutorial.qdoc
index 1f5782317..152f392c4 100644
--- a/doc/tutorial.qdoc
+++ b/doc/tutorial.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,37 +37,37 @@
\image ifw-introduction-page.png "Introduction page"
- This section describes the following tasks that you must accomplish to
+ This section describes the following tasks that you must carry out to
create the installer:
\list 1
- \li Create a \e {package directory} that will contain all the
+ \li Create a \e {package directory} that will have all the
configuration files and installable packages.
- \li Create a \e {configuration file} that contains information about how
+ \li Create a \e {configuration file} that has information about how
to build the installer binaries and online repositories.
- \li Create a \e {package information file} that contains information
+ \li Create a \e {package information file} that has information
about the installable components.
\li Create installer content and copy it to the package directory.
\li Use the \c binarycreator tool to create the \e installer.
- The installer pages are created by using the information you provide
- in the configuration and package information file.
+ To create the installer pages, use the information set in the
+ configuration and package information file.
\endlist
- The example files are located in the \c{examples\tutorial} directory in the
+ You can find the example files in the \c{examples\tutorial} directory in the
Qt Installer Framework repository.
\section1 Creating a Package Directory
Create a directory structure that reflects the design of the installer and
- allows the installer to be extended in the future. The directory must
- contain subdirectories called \c config and \c packages.
+ allows for future extension. The directory must contain subdirectories
+ called \c config and \c packages.
\image ifw-tutorial-files.png
@@ -80,37 +80,37 @@
\quotefile ../examples/tutorial/config/config.xml
- The configuration file specifies the following information that is
- displayed on the introduction page:
+ The configuration file includes the following information for the
+ introduction page:
\list
- \li The \c <Title> element specifies the installer name displayed on the
- title bar (1).
+ \li The \c <Title> element sets the installer name and displays it on
+ the title bar (1).
- \li The \c <Name> element specifies the application name that is added to
- the page name and introduction text (2).
+ \li The \c <Name> element sets the application name and adds it to
+ the page number and introduction text (2).
\endlist
\image ifw-tutorial-introduction-page.png "Introduction page"
- The other elements are used to customize the behavior of the installer:
+ The other elements customize the behavior of the installer:
\list
- \li The \c <Version> element specifies the application version number.
+ \li The \c <Version> element sets the application version number.
- \li The \c <Publisher> element specifies the publisher of the software
+ \li The \c <Publisher> element sets the publisher of the software
(as shown in the Windows Control Panel, for example).
- \li The \c <StartMenuDir> element specifies the name of the default
+ \li The \c <StartMenuDir> element sets the name of the default
program group for the product in the Windows \gui Start menu.
- \li The \c <TargetDir> element specifies that the default target
- directory displayed to users is \c InstallationDirectory in the home
- directory of the current user (because the predefined variable
- \c @HomeDir@ is used as a part of the value). For more information,
+ \li The \c <TargetDir> element sets and displays \c InstallationDirectory
+ in the home directory of the current user as the default target
+ directory displayed to users (because it uses the predefined
+ variable \c @HomeDir@ as part of the value). For more information,
see \l{Predefined Variables}.
\endlist
@@ -120,30 +120,28 @@
\section1 Creating a Package Information File
- In this easy scenario, the installer handles only one component that is
- called \c{com.vendor.product}. To provide the installer with information
+ In this easy scenario, the installer handles only one component,
+ \c{com.vendor.product}. To give the installer information
about the component, create a file called \c package.xml with the
following contents and place it in the \c meta directory:
\quotefile ../examples/tutorial/packages/com.vendor.product/meta/package.xml
- The elements in the example file are described in more detail below.
-
+ Below is a more detailed description of the elements in the example file.
For more information about the package information file, see
\l{Package Information File Syntax}.
\section2 Specifying Component Information
- The information from the following elements is displayed on the component
- selection page:
+ The component selection page displays information from the following elements:
\list
- \li The \c <DisplayName> element specifies the name of the component in
- the list of components (1).
+ \li The \c <DisplayName> element sets the name of the component in the
+ component list (1).
- \li The \c <Description> element specifies the text that is displayed when
- the component is selected (2).
+ \li The \c <Description> element sets and displays text based on the
+ selected component (2).
\endlist
@@ -151,29 +149,31 @@
\section2 Specifying Installer Version
- The \c <Version> element enables you to promote updates to users when they
- become available.
+ The \c <Version> element offers updates to users when they become
+ available.
\section2 Adding Licenses
- The \c <License> element specifies the name of the file that contains the text
- for the license agreement (1) that is displayed on the license check page:
+ The \c <License> element sets the name of the file, which has the
+ license agreement text (1). The license check page displays this license
+ text.
\image ifw-tutorial-license-check.png "License check page"
\section2 Selecting Default Contents
- The \c <Default> element specifies whether the component is selected by
- default. The value \c true sets the component as selected. In this example,
- we use the value \c script to resolve the value during runtime. The
- name of the JavaScript script file, installscript.qs, is specified in the
- \c <Script> element.
+ The \c <Default> element specifies whether the selected component
+ is a default value. The value \c true sets the component as selected. This
+ example uses the value \c script to resolve the value during runtime. The
+ \c <Script> element sets the name of the JavaScript script file,
+ installscript.qs.
\section1 Creating Installer Content
- Content to be installed is stored in the \c data directory of a component.
+ The \c data directory of a component can store content available for
+ installation.
As there is only one component, place the data in the
- \c{packages/com.vendor.product/data} directory. The example already contains
+ \c{packages/com.vendor.product/data} directory. The example already has
a file for testing purposes, but you can place basically any files in the
directory.
@@ -184,7 +184,7 @@
You are now ready to create your first installer. Switch to the
\c examples\tutorial directory on the command line. To create an installer called
- YourInstaller.exe that contains the packages identified by
+ YourInstaller.exe that has the packages identified by
com.vendor.product, enter the following command:
\list
@@ -201,15 +201,13 @@
\endlist
- The installer is created in the current directory and you can deliver it to
- end users.
+ \IFW creates the installer in the current directory and you can deliver it
+ to end users.
For more information about using the \c binarycreator tool, see
\l{binarycreator}.
- \note If an error message is displayed when you run the tutorial installer,
+ \note If an error message appears when you run the tutorial installer,
check that you used a statically built Qt to create the installer. For more
information, see \l{Configuring Qt}.
*/
-
-
diff --git a/examples/componentalias/README b/examples/componentalias/README
new file mode 100644
index 000000000..8253c6701
--- /dev/null
+++ b/examples/componentalias/README
@@ -0,0 +1,5 @@
+Create an installer that shows how component aliases work
+
+Generate installer with:
+
+binarycreator -c config/config.xml -p packages installer
diff --git a/examples/componentalias/componentalias.pro b/examples/componentalias/componentalias.pro
new file mode 100644
index 000000000..415df49d5
--- /dev/null
+++ b/examples/componentalias/componentalias.pro
@@ -0,0 +1,13 @@
+TEMPLATE = aux
+
+INSTALLER = installer
+
+INPUT = $$PWD/config/config.xml $$PWD/packages
+example.input = INPUT
+example.output = $$INSTALLER
+example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT}
+example.CONFIG += target_predeps no_link combine
+
+QMAKE_EXTRA_COMPILERS += example
+
+OTHER_FILES = README
diff --git a/examples/componentalias/config/aliases.xml b/examples/componentalias/config/aliases.xml
new file mode 100644
index 000000000..baf89f9b1
--- /dev/null
+++ b/examples/componentalias/config/aliases.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<Aliases>
+ <Alias>
+ <Name>set1</Name>
+ <DisplayName>Component Set 1</DisplayName>
+ <Description>Alias for components A, B, and C</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>componentA,componentB,componentC</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set2</Name>
+ <DisplayName>Component Set 2</DisplayName>
+ <Description>Alias for components D, and E</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>componentD,componentE</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-full</Name>
+ <DisplayName>Full Component Set</DisplayName>
+ <Description>Alias for full installation</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set1,set2</RequiredAliases>
+ </Alias>
+</Aliases>
+
diff --git a/examples/componentalias/config/config.xml b/examples/componentalias/config/config.xml
new file mode 100644
index 000000000..440930355
--- /dev/null
+++ b/examples/componentalias/config/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Installer>
+ <Name>Component Alias Example</Name>
+ <Version>1.0.0</Version>
+ <Title>Component Alias Example</Title>
+ <Publisher>Qt-Project</Publisher>
+ <StartMenuDir>Qt IFW Examples</StartMenuDir>
+ <TargetDir>@HomeDir@/IfwExamples/componentalias</TargetDir>
+ <AliasDefinitionsFile>aliases.xml</AliasDefinitionsFile>
+</Installer>
diff --git a/examples/componentalias/packages/componentA/data/installcontentA.txt b/examples/componentalias/packages/componentA/data/installcontentA.txt
new file mode 100644
index 000000000..f40001983
--- /dev/null
+++ b/examples/componentalias/packages/componentA/data/installcontentA.txt
@@ -0,0 +1,2 @@
+This file will be installed into the target directory....
+
diff --git a/examples/componentalias/packages/componentA/meta/package.xml b/examples/componentalias/packages/componentA/meta/package.xml
new file mode 100644
index 000000000..729989fd9
--- /dev/null
+++ b/examples/componentalias/packages/componentA/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component A</DisplayName>
+ <Description>This component is a part of a component alias.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+</Package>
diff --git a/examples/componentalias/packages/componentB/data/installcontentB.txt b/examples/componentalias/packages/componentB/data/installcontentB.txt
new file mode 100644
index 000000000..f40001983
--- /dev/null
+++ b/examples/componentalias/packages/componentB/data/installcontentB.txt
@@ -0,0 +1,2 @@
+This file will be installed into the target directory....
+
diff --git a/examples/componentalias/packages/componentB/meta/package.xml b/examples/componentalias/packages/componentB/meta/package.xml
new file mode 100644
index 000000000..c6eba4223
--- /dev/null
+++ b/examples/componentalias/packages/componentB/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component B</DisplayName>
+ <Description>This component is a part of a component alias.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>90</SortingPriority>
+</Package>
diff --git a/examples/componentalias/packages/componentC/data/installcontentC.txt b/examples/componentalias/packages/componentC/data/installcontentC.txt
new file mode 100644
index 000000000..f40001983
--- /dev/null
+++ b/examples/componentalias/packages/componentC/data/installcontentC.txt
@@ -0,0 +1,2 @@
+This file will be installed into the target directory....
+
diff --git a/examples/componentalias/packages/componentC/meta/package.xml b/examples/componentalias/packages/componentC/meta/package.xml
new file mode 100644
index 000000000..59af43132
--- /dev/null
+++ b/examples/componentalias/packages/componentC/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component C</DisplayName>
+ <Description>This component is a part of a component alias.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+</Package>
diff --git a/examples/componentalias/packages/componentD/data/installcontentD.txt b/examples/componentalias/packages/componentD/data/installcontentD.txt
new file mode 100644
index 000000000..f40001983
--- /dev/null
+++ b/examples/componentalias/packages/componentD/data/installcontentD.txt
@@ -0,0 +1,2 @@
+This file will be installed into the target directory....
+
diff --git a/examples/componentalias/packages/componentD/meta/package.xml b/examples/componentalias/packages/componentD/meta/package.xml
new file mode 100644
index 000000000..eccbd8d4a
--- /dev/null
+++ b/examples/componentalias/packages/componentD/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component D</DisplayName>
+ <Description>This component is a part of a component alias.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>70</SortingPriority>
+</Package>
diff --git a/examples/componentalias/packages/componentE/data/installcontentE.txt b/examples/componentalias/packages/componentE/data/installcontentE.txt
new file mode 100644
index 000000000..f40001983
--- /dev/null
+++ b/examples/componentalias/packages/componentE/data/installcontentE.txt
@@ -0,0 +1,2 @@
+This file will be installed into the target directory....
+
diff --git a/examples/componentalias/packages/componentE/meta/package.xml b/examples/componentalias/packages/componentE/meta/package.xml
new file mode 100644
index 000000000..95b4ebd72
--- /dev/null
+++ b/examples/componentalias/packages/componentE/meta/package.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<Package>
+ <DisplayName>Component E</DisplayName>
+ <Description>This component is a part of a component alias.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>60</SortingPriority>
+</Package>
diff --git a/examples/doc/changeuserinterface.qdoc b/examples/doc/changeuserinterface.qdoc
index 8de6bcadd..012f6bf43 100644
--- a/examples/doc/changeuserinterface.qdoc
+++ b/examples/doc/changeuserinterface.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,14 +30,14 @@
\ingroup qtifwexamples
\title Change Installer UI Example
- \brief Using a component script to modify the installer UI.
+ \brief Using a component script to change the installer UI.
\image qtifw-examples-changeuserinterface.png
\e {Change Installer UI} demonstrates how to use the \c Component() function
- to modify the default text for the check box label on the
+ to change the default text for the check box label on the
\l{License Agreement Page}{license check page}. This example does not install
- any components, but we specify a dummy component in the package information
+ any components, but it specifies a dummy component in the package information
file, because installers without components are not allowed.
\include installerfw-examples-configuring.qdocinc
@@ -49,26 +49,26 @@
\list
\li The \c <Default> element specifies whether the component is
preselected for installation in the user interface by default.
- \li The \c <Script> element specifies the file name of the JavaScript
- file that is loaded to perform operations.
- \li The \c <Licenses> element specifies the \c name of the license
- agreement to be accepted by the end user and the filename of the
- \c file that contains the license.
+ \li The \c <Script> element sets the file name of the loaded JavaScript
+ file.
+ \li The \c <Licenses> element sets the \c name of the license
+ agreement that the end user accepts, as well as the filename of the
+ \c file that has the license.
\endlist
\quotefile changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/package.xml
- \section1 Modifying UI Text
+ \section1 Updating UI Text
- In installscript.qs, we call the \c Component() function to add the license
- check page and to connect to the \c changeLicenseLabels signal when end
+ In installscript.qs, the \c Component() function adds the license
+ check page and connects to the \c changeLicenseLabels signal when end
users enter the page:
\quotefromfile changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/installscript.qs
\skipto Component()
\printuntil }
- We use the \c changeLicenseLabels function to change the text label for the
+ The \c changeLicenseLabels function changes the text label for the
accept license check box on the page:
\printuntil }
diff --git a/examples/doc/componentalias.qdoc b/examples/doc/componentalias.qdoc
new file mode 100644
index 000000000..c4e7c8e99
--- /dev/null
+++ b/examples/doc/componentalias.qdoc
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example componentalias
+ \ingroup qtifwexamples
+ \title Component Alias Example
+
+ \brief Using installer's aliases.xml file to define component aliases and their
+ relations to other aliases and components.
+
+ \e{Component Alias} illustrates how to specify component aliases and how they influence
+ the installation and maintenance processes for command line usage.
+
+ \include installerfw-examples-configuring.qdocinc
+
+ \list
+ \li The \c <AliasDefinitionsFile> element is set to \c aliases.xml to
+ tell the installer the source for the alias definitions.
+ \endlist
+
+ \quotefile componentalias/config/config.xml
+
+ In this example, the aliases.xml file declares three component aliases. Two aliases,
+ \c set1 and \c set2 require a subset of available components, while \c set-full
+ requires the former two aliases, thus all available components.
+
+ \quotefile componentalias/config/aliases.xml
+
+ For full reference of the alias definitions file syntax, see \l{Alias Definition File}.
+
+ \include installerfw-examples-packaging.qdocinc
+
+ \section1 Referring to Component Aliases from Command Line
+
+ The declared component aliases can be used from the installer's command line interface
+ with the \c search and \c install commands.
+
+ \section2 Searching Available Component Aliases
+
+ The \c search command will by default search available aliases first, and then normal
+ components:
+
+ \code
+ $ installer search
+ \endcode
+
+ The command will print a table of aliases that were declared in the alias definition file:
+
+ \code
+ Name: set-full
+ Display name: Full Component Set
+ Description: Alias for full installation
+ Version: 1.0.0
+ Components:
+ Required aliases: set1,set2
+ ========================================
+ Name: set1
+ Display name: Component Set 1
+ Description: Alias for components A, B, and C
+ Version: 1.0.0
+ Components: componentA,componentB,componentC
+ Required aliases:
+ ========================================
+ Name: set2
+ Display name: Component Set 2
+ Description: Alias for components D, and E
+ Version: 1.0.0
+ Components: componentD,componentE
+ Required aliases:
+ \endcode
+
+ \section2 Installing Available Component Aliases
+
+ The \c install command can be used to install the components referred by a component alias:
+
+ \code
+ $ installer install set1
+ \endcode
+
+ This command is practically equivalent to selecting the components explicitly by their names:
+
+ \code
+ $ installer install componentA componentB componentC
+ \endcode
+
+ When selecting aliases for installation, the component changes summary will show
+ separate sections for components selected by a aliases, to differentiate from manual
+ selection:
+
+ \code
+ $ installer install set1 componentD
+ \endcode
+
+ This will output the following component changes summary:
+
+ \code
+ Components selected by alias "set1":
+ componentA
+ componentB
+ componentC
+ Selected components without dependencies:
+ componentD
+ \endcode
+
+ The \c <RequiredComponents> property does not declare a hard dependency for components,
+ and those can be later on updated or uninstalled individually from the alias:
+
+ \code
+ $ maintenancetool remove componentA
+ \endcode
+
+ After this, selecting the \c set1 alias for installation again would also select
+ \c componentA for reinstallation.
+
+ \section1 Virtual Component Aliases
+
+ Component aliases may be declared \c <Virtual>. This works similarly with virtual components,
+ so that they cannot be manually selected by the user and do not show in component search results.
+ Such aliases must be included by other aliases with the \c <RequiredAliases> property.
+
+ \section1 Optional Dependencies for Component Aliases
+
+ In addition to dependencies declared with \c <RequiredComponents> and \c <RequiredAliases> as
+ shown in this example, aliases may have optional dependencies declared with \c <OptionalComponents>
+ and \c <OptionalAliases> properties. The latter properties differ from the requirements in that
+ they do not cause the alias declaring the requirement to become unselectable, in case the referenced
+ components or aliases are not known to the installer.
+
+ This provides the packager some flexibility for declaring the dependencies. For example, an
+ installer may be expected to use an optional repository, which contains components that a
+ component alias optionally depends on, but the alias should be also available for installation
+ when the repository is disabled.
+
+ \include installerfw-examples-generating.qdocinc
+*/
diff --git a/examples/doc/online.qdoc b/examples/doc/online.qdoc
index f901c3445..0c14a8c26 100644
--- a/examples/doc/online.qdoc
+++ b/examples/doc/online.qdoc
@@ -150,7 +150,7 @@
only updates packages that are new, or have a higher version number.
See also the \l{Summary of repogen Parameters}.
- If you then run the maintenance tool from the previous installation and
+ If you then run the \MT from the previous installation and
select \gui {Update Components}, you should see that an update of package
\c A is available.
diff --git a/examples/doc/treename.qdoc b/examples/doc/treename.qdoc
index 22acb8e08..774b6e1fa 100644
--- a/examples/doc/treename.qdoc
+++ b/examples/doc/treename.qdoc
@@ -106,7 +106,7 @@
\section2 Multiple Tree Names in One Component Branch
Components in a single branch (\e A, \e{A.sub1}, \e{A.sub1.sub1}, \e{A.sub1.sub1.sub2} and so
- on) may each declare a separate tree name. The installer or maintenance tool calculates the
+ on) may each declare a separate tree name. The installer or \MT calculates the
new locations for components in an ascending order from leaf components to root components, so that
\e{A.sub1.sub1.sub2} is moved first and \e A last.
@@ -120,18 +120,18 @@
of the installer has installed component \e A with tree name \e ANewName1, and the
repository is updated with a new version of component \e A that declares tree name
\e ANewName2, the new location is only applied when the user updates the component
- locally with the maintenance tool.
+ locally with the \MT.
\li The automatic tree names of children of components with tree name and \c moveChildren
set to \c true are moved with the parent regardless if installed or not. Therefore, if
the user has installed component \e{A.sub1} but not component \e A, and \e A is updated
in the repository with a new tree name, the \e{A.sub1} uses also the new tree name in
- maintenance tool's component tree.
+ \MT's component tree.
\endlist
\section2 Tree Name and Name Conflicts
The tree names may conflict with existing component names or other tree names. Depending on whether
- the installer or maintenance tool is configured to allow unstable components with
+ the installer or \MT is configured to allow unstable components with
\c <AllowUnstableComponents> configuration file element, the offending components are either
registered as unstable components with their original name or not registered at all.
diff --git a/examples/examples.pro b/examples/examples.pro
index 283f81abe..eca806812 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
SUBDIRS += \
changeuserinterface \
+ componentalias \
componenterror \
dependencies \
dynamicpage \
diff --git a/installerfw.pri b/installerfw.pri
index 40b38ffad..e2ae12f11 100644
--- a/installerfw.pri
+++ b/installerfw.pri
@@ -3,13 +3,14 @@
}
IFW_PRI_INCLUDED = 1
-IFW_VERSION_STR = 4.4.0
-IFW_VERSION = 0x040400
-IFW_VERSION_WIN32 = 4,4,0,0
+IFW_VERSION_STR = 5.0.0
+IFW_VERSION = 0x050000
+IFW_VERSION_WIN32 = 5,0,0,0
IFW_VERSION_STR_WIN32 = $$IFW_VERSION_STR\0
IFW_REPOSITORY_FORMAT_VERSION = 1.0.0
+IFW_CACHE_FORMAT_VERSION = 1.2.0
IFW_NEWLINE = $$escape_expand(\\n\\t)
isEmpty(IFW_DISABLE_TRANSLATIONS): IFW_DISABLE_TRANSLATIONS = $$(IFW_DISABLE_TRANSLATIONS)
@@ -97,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
@@ -136,10 +146,17 @@ macx:LIBS += -framework Carbon -framework Security
QT += uitools core-private
CONFIG(static, static|shared) {
- win32:QT += winextras
+ win32:lessThan(QT_MAJOR_VERSION, 6):QT += winextras
QT += concurrent network qml xml
+ greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat
}
-CONFIG += depend_includepath no_private_qt_headers_warning c++11
+CONFIG += depend_includepath no_private_qt_headers_warning
+versionAtLeast(QT_MAJOR_VERSION, 6) {
+ CONFIG+=c++17
+} else {
+ CONFIG+=c++11
+}
+
win32:CONFIG += console
exists(".git") {
@@ -157,7 +174,8 @@ DEFINES += NOMINMAX QT_NO_CAST_FROM_ASCII QT_STRICT_ITERATORS QT_USE_QSTRINGBUIL
IFW_VERSION=$$IFW_VERSION \
IFW_VERSION_STR_WIN32=$$IFW_VERSION_STR_WIN32 \
IFW_VERSION_WIN32=$$IFW_VERSION_WIN32
-DEFINES += IFW_REPOSITORY_FORMAT_VERSION=$$IFW_REPOSITORY_FORMAT_VERSION
+DEFINES += IFW_REPOSITORY_FORMAT_VERSION=$$IFW_REPOSITORY_FORMAT_VERSION \
+ IFW_CACHE_FORMAT_VERSION=$$IFW_CACHE_FORMAT_VERSION
win32-g++*: LIBS += -lmpr -luuid
@@ -171,7 +189,7 @@ CONFIG(libarchive):equals(TEMPLATE, app) {
LIBS += -llibarchive
!isEmpty(IFW_ZLIB_LIBRARY) {
LIBS += $$IFW_ZLIB_LIBRARY
- } else {
+ } else:!contains(QT_MODULES, zlib) {
unix:LIBS += -lz
win32:LIBS += -lzlib
}
diff --git a/installerfw.pro b/installerfw.pro
index ee9e5b1cc..f8f2b5469 100644
--- a/installerfw.pro
+++ b/installerfw.pro
@@ -1,10 +1,11 @@
TEMPLATE = subdirs
-SUBDIRS += src tools doc
+SUBDIRS += src tools
tools.depends = src
requires(!cross_compile)
include (installerfw.pri)
+include (doc/doc.pri)
BUILD_TESTS = $$(BUILDTESTS)
isEmpty(BUILD_TESTS):BUILD_TESTS=$${BUILDTESTS}
diff --git a/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
index 37f4fe991..e101db855 100644
--- a/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
@@ -37,7 +37,7 @@ void SplitCommandLine(const UString &s, UStringVector &parts)
parts.Clear();
const QString cmdLine = QString::fromStdWString(static_cast<const wchar_t*>(s));
- const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts);
+ const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts);
foreach (const QString &arg, args)
parts.Add(arg.toStdWString().c_str());
}
diff --git a/src/libs/3rdparty/libarchive/archive.h b/src/libs/3rdparty/libarchive/archive.h
index 46041eb08..618b1a176 100644
--- a/src/libs/3rdparty/libarchive/archive.h
+++ b/src/libs/3rdparty/libarchive/archive.h
@@ -36,7 +36,7 @@
* assert that ARCHIVE_VERSION_NUMBER >= 2012108.
*/
/* Note: Compiler will complain if this does not match archive_entry.h! */
-#define ARCHIVE_VERSION_NUMBER 3006001
+#define ARCHIVE_VERSION_NUMBER 3007001
#include <sys/stat.h>
#include <stddef.h> /* for wchar_t */
@@ -120,6 +120,8 @@ typedef ssize_t la_ssize_t;
# define __LA_DECL __declspec(dllimport)
# endif
# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
#else
/* Static libraries or non-Windows needs no special declaration. */
# define __LA_DECL
@@ -155,7 +157,7 @@ __LA_DECL int archive_version_number(void);
/*
* Textual name/version of the library, useful for version displays.
*/
-#define ARCHIVE_VERSION_ONLY_STRING "3.6.1"
+#define ARCHIVE_VERSION_ONLY_STRING "3.7.1"
#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
__LA_DECL const char * archive_version_string(void);
diff --git a/src/libs/3rdparty/libarchive/archive_digest.c b/src/libs/3rdparty/libarchive/archive_digest.c
index a7bd5f028..08a9aeb02 100644
--- a/src/libs/3rdparty/libarchive/archive_digest.c
+++ b/src/libs/3rdparty/libarchive/archive_digest.c
@@ -36,6 +36,11 @@
#error Cannot use both OpenSSL and libmd.
#endif
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
/*
* Message digest functions for Windows platform.
*/
@@ -48,17 +53,37 @@
/*
* Initialize a Message digest.
*/
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
static int
-win_crypto_init(Digest_CTX *ctx, ALG_ID algId)
+win_crypto_init(Digest_CTX *ctx, const WCHAR *algo)
+{
+ NTSTATUS status;
+ ctx->valid = 0;
+
+ status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return (ARCHIVE_FAILED);
+ status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ return (ARCHIVE_FAILED);
+ }
+
+ ctx->valid = 1;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId)
{
ctx->valid = 0;
if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ prov, CRYPT_VERIFYCONTEXT)) {
if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
return (ARCHIVE_FAILED);
if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
- PROV_RSA_FULL, CRYPT_NEWKEYSET))
+ prov, CRYPT_NEWKEYSET))
return (ARCHIVE_FAILED);
}
@@ -70,6 +95,7 @@ win_crypto_init(Digest_CTX *ctx, ALG_ID algId)
ctx->valid = 1;
return (ARCHIVE_OK);
}
+#endif
/*
* Update a Message digest.
@@ -81,23 +107,37 @@ win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)
if (!ctx->valid)
return (ARCHIVE_FAILED);
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCryptHashData(ctx->hHash,
+ (PUCHAR)(uintptr_t)buf,
+ len, 0);
+#else
CryptHashData(ctx->hash,
(unsigned char *)(uintptr_t)buf,
(DWORD)len, 0);
+#endif
return (ARCHIVE_OK);
}
static int
win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
{
+#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA)
DWORD siglen = (DWORD)bufsize;
+#endif
if (!ctx->valid)
return (ARCHIVE_FAILED);
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0);
+ BCryptDestroyHash(ctx->hHash);
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+#else
CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
CryptDestroyHash(ctx->hash);
CryptReleaseContext(ctx->cryptProv, 0);
+#endif
ctx->valid = 0;
return (ARCHIVE_OK);
}
@@ -276,7 +316,11 @@ __archive_md5final(archive_md5_ctx *ctx, void *md)
static int
__archive_md5init(archive_md5_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_MD5));
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5));
+#endif
}
static int
@@ -659,7 +703,11 @@ __archive_sha1final(archive_sha1_ctx *ctx, void *md)
static int
__archive_sha1init(archive_sha1_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA1));
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1));
+#endif
}
static int
@@ -919,7 +967,11 @@ __archive_sha256final(archive_sha256_ctx *ctx, void *md)
static int
__archive_sha256init(archive_sha256_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA_256));
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256));
+#endif
}
static int
@@ -1155,7 +1207,11 @@ __archive_sha384final(archive_sha384_ctx *ctx, void *md)
static int
__archive_sha384init(archive_sha384_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA_384));
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384));
+#endif
}
static int
@@ -1415,7 +1471,11 @@ __archive_sha512final(archive_sha512_ctx *ctx, void *md)
static int
__archive_sha512init(archive_sha512_ctx *ctx)
{
- return (win_crypto_init(ctx, CALG_SHA_512));
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512));
+#endif
}
static int
diff --git a/src/libs/3rdparty/libarchive/archive_digest_private.h b/src/libs/3rdparty/libarchive/archive_digest_private.h
index 9b3bd6621..339b4edca 100644
--- a/src/libs/3rdparty/libarchive/archive_digest_private.h
+++ b/src/libs/3rdparty/libarchive/archive_digest_private.h
@@ -164,6 +164,15 @@
defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+typedef struct {
+ int valid;
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+} Digest_CTX;
+#else
#include <windows.h>
#include <wincrypt.h>
typedef struct {
@@ -172,6 +181,7 @@ typedef struct {
HCRYPTHASH hash;
} Digest_CTX;
#endif
+#endif
/* typedefs */
#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
diff --git a/src/libs/3rdparty/libarchive/archive_entry.c b/src/libs/3rdparty/libarchive/archive_entry.c
index ca7a4bdb5..ae6dc3336 100644
--- a/src/libs/3rdparty/libarchive/archive_entry.c
+++ b/src/libs/3rdparty/libarchive/archive_entry.c
@@ -568,6 +568,13 @@ archive_entry_nlink(struct archive_entry *entry)
return (entry->ae_stat.aest_nlink);
}
+/* Instead, our caller could have chosen a specific encoding
+ * (archive_mstring_get_mbs, archive_mstring_get_utf8,
+ * archive_mstring_get_wcs). So we should try multiple
+ * encodings. Try mbs first because of history, even though
+ * utf8 might be better for pathname portability.
+ * Also omit wcs because of type mismatch (char * versus wchar *)
+ */
const char *
archive_entry_pathname(struct archive_entry *entry)
{
@@ -575,6 +582,13 @@ archive_entry_pathname(struct archive_entry *entry)
if (archive_mstring_get_mbs(
entry->archive, &entry->ae_pathname, &p) == 0)
return (p);
+#if HAVE_EILSEQ /*{*/
+ if (errno == EILSEQ) {
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ }
+#endif /*}*/
if (errno == ENOMEM)
__archive_errx(1, "No memory");
return (NULL);
diff --git a/src/libs/3rdparty/libarchive/archive_entry.h b/src/libs/3rdparty/libarchive/archive_entry.h
index d5cb30de7..c15460b96 100644
--- a/src/libs/3rdparty/libarchive/archive_entry.h
+++ b/src/libs/3rdparty/libarchive/archive_entry.h
@@ -30,7 +30,7 @@
#define ARCHIVE_ENTRY_H_INCLUDED
/* Note: Compiler will complain if this does not match archive.h! */
-#define ARCHIVE_VERSION_NUMBER 3006001
+#define ARCHIVE_VERSION_NUMBER 3007001
/*
* Note: archive_entry.h is for use outside of libarchive; the
@@ -122,6 +122,8 @@ typedef ssize_t la_ssize_t;
# define __LA_DECL __declspec(dllimport)
# endif
# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
#else
/* Static libraries on all platforms and shared libraries on non-Windows. */
# define __LA_DECL
diff --git a/src/libs/3rdparty/libarchive/archive_getdate.c b/src/libs/3rdparty/libarchive/archive_getdate.c
index 39e224cb9..20ab1b158 100644
--- a/src/libs/3rdparty/libarchive/archive_getdate.c
+++ b/src/libs/3rdparty/libarchive/archive_getdate.c
@@ -698,13 +698,9 @@ Convert(time_t Month, time_t Day, time_t Year,
time_t Julian;
int i;
struct tm *ltime;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
struct tm tmbuf;
#endif
-#if defined(HAVE__LOCALTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
if (Year < 69)
Year += 2000;
@@ -731,15 +727,10 @@ Convert(time_t Month, time_t Day, time_t Year,
Julian *= DAY;
Julian += Timezone;
Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
ltime = localtime_r(&Julian, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = Julian;
- terr = _localtime64_s(&tmbuf, &tmptime);
- if (terr)
- ltime = NULL;
- else
- ltime = &tmbuf;
#else
ltime = localtime(&Julian);
#endif
@@ -755,36 +746,21 @@ DSTcorrect(time_t Start, time_t Future)
time_t StartDay;
time_t FutureDay;
struct tm *ltime;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
struct tm tmbuf;
#endif
-#if defined(HAVE__LOCALTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
-
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
ltime = localtime_r(&Start, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = Start;
- terr = _localtime64_s(&tmbuf, &tmptime);
- if (terr)
- ltime = NULL;
- else
- ltime = &tmbuf;
#else
ltime = localtime(&Start);
#endif
StartDay = (ltime->tm_hour + 1) % 24;
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
ltime = localtime_r(&Future, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = Future;
- terr = _localtime64_s(&tmbuf, &tmptime);
- if (terr)
- ltime = NULL;
- else
- ltime = &tmbuf;
#else
ltime = localtime(&Future);
#endif
@@ -799,24 +775,15 @@ RelativeDate(time_t Start, time_t zone, int dstmode,
{
struct tm *tm;
time_t t, now;
-#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
struct tm tmbuf;
#endif
-#if defined(HAVE__GMTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
t = Start - zone;
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+ tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_GMTIME_R)
tm = gmtime_r(&t, &tmbuf);
-#elif defined(HAVE__GMTIME64_S)
- tmptime = t;
- terr = _gmtime64_s(&tmbuf, &tmptime);
- if (terr)
- tm = NULL;
- else
- tm = &tmbuf;
#else
tm = gmtime(&t);
#endif
@@ -835,25 +802,16 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
struct tm *tm;
time_t Month;
time_t Year;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
struct tm tmbuf;
#endif
-#if defined(HAVE__LOCALTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
if (RelMonth == 0)
return 0;
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
tm = localtime_r(&Start, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = Start;
- terr = _localtime64_s(&tmbuf, &tmptime);
- if (terr)
- tm = NULL;
- else
- tm = &tmbuf;
#else
tm = localtime(&Start);
#endif
@@ -993,10 +951,6 @@ __archive_get_date(time_t now, const char *p)
time_t Start;
time_t tod;
long tzone;
-#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
/* Clear out the parsed token array. */
memset(tokens, 0, sizeof(tokens));
@@ -1005,36 +959,26 @@ __archive_get_date(time_t now, const char *p)
gds = &_gds;
/* Look up the current time. */
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&local, &now) ? NULL : &local;
+#elif defined(HAVE_LOCALTIME_R)
tm = localtime_r(&now, &local);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = now;
- terr = _localtime64_s(&local, &tmptime);
- if (terr)
- tm = NULL;
- else
- tm = &local;
#else
memset(&local, 0, sizeof(local));
tm = localtime(&now);
#endif
if (tm == NULL)
return -1;
-#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S)
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
local = *tm;
#endif
/* Look up UTC if we can and use that to determine the current
* timezone offset. */
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+ gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
gmt_ptr = gmtime_r(&now, &gmt);
-#elif defined(HAVE__GMTIME64_S)
- tmptime = now;
- terr = _gmtime64_s(&gmt, &tmptime);
- if (terr)
- gmt_ptr = NULL;
- else
- gmt_ptr = &gmt;
#else
memset(&gmt, 0, sizeof(gmt));
gmt_ptr = gmtime(&now);
@@ -1076,15 +1020,10 @@ __archive_get_date(time_t now, const char *p)
* time components instead of the local timezone. */
if (gds->HaveZone && gmt_ptr != NULL) {
now -= gds->Timezone;
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+ gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
gmt_ptr = gmtime_r(&now, &gmt);
-#elif defined(HAVE__GMTIME64_S)
- tmptime = now;
- terr = _gmtime64_s(&gmt, &tmptime);
- if (terr)
- gmt_ptr = NULL;
- else
- gmt_ptr = &gmt;
#else
gmt_ptr = gmtime(&now);
#endif
diff --git a/src/libs/3rdparty/libarchive/archive_hmac.c b/src/libs/3rdparty/libarchive/archive_hmac.c
index 2a9d04c8d..edb3bf5ab 100644
--- a/src/libs/3rdparty/libarchive/archive_hmac.c
+++ b/src/libs/3rdparty/libarchive/archive_hmac.c
@@ -230,10 +230,28 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
static int
__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *mac;
+
+ char sha1[] = "SHA1";
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1),
+ OSSL_PARAM_END
+ };
+
+ mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ *ctx = EVP_MAC_CTX_new(mac);
+ EVP_MAC_free(mac);
+ if (*ctx == NULL)
+ return -1;
+
+ EVP_MAC_init(*ctx, key, key_len, params);
+#else
*ctx = HMAC_CTX_new();
if (*ctx == NULL)
return -1;
HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);
+#endif
return 0;
}
@@ -241,22 +259,38 @@ static void
__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
size_t data_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_update(*ctx, data, data_len);
+#else
HMAC_Update(*ctx, data, data_len);
+#endif
}
static void
__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ size_t len = *out_len;
+#else
unsigned int len = (unsigned int)*out_len;
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_final(*ctx, out, &len, *out_len);
+#else
HMAC_Final(*ctx, out, &len);
+#endif
*out_len = len;
}
static void
__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_CTX_free(*ctx);
+#else
HMAC_CTX_free(*ctx);
+#endif
*ctx = NULL;
}
diff --git a/src/libs/3rdparty/libarchive/archive_hmac_private.h b/src/libs/3rdparty/libarchive/archive_hmac_private.h
index 13a67d495..d0fda7f96 100644
--- a/src/libs/3rdparty/libarchive/archive_hmac_private.h
+++ b/src/libs/3rdparty/libarchive/archive_hmac_private.h
@@ -74,9 +74,18 @@ typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx;
#elif defined(HAVE_LIBCRYPTO)
+#include <openssl/opensslv.h>
+#include <openssl/hmac.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/params.h>
+
+typedef EVP_MAC_CTX *archive_hmac_sha1_ctx;
+
+#else
#include "archive_openssl_hmac_private.h"
typedef HMAC_CTX* archive_hmac_sha1_ctx;
+#endif
#else
diff --git a/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h b/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h
index ebb06702d..8ac477280 100644
--- a/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h
+++ b/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h
@@ -33,7 +33,8 @@
#include <openssl/evp.h>
#include <openssl/opensslv.h>
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
#include <stdlib.h> /* malloc, free */
#include <string.h> /* memset */
static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
diff --git a/src/libs/3rdparty/libarchive/archive_platform.h b/src/libs/3rdparty/libarchive/archive_platform.h
index 3426975de..1038932ac 100644
--- a/src/libs/3rdparty/libarchive/archive_platform.h
+++ b/src/libs/3rdparty/libarchive/archive_platform.h
@@ -195,8 +195,9 @@
/*
* glibc 2.24 deprecates readdir_r
+ * bionic c deprecates readdir_r too
*/
-#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))
+#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24)) && (!defined(__ANDROID__))
#define USE_READDIR_R 1
#else
#undef USE_READDIR_R
diff --git a/src/libs/3rdparty/libarchive/archive_random.c b/src/libs/3rdparty/libarchive/archive_random.c
index 9d1aa493f..301765acd 100644
--- a/src/libs/3rdparty/libarchive/archive_random.c
+++ b/src/libs/3rdparty/libarchive/archive_random.c
@@ -51,16 +51,27 @@ __FBSDID("$FreeBSD$");
#include <pthread.h>
#endif
-static void arc4random_buf(void *, size_t);
+static void la_arc4random_buf(void *, size_t);
#endif /* HAVE_ARC4RANDOM_BUF */
#include "archive.h"
#include "archive_random_private.h"
-#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
#include <wincrypt.h>
#endif
+#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
@@ -75,6 +86,20 @@ int
archive_random(void *buf, size_t nbytes)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ NTSTATUS status;
+ BCRYPT_ALG_HANDLE hAlg;
+
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return ARCHIVE_FAILED;
+ status = BCryptGenRandom(hAlg, buf, nbytes, 0);
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return ARCHIVE_FAILED;
+
+ return ARCHIVE_OK;
+# else
HCRYPTPROV hProv;
BOOL success;
@@ -92,6 +117,10 @@ archive_random(void *buf, size_t nbytes)
}
/* TODO: Does this case really happen? */
return ARCHIVE_FAILED;
+# endif
+#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+ la_arc4random_buf(buf, nbytes);
+ return ARCHIVE_OK;
#else
arc4random_buf(buf, nbytes);
return ARCHIVE_OK;
@@ -256,7 +285,7 @@ arc4_getbyte(void)
}
static void
-arc4random_buf(void *_buf, size_t n)
+la_arc4random_buf(void *_buf, size_t n)
{
uint8_t *buf = (uint8_t *)_buf;
_ARC4_LOCK();
diff --git a/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c b/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c
index b4398f1ec..f16ca5c82 100644
--- a/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c
+++ b/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c
@@ -95,8 +95,13 @@ archive_read_data_into_fd(struct archive *a, int fd)
"archive_read_data_into_fd");
can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
- if (!can_lseek)
+ if (!can_lseek) {
nulls = calloc(1, nulls_size);
+ if (!nulls) {
+ r = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ }
while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
ARCHIVE_OK) {
diff --git a/src/libs/3rdparty/libarchive/archive_read_disk_posix.c b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
index 2b39e672b..8d5c32f03 100644
--- a/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
+++ b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
@@ -34,9 +34,6 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
-#ifdef HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
@@ -54,6 +51,8 @@ __FBSDID("$FreeBSD$");
#endif
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
+#elif HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
#endif
/*
* Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
@@ -1671,6 +1670,11 @@ setup_current_filesystem(struct archive_read_disk *a)
else
t->current_filesystem->name_max = nm;
#endif
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
#endif /* USE_READDIR_R */
return (ARCHIVE_OK);
}
@@ -1861,8 +1865,17 @@ setup_current_filesystem(struct archive_read_disk *a)
#if defined(USE_READDIR_R)
/* Set maximum filename length. */
+#if defined(HAVE_STATVFS)
+ t->current_filesystem->name_max = svfs.f_namemax;
+#else
t->current_filesystem->name_max = sfs.f_namelen;
#endif
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif
return (ARCHIVE_OK);
}
@@ -1943,6 +1956,11 @@ setup_current_filesystem(struct archive_read_disk *a)
#if defined(USE_READDIR_R)
/* Set maximum filename length. */
t->current_filesystem->name_max = svfs.f_namemax;
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
#endif
return (ARCHIVE_OK);
}
@@ -1997,6 +2015,11 @@ setup_current_filesystem(struct archive_read_disk *a)
else
t->current_filesystem->name_max = nm;
# endif /* _PC_NAME_MAX */
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
#endif /* USE_READDIR_R */
return (ARCHIVE_OK);
}
@@ -2103,6 +2126,8 @@ tree_push(struct tree *t, const char *path, int filesystem_id,
struct tree_entry *te;
te = calloc(1, sizeof(*te));
+ if (te == NULL)
+ __archive_errx(1, "Out of memory");
te->next = t->stack;
te->parent = t->current;
if (te->parent)
@@ -2542,7 +2567,11 @@ tree_current_lstat(struct tree *t)
#else
if (tree_enter_working_dir(t) != 0)
return NULL;
+#ifdef HAVE_LSTAT
if (lstat(tree_current_access_path(t), &t->lst) != 0)
+#else
+ if (la_stat(tree_current_access_path(t), &t->lst) != 0)
+#endif
#endif
return NULL;
t->flags |= hasLstat;
diff --git a/src/libs/3rdparty/libarchive/archive_read_disk_windows.c b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
index ea32e2aac..f92a78a21 100644
--- a/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
+++ b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
@@ -418,8 +418,19 @@ la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)
FILE_FLAG_OPEN_REPARSE_POINT;
int ret;
- h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag,
- NULL);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
return (-1);
@@ -1066,14 +1077,29 @@ next_entry(struct archive_read_disk *a, struct tree *t,
if (archive_entry_filetype(entry) == AE_IFREG &&
archive_entry_size(entry) > 0) {
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
if (t->async_io)
flags |= FILE_FLAG_OVERLAPPED;
if (t->direct_io)
flags |= FILE_FLAG_NO_BUFFERING;
else
flags |= FILE_FLAG_SEQUENTIAL_SCAN;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flags;
+ t->entry_fh = CreateFile2(tree_current_access_path(t),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
t->entry_fh = CreateFileW(tree_current_access_path(t),
- GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL);
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, flags, NULL);
+#endif
if (t->entry_fh == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
archive_set_error(&a->archive, errno,
@@ -1544,6 +1570,9 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt)
{
HANDLE handle;
int r = 0;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)
return (0);
@@ -1557,8 +1586,16 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt)
if ((t->flags & needsRestoreTimes) == 0)
return (r);
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES,
+ 0, OPEN_EXISTING, &createExParams);
+#else
handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,
0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
if (handle == INVALID_HANDLE_VALUE) {
errno = EINVAL;
return (-1);
@@ -2043,11 +2080,24 @@ tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st,
HANDLE h;
int r;
DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
-
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
if (sim_lstat && tree_current_is_physical_link(t))
flag |= FILE_FLAG_OPEN_REPARSE_POINT;
- h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL,
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, flag, NULL);
+#endif
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
t->tree_errno = errno;
@@ -2253,7 +2303,10 @@ archive_read_disk_entry_from_file(struct archive *_a,
} else {
WIN32_FIND_DATAW findData;
DWORD flag, desiredAccess;
-
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
h = FindFirstFileW(path, &findData);
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
@@ -2275,8 +2328,18 @@ archive_read_disk_entry_from_file(struct archive *_a,
} else
desiredAccess = GENERIC_READ;
- h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL,
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, flag, NULL);
+#endif
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
archive_set_error(&a->archive, errno,
@@ -2337,8 +2400,19 @@ archive_read_disk_entry_from_file(struct archive *_a,
if (fd >= 0) {
h = (HANDLE)_get_osfhandle(fd);
} else {
- h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ h = CreateFile2(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
archive_set_error(&a->archive, errno,
diff --git a/src/libs/3rdparty/libarchive/archive_read_open_file.c b/src/libs/3rdparty/libarchive/archive_read_open_file.c
index 101dae6cd..03719e8bf 100644
--- a/src/libs/3rdparty/libarchive/archive_read_open_file.c
+++ b/src/libs/3rdparty/libarchive/archive_read_open_file.c
@@ -154,10 +154,10 @@ file_skip(struct archive *a, void *client_data, int64_t request)
#ifdef __ANDROID__
/* fileno() isn't safe on all platforms ... see above. */
if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)
-#elif HAVE_FSEEKO
- if (fseeko(mine->f, skip, SEEK_CUR) != 0)
#elif HAVE__FSEEKI64
if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
+#elif HAVE_FSEEKO
+ if (fseeko(mine->f, skip, SEEK_CUR) != 0)
#else
if (fseek(mine->f, skip, SEEK_CUR) != 0)
#endif
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c
index 793d605c8..9158e668e 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c
@@ -230,7 +230,7 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p)
/* Empty our output buffer. */
state->stream.next_out = state->out_block;
- state->stream.avail_out = state->out_block_size;
+ state->stream.avail_out = (uint32_t)state->out_block_size;
/* Try to fill the output buffer. */
for (;;) {
@@ -288,7 +288,7 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p)
return (ARCHIVE_FATAL);
}
state->stream.next_in = (char *)(uintptr_t)read_buf;
- state->stream.avail_in = ret;
+ state->stream.avail_in = (uint32_t)ret;
/* There is no more data, return whatever we have. */
if (ret == 0) {
state->eof = 1;
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
index ae0b08003..d0fc1a83e 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
@@ -450,7 +450,9 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
chsum = (chsum >> 8) & 0xff;
chsum_verifier = read_buf[descriptor_bytes-1] & 0xff;
if (chsum != chsum_verifier)
+#ifndef DONT_FAIL_ON_CRC_ERROR
goto malformed_error;
+#endif
__archive_read_filter_consume(self->upstream, descriptor_bytes);
@@ -521,7 +523,9 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
unsigned int chsum_block =
archive_le32dec(read_buf + 4 + compressed_size);
if (chsum != chsum_block)
+#ifndef DONT_FAIL_ON_CRC_ERROR
goto malformed_error;
+#endif
}
@@ -580,7 +584,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
state->out_block + prefix64k, (int)compressed_size,
state->flags.block_maximum_size,
state->out_block,
- prefix64k);
+ (int)prefix64k);
#else
uncompressed_size = LZ4_decompress_safe_withPrefix64k(
read_buf + 4,
@@ -652,10 +656,12 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
state->xxh32_state);
state->xxh32_state = NULL;
if (checksum != checksum_stream) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
"lz4 stream checksum error");
return (ARCHIVE_FATAL);
+#endif
}
} else if (ret > 0)
__archive_xxhash.XXH32_update(state->xxh32_state,
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
index afd2d4d0c..4ebdd3bf3 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
@@ -283,7 +283,9 @@ consume_header(struct archive_read_filter *self)
else
checksum = adler32(adler32(0, NULL, 0), p, len);
if (archive_be32dec(p + len) != checksum)
+#ifndef DONT_FAIL_ON_CRC_ERROR
goto corrupted;
+#endif
__archive_read_filter_consume(self->upstream, len + 4);
if (flags & EXTRA_FIELD) {
/* Skip extra field */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
index 32ae0be92..e313d39c0 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
@@ -612,9 +612,11 @@ lzip_tail(struct archive_read_filter *self)
/* Check the crc32 value of the uncompressed data of the current
* member */
if (state->crc32 != archive_le32dec(f)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
"Lzip: CRC32 error");
return (ARCHIVE_FAILED);
+#endif
}
/* Check the uncompressed size of the current member */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c
index 39f25f1bf..1959b5ac3 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c
@@ -115,9 +115,9 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self,
unsigned prefix;
/* Zstd frame magic values */
- const unsigned zstd_magic = 0xFD2FB528U;
- const unsigned zstd_magic_skippable_start = 0x184D2A50U;
- const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
+ unsigned zstd_magic = 0xFD2FB528U;
+ unsigned zstd_magic_skippable_start = 0x184D2A50U;
+ unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
(void) self; /* UNUSED */
@@ -170,7 +170,7 @@ static int
zstd_bidder_init(struct archive_read_filter *self)
{
struct private_data *state;
- const size_t out_block_size = ZSTD_DStreamOutSize();
+ size_t out_block_size = ZSTD_DStreamOutSize();
void *out_block;
ZSTD_DStream *dstream;
@@ -211,6 +211,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
ssize_t avail_in;
ZSTD_outBuffer out;
ZSTD_inBuffer in;
+ size_t ret;
state = (struct private_data *)self->data;
@@ -219,7 +220,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
/* Try to fill the output buffer. */
while (out.pos < out.size && !state->eof) {
if (!state->in_frame) {
- const size_t ret = ZSTD_initDStream(state->dstream);
+ ret = ZSTD_initDStream(state->dstream);
if (ZSTD_isError(ret)) {
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
@@ -249,8 +250,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
in.pos = 0;
{
- const size_t ret =
- ZSTD_decompressStream(state->dstream, &out, &in);
+ ret = ZSTD_decompressStream(state->dstream, &out, &in);
if (ZSTD_isError(ret)) {
archive_set_error(&self->archive->archive,
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
index 564ba514a..b171bea01 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
@@ -41,6 +41,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
#include "archive.h"
#include "archive_entry.h"
@@ -80,8 +83,11 @@ __FBSDID("$FreeBSD$");
#define _7Z_IA64 0x03030401
#define _7Z_ARM 0x03030501
#define _7Z_ARMTHUMB 0x03030701
+#define _7Z_ARM64 0xa
#define _7Z_SPARC 0x03030805
+#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */
+
/*
* 7-Zip header property IDs.
*/
@@ -278,6 +284,11 @@ struct _7zip {
z_stream stream;
int stream_valid;
#endif
+ /* Decoding Zstandard data. */
+#if HAVE_ZSTD_H
+ ZSTD_DStream *zstd_dstream;
+ int zstdstream_valid;
+#endif
/* Decoding PPMd data. */
int ppmd7_stat;
CPpmd7 ppmd7_context;
@@ -397,6 +408,9 @@ static int setup_decode_folder(struct archive_read *, struct _7z_folder *,
int);
static void x86_Init(struct _7zip *);
static size_t x86_Convert(struct _7zip *, uint8_t *, size_t);
+static void arm_Init(struct _7zip *);
+static size_t arm_Convert(struct _7zip *, uint8_t *, size_t);
+static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t);
static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
@@ -776,7 +790,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
}
/* Set up a more descriptive format name. */
- sprintf(zip->format_name, "7-Zip");
+ snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip");
a->archive.archive_format_name = zip->format_name;
return (ret);
@@ -1027,10 +1041,13 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
case _7Z_COPY:
case _7Z_BZ2:
case _7Z_DEFLATE:
+ case _7Z_ZSTD:
case _7Z_PPMD:
if (coder2 != NULL) {
if (coder2->codec != _7Z_X86 &&
- coder2->codec != _7Z_X86_BCJ2) {
+ coder2->codec != _7Z_X86_BCJ2 &&
+ coder2->codec != _7Z_ARM &&
+ coder2->codec != _7Z_ARM64) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"Unsupported filter %lx for %lx",
@@ -1041,6 +1058,8 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
zip->bcj_state = 0;
if (coder2->codec == _7Z_X86)
x86_Init(zip);
+ else if (coder2->codec == _7Z_ARM)
+ arm_Init(zip);
}
break;
default:
@@ -1137,6 +1156,12 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
filters[fi].id = LZMA_FILTER_ARMTHUMB;
fi++;
break;
+#ifdef LZMA_FILTER_ARM64
+ case _7Z_ARM64:
+ filters[fi].id = LZMA_FILTER_ARM64;
+ fi++;
+ break;
+#endif
case _7Z_SPARC:
filters[fi].id = LZMA_FILTER_SPARC;
fi++;
@@ -1222,6 +1247,22 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
"BZ2 codec is unsupported");
return (ARCHIVE_FAILED);
#endif
+ case _7Z_ZSTD:
+ {
+#if defined(HAVE_ZSTD_H)
+ if (zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstd_dstream);
+ zip->zstdstream_valid = 0;
+ }
+ zip->zstd_dstream = ZSTD_createDStream();
+ zip->zstdstream_valid = 1;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZSTD codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ }
case _7Z_DEFLATE:
#ifdef HAVE_ZLIB_H
if (zip->stream_valid)
@@ -1436,9 +1477,9 @@ decompress(struct archive_read *a, struct _7zip *zip,
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
case _7Z_BZ2:
zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
- zip->bzstream.avail_in = t_avail_in;
+ zip->bzstream.avail_in = (uint32_t)t_avail_in;
zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
- zip->bzstream.avail_out = t_avail_out;
+ zip->bzstream.avail_out = (uint32_t)t_avail_out;
r = BZ2_bzDecompress(&(zip->bzstream));
switch (r) {
case BZ_STREAM_END: /* Found end of stream. */
@@ -1488,6 +1529,22 @@ decompress(struct archive_read *a, struct _7zip *zip,
t_avail_out = zip->stream.avail_out;
break;
#endif
+#ifdef HAVE_ZSTD_H
+ case _7Z_ZSTD:
+ {
+ ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
+ ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+
+ size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+ if (ZSTD_isError(zret)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+ return ARCHIVE_FAILED;
+ }
+ t_avail_in -= input.pos;
+ t_avail_out -= output.pos;
+ break;
+ }
+#endif
case _7Z_PPMD:
{
uint64_t flush_bytes;
@@ -1572,16 +1629,23 @@ decompress(struct archive_read *a, struct _7zip *zip,
/*
* Decord BCJ.
*/
- if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
- size_t l = x86_Convert(zip, buff, *outbytes);
- zip->odd_bcj_size = *outbytes - l;
- if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
- o_avail_in && ret != ARCHIVE_EOF) {
- memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
- zip->odd_bcj_size);
- *outbytes = l;
- } else
- zip->odd_bcj_size = 0;
+ if (zip->codec != _7Z_LZMA2) {
+ if (zip->codec2 == _7Z_X86) {
+ size_t l = x86_Convert(zip, buff, *outbytes);
+
+ zip->odd_bcj_size = *outbytes - l;
+ if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
+ o_avail_in && ret != ARCHIVE_EOF) {
+ memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
+ zip->odd_bcj_size);
+ *outbytes = l;
+ } else
+ zip->odd_bcj_size = 0;
+ } else if (zip->codec2 == _7Z_ARM) {
+ *outbytes = arm_Convert(zip, buff, *outbytes);
+ } else if (zip->codec2 == _7Z_ARM64) {
+ *outbytes = arm64_Convert(zip, buff, *outbytes);
+ }
}
/*
@@ -2857,8 +2921,10 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
/* CRC check. */
if (crc32(0, (const unsigned char *)p + 12, 20)
!= archive_le32dec(p + 8)) {
+#ifdef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, -1, "Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
next_header_offset = archive_le64dec(p + 12);
@@ -2908,8 +2974,10 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
/* Check the EncodedHeader CRC.*/
if (r == 0 && zip->header_crc32 != next_header_crc) {
archive_set_error(&a->archive, -1,
+#ifndef DONT_FAIL_ON_CRC_ERROR
"Damaged 7-Zip archive");
r = -1;
+#endif
}
if (r == 0) {
if (zip->si.ci.folders[0].digest_defined)
@@ -2960,9 +3028,11 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
/* Check the Header CRC.*/
if (check_header_crc && zip->header_crc32 != next_header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, -1,
"Malformed 7-Zip archive");
return (ARCHIVE_FATAL);
+#endif
}
break;
default:
@@ -3721,6 +3791,116 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
return (bufferPos);
}
+static void
+arm_Init(struct _7zip *zip)
+{
+ zip->bcj_ip = 8;
+}
+
+static size_t
+arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+ // This function was adapted from
+ // static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+ // in https://git.tukaani.org/xz-embedded.git
+
+ /*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <https://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ if (buf[i + 3] == 0xEB) {
+ // Calculate the transformed addr.
+ addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+ | ((uint32_t)buf[i + 2] << 16);
+ addr <<= 2;
+ addr -= zip->bcj_ip + (uint32_t)i;
+ addr >>= 2;
+
+ // Store the transformed addr in buf.
+ buf[i] = (uint8_t)addr;
+ buf[i + 1] = (uint8_t)(addr >> 8);
+ buf[i + 2] = (uint8_t)(addr >> 16);
+ }
+ }
+
+ zip->bcj_ip += (uint32_t)i;
+
+ return i;
+}
+
+static size_t
+arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+ // This function was adapted from
+ // static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+ // in https://git.tukaani.org/xz-embedded.git
+
+ /*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <https://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+ size_t i;
+ uint32_t instr;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = (uint32_t)buf[i]
+ | ((uint32_t)buf[i+1] << 8)
+ | ((uint32_t)buf[i+2] << 16)
+ | ((uint32_t)buf[i+3] << 24);
+
+ if ((instr >> 26) == 0x25) {
+ /* BL instruction */
+ addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2);
+ instr = 0x94000000 | (addr & 0x03FFFFFF);
+
+ buf[i] = (uint8_t)instr;
+ buf[i+1] = (uint8_t)(instr >> 8);
+ buf[i+2] = (uint8_t)(instr >> 16);
+ buf[i+3] = (uint8_t)(instr >> 24);
+ } else if ((instr & 0x9F000000) == 0x90000000) {
+ /* ADRP instruction */
+ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+ /* Only convert values in the range +/-512 MiB. */
+ if ((addr + 0x020000) & 0x1C0000)
+ continue;
+
+ addr -= (zip->bcj_ip + (uint32_t)i) >> 12;
+
+ instr &= 0x9000001F;
+ instr |= (addr & 3) << 29;
+ instr |= (addr & 0x03FFFC) << 3;
+ instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+ buf[i] = (uint8_t)instr;
+ buf[i+1] = (uint8_t)(instr >> 8);
+ buf[i+2] = (uint8_t)(instr >> 16);
+ buf[i+3] = (uint8_t)(instr >> 24);
+ }
+ }
+
+ zip->bcj_ip += (uint32_t)i;
+
+ return i;
+}
+
/*
* Brought from LZMA SDK.
*
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
index 950f3d254..3b552a84d 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
@@ -996,7 +996,7 @@ archive_read_format_cab_read_header(struct archive_read *a,
cab->end_of_entry_cleanup = cab->end_of_entry = 1;
/* Set up a more descriptive format name. */
- sprintf(cab->format_name, "CAB %d.%d (%s)",
+ snprintf(cab->format_name, sizeof(cab->format_name), "CAB %d.%d (%s)",
hd->major, hd->minor, cab->entry_cffolder->compname);
a->archive.archive_format_name = cab->format_name;
@@ -1134,7 +1134,7 @@ cab_checksum_update(struct archive_read *a, size_t bytes)
}
if (sumbytes) {
int odd = sumbytes & 3;
- if (sumbytes - odd > 0)
+ if ((int)(sumbytes - odd) > 0)
cfdata->sum_calculated = cab_checksum_cfdata_4(
p, sumbytes - odd, cfdata->sum_calculated);
if (odd)
@@ -1171,12 +1171,14 @@ cab_checksum_finish(struct archive_read *a)
cfdata->sum_calculated = cab_checksum_cfdata(
cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
if (cfdata->sum_calculated != cfdata->sum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes",
cab->entry_cffolder->cfdata_index -1,
cfdata->sum, cfdata->sum_calculated,
cfdata->compressed_size);
return (ARCHIVE_FAILED);
+#endif
}
return (ARCHIVE_OK);
}
@@ -2292,10 +2294,10 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
(br->cache_buffer << 48) |
((uint64_t)strm->next_in[1]) << 40 |
((uint64_t)strm->next_in[0]) << 32 |
- ((uint32_t)strm->next_in[3]) << 24 |
- ((uint32_t)strm->next_in[2]) << 16 |
- ((uint32_t)strm->next_in[5]) << 8 |
- (uint32_t)strm->next_in[4];
+ ((uint64_t)strm->next_in[3]) << 24 |
+ ((uint64_t)strm->next_in[2]) << 16 |
+ ((uint64_t)strm->next_in[5]) << 8 |
+ (uint64_t)strm->next_in[4];
strm->next_in += 6;
strm->avail_in -= 6;
br->cache_avail += 6 * 8;
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c
index 6b8ae33a4..9adcfd335 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c
@@ -441,7 +441,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
/* Compare name to "TRAILER!!!" to test for end-of-archive. */
if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
- 11) == 0) {
+ 10) == 0) {
/* TODO: Store file location of start of block. */
archive_clear_error(&a->archive);
return (ARCHIVE_EOF);
@@ -985,14 +985,14 @@ archive_read_format_cpio_cleanup(struct archive_read *a)
static int64_t
le4(const unsigned char *p)
{
- return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8));
+ return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8));
}
static int64_t
be4(const unsigned char *p)
{
- return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3]));
+ return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]));
}
/*
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
index cd7f92f46..f5414be2a 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
@@ -1757,7 +1757,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
size_t name_len;
const unsigned char *rr_start, *rr_end;
const unsigned char *p;
- size_t dr_len;
+ size_t dr_len = 0;
uint64_t fsize, offset;
int32_t location;
int flags;
@@ -1901,7 +1901,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
* NUMBER of RRIP "PX" extension.
* Note: Old mkisofs did not record that FILE SERIAL NUMBER
* in ISO images.
- * Note2: xorriso set 0 to the location of a symlink file.
+ * Note2: xorriso set 0 to the location of a symlink file.
*/
if (file->size == 0 && location >= 0) {
/* If file->size is zero, its location points wrong place,
@@ -1955,7 +1955,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
* made by makefs is not zero and its location is
* the same as those of next regular file. That is
* the same as hard like file and it causes unexpected
- * error.
+ * error.
*/
if (file->size > 0 &&
(file->mode & AE_IFMT) == AE_IFLNK) {
@@ -2747,7 +2747,7 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660,
* If directory entries all which are descendant of
* rr_moved are still remaining, expose their.
*/
- if (iso9660->re_files.first != NULL &&
+ if (iso9660->re_files.first != NULL &&
iso9660->rr_moved != NULL &&
iso9660->rr_moved->rr_moved_has_re_only)
/* Expose "rr_moved" entry. */
@@ -3180,11 +3180,11 @@ isodate17(const unsigned char *v)
static time_t
time_from_tm(struct tm *t)
{
-#if HAVE_TIMEGM
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
/* Use platform timegm() if available. */
return (timegm(t));
-#elif HAVE__MKGMTIME64
- return (_mkgmtime64(t));
#else
/* Else use direct calculation using POSIX assumptions. */
/* First, fix up tm_yday based on the year/month/day. */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
index bff0f01f4..1c64b2900 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
@@ -739,7 +739,7 @@ archive_read_format_lha_read_header(struct archive_read *a,
if (lha->directory || lha->compsize == 0)
lha->end_of_entry = 1;
- sprintf(lha->format_name, "lha -%c%c%c-",
+ snprintf(lha->format_name, sizeof(lha->format_name), "lha -%c%c%c-",
lha->method[0], lha->method[1], lha->method[2]);
a->archive.archive_format_name = lha->format_name;
@@ -1039,9 +1039,11 @@ lha_read_file_header_2(struct archive_read *a, struct lha *lha)
}
if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"LHa header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
return (err);
}
@@ -1107,9 +1109,11 @@ lha_read_file_header_3(struct archive_read *a, struct lha *lha)
return (err);
if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"LHa header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
return (err);
invalid:
@@ -1814,13 +1818,16 @@ lha_crc16(uint16_t crc, const void *pp, size_t len)
/* This if statement expects compiler optimization will
* remove the statement which will not be executed. */
#undef bswap16
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */
# define bswap16(x) _byteswap_ushort(x)
#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4)
/* GCC 4.8 and later has __builtin_bswap16() */
# define bswap16(x) __builtin_bswap16(x)
-#elif defined(__clang__)
-/* All clang versions have __builtin_bswap16() */
+#elif defined(__clang__) && __has_builtin(__builtin_bswap16)
+/* Newer clang versions have __builtin_bswap16() */
# define bswap16(x) __builtin_bswap16(x)
#else
# define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
@@ -2005,10 +2012,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
((uint64_t)strm->next_in[0]) << 48 |
((uint64_t)strm->next_in[1]) << 40 |
((uint64_t)strm->next_in[2]) << 32 |
- ((uint32_t)strm->next_in[3]) << 24 |
- ((uint32_t)strm->next_in[4]) << 16 |
- ((uint32_t)strm->next_in[5]) << 8 |
- (uint32_t)strm->next_in[6];
+ ((uint64_t)strm->next_in[3]) << 24 |
+ ((uint64_t)strm->next_in[4]) << 16 |
+ ((uint64_t)strm->next_in[5]) << 8 |
+ (uint64_t)strm->next_in[6];
strm->next_in += 7;
strm->avail_in -= 7;
br->cache_avail += 7 * 8;
@@ -2018,10 +2025,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
(br->cache_buffer << 48) |
((uint64_t)strm->next_in[0]) << 40 |
((uint64_t)strm->next_in[1]) << 32 |
- ((uint32_t)strm->next_in[2]) << 24 |
- ((uint32_t)strm->next_in[3]) << 16 |
- ((uint32_t)strm->next_in[4]) << 8 |
- (uint32_t)strm->next_in[5];
+ ((uint64_t)strm->next_in[2]) << 24 |
+ ((uint64_t)strm->next_in[3]) << 16 |
+ ((uint64_t)strm->next_in[4]) << 8 |
+ (uint64_t)strm->next_in[5];
strm->next_in += 6;
strm->avail_in -= 6;
br->cache_avail += 6 * 8;
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
index 4a2816325..a5fa30e3c 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
@@ -994,9 +994,11 @@ process_add_entry(struct archive_read *a, struct mtree *mtree,
struct mtree_entry *alt;
alt = (struct mtree_entry *)__archive_rb_tree_find_node(
&mtree->rbtree, entry->name);
- while (alt->next_dup)
- alt = alt->next_dup;
- alt->next_dup = entry;
+ if (alt != NULL) {
+ while (alt->next_dup)
+ alt = alt->next_dup;
+ alt->next_dup = entry;
+ }
}
}
@@ -1071,7 +1073,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
continue;
/* Non-printable characters are not allowed */
for (s = p;s < p + len - 1; s++) {
- if (!isprint((unsigned char)*s)) {
+ if (!isprint((unsigned char)*s) && *s != '\t') {
r = ARCHIVE_FATAL;
break;
}
@@ -1250,9 +1252,17 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
archive_entry_filetype(entry) == AE_IFDIR) {
mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
__archive_ensure_cloexec_flag(mtree->fd);
- if (mtree->fd == -1 &&
- (errno != ENOENT ||
- archive_strlen(&mtree->contents_name) > 0)) {
+ if (mtree->fd == -1 && (
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * On Windows, attempting to open a file with an
+ * invalid name result in EINVAL (Error 22)
+ */
+ (errno != ENOENT && errno != EINVAL)
+#else
+ errno != ENOENT
+#endif
+ || archive_strlen(&mtree->contents_name) > 0)) {
archive_set_error(&a->archive, errno,
"Can't open %s", path);
r = ARCHIVE_WARN;
@@ -1270,7 +1280,13 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
mtree->fd = -1;
st = NULL;
}
- } else if (lstat(path, st) == -1) {
+ }
+#ifdef HAVE_LSTAT
+ else if (lstat(path, st) == -1)
+#else
+ else if (la_stat(path, st) == -1)
+#endif
+ {
st = NULL;
}
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
index f9cbe2a88..16b6e6eed 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
@@ -1007,9 +1007,11 @@ archive_read_format_rar_read_header(struct archive_read *a,
crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);
if ((crc32_val & 0xffff) != archive_le16dec(p)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
__archive_read_consume(a, skip);
break;
@@ -1060,14 +1062,16 @@ archive_read_format_rar_read_header(struct archive_read *a,
return (ARCHIVE_FATAL);
}
p = h;
- crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read);
+ crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned int)to_read);
__archive_read_consume(a, to_read);
skip -= to_read;
}
if ((crc32_val & 0xffff) != crc32_expected) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
if (head_type == ENDARC_HEAD)
return (ARCHIVE_EOF);
@@ -1432,9 +1436,11 @@ read_header(struct archive_read *a, struct archive_entry *entry,
/* File Header CRC check. */
crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));
if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return (ARCHIVE_FATAL);
+#endif
}
/* If no CRC error, Go on parsing File Header. */
p = h;
@@ -1824,13 +1830,9 @@ read_exttime(const char *p, struct rar *rar, const char *endp)
struct tm *tm;
time_t t;
long nsec;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
struct tm tmbuf;
#endif
-#if defined(HAVE__LOCALTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
if (p + 2 > endp)
return (-1);
@@ -1862,15 +1864,10 @@ read_exttime(const char *p, struct rar *rar, const char *endp)
rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
p++;
}
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
tm = localtime_r(&t, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = t;
- terr = _localtime64_s(&tmbuf, &tmptime);
- if (terr)
- tm = NULL;
- else
- tm = &tmbuf;
#else
tm = localtime(&t);
#endif
@@ -1952,9 +1949,11 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size,
*size = 0;
*offset = rar->offset;
if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"File CRC error");
return (ARCHIVE_FATAL);
+#endif
}
rar->entry_eof = 1;
return (ARCHIVE_EOF);
@@ -2045,9 +2044,11 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
*size = 0;
*offset = rar->offset;
if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"File CRC error");
return (ARCHIVE_FATAL);
+#endif
}
rar->entry_eof = 1;
return (ARCHIVE_EOF);
@@ -3436,7 +3437,7 @@ compile_program(const uint8_t *bytes, size_t length)
prog = calloc(1, sizeof(*prog));
if (!prog)
return NULL;
- prog->fingerprint = crc32(0, bytes, length) | ((uint64_t)length << 32);
+ prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32);
if (membr_bits(&br, 1))
{
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
index a3cfa72e7..1f9099439 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
@@ -2475,7 +2475,7 @@ static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
* `stored_crc32` info filled in. */
if(rar->file.stored_crc32 > 0) {
rar->file.calculated_crc32 =
- crc32(rar->file.calculated_crc32, p, to_read);
+ crc32(rar->file.calculated_crc32, p, (unsigned int)to_read);
}
/* Check if the file uses an optional BLAKE2sp checksum
@@ -2821,11 +2821,13 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p,
^ (uint8_t) (*block_size >> 16);
if(calculated_cksum != hdr->block_cksum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Block checksum error: got 0x%x, expected 0x%x",
hdr->block_cksum, calculated_cksum);
return ARCHIVE_FATAL;
+#endif
}
return ARCHIVE_OK;
@@ -3911,6 +3913,13 @@ static int do_unpack(struct archive_read* a, struct rar5* rar,
case GOOD:
/* fallthrough */
case BEST:
+ /* No data is returned here. But because a sparse-file aware
+ * caller (like archive_read_data_into_fd) may treat zero-size
+ * as a sparse file block, we need to update the offset
+ * accordingly. At this point the decoder doesn't have any
+ * pending uncompressed data blocks, so the current position in
+ * the output file should be last_write_ptr. */
+ if (offset) *offset = rar->cstate.last_write_ptr;
return uncompress_file(a);
default:
archive_set_error(&a->archive,
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
index bfdad7f87..93c3fd585 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
@@ -407,14 +407,13 @@ archive_read_format_tar_bid(struct archive_read *a, int best_bid)
/*
* Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields.
*/
- if (bid > 0 && (
- validate_number_field(header->mode, sizeof(header->mode)) == 0
+ if (validate_number_field(header->mode, sizeof(header->mode)) == 0
|| validate_number_field(header->uid, sizeof(header->uid)) == 0
|| validate_number_field(header->gid, sizeof(header->gid)) == 0
|| validate_number_field(header->mtime, sizeof(header->mtime)) == 0
|| validate_number_field(header->size, sizeof(header->size)) == 0
|| validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0
- || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) {
+ || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0) {
bid = 0;
}
@@ -2108,6 +2107,21 @@ pax_attribute(struct archive_read *a, struct tar *tar,
/* "size" is the size of the data in the entry. */
tar->entry_bytes_remaining
= tar_atol10(value, strlen(value));
+ if (tar->entry_bytes_remaining < 0) {
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute is negative");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->entry_bytes_remaining == INT64_MAX) {
+ /* Note: tar_atol returns INT64_MAX on overflow */
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute overflow");
+ return (ARCHIVE_FATAL);
+ }
/*
* The "size" pax header keyword always overrides the
* "size" field in the tar header.
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c b/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c
index 27329962d..61ab29ea1 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c
@@ -530,11 +530,11 @@ strtoi_lim(const char *str, const char **ep, int llim, int ulim)
static time_t
time_from_tm(struct tm *t)
{
-#if HAVE_TIMEGM
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
/* Use platform timegm() if available. */
return (timegm(t));
-#elif HAVE__MKGMTIME64
- return (_mkgmtime64(t));
#else
/* Else use direct calculation using POSIX assumptions. */
/* First, fix up tm_yday based on the year/month/day. */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
index 503ff58b9..ec9cb1981 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
@@ -624,7 +624,9 @@ read_toc(struct archive_read *a)
__archive_read_consume(a, xar->toc_chksum_size);
xar->offset += xar->toc_chksum_size;
if (r != ARCHIVE_OK)
+#ifndef DONT_FAIL_ON_CRC_ERROR
return (ARCHIVE_FATAL);
+#endif
}
/*
@@ -827,10 +829,12 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
xattr->a_sum.val, xattr->a_sum.len,
xattr->e_sum.val, xattr->e_sum.len);
if (r != ARCHIVE_OK) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"Xattr checksum error");
r = ARCHIVE_WARN;
break;
+#endif
}
if (xattr->name.s == NULL) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
@@ -1123,7 +1127,7 @@ atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)
x |= p[1] - '0';
else
return (-1);
-
+
*b++ = x;
bsize--;
p += 2;
@@ -1135,11 +1139,11 @@ atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)
static time_t
time_from_tm(struct tm *t)
{
-#if HAVE_TIMEGM
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
/* Use platform timegm() if available. */
return (timegm(t));
-#elif HAVE__MKGMTIME64
- return (_mkgmtime64(t));
#else
/* Else use direct calculation using POSIX assumptions. */
/* First, fix up tm_yday based on the year/month/day. */
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c
index 9d6c900b2..c3b9b5755 100644
--- a/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c
@@ -2186,11 +2186,11 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
/* Setup buffer boundaries. */
zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
- zip->bzstream.avail_in = in_bytes;
+ zip->bzstream.avail_in = (uint32_t)in_bytes;
zip->bzstream.total_in_hi32 = 0;
zip->bzstream.total_in_lo32 = 0;
zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
- zip->bzstream.avail_out = zip->uncompressed_buffer_size;
+ zip->bzstream.avail_out = (uint32_t)zip->uncompressed_buffer_size;
zip->bzstream.total_out_hi32 = 0;
zip->bzstream.total_out_lo32 = 0;
@@ -2227,7 +2227,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
to_consume = zip->bzstream.total_in_lo32;
__archive_read_consume(a, to_consume);
- total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) +
+ total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) |
zip->bzstream.total_out_lo32;
zip->entry_bytes_remaining -= to_consume;
diff --git a/src/libs/3rdparty/libarchive/archive_string.c b/src/libs/3rdparty/libarchive/archive_string.c
index d7f2c46b2..accf52631 100644
--- a/src/libs/3rdparty/libarchive/archive_string.c
+++ b/src/libs/3rdparty/libarchive/archive_string.c
@@ -1324,6 +1324,10 @@ free_sconv_object(struct archive_string_conv *sc)
}
#if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define GetOEMCP() CP_OEMCP
+# endif
+
static unsigned
my_atoi(const char *p)
{
@@ -3988,10 +3992,10 @@ int
archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
const char **p, size_t *length, struct archive_string_conv *sc)
{
- int r, ret = 0;
-
- (void)r; /* UNUSED */
+ int ret = 0;
#if defined(_WIN32) && !defined(__CYGWIN__)
+ int r;
+
/*
* Internationalization programming on Windows must use Wide
* characters because Windows platform cannot make locale UTF-8.
diff --git a/src/libs/3rdparty/libarchive/archive_util.c b/src/libs/3rdparty/libarchive/archive_util.c
index b1582edbe..40603c483 100644
--- a/src/libs/3rdparty/libarchive/archive_util.c
+++ b/src/libs/3rdparty/libarchive/archive_util.c
@@ -42,9 +42,20 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1
#ifdef HAVE_STRING_H
#include <string.h>
#endif
-#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
#include <wincrypt.h>
#endif
+#endif
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
@@ -233,14 +244,16 @@ __archive_mktempx(const char *tmpdir, wchar_t *template)
L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
L'u', L'v', L'w', L'x', L'y', L'z'
};
- HCRYPTPROV hProv;
struct archive_wstring temp_name;
wchar_t *ws;
DWORD attr;
wchar_t *xp, *ep;
int fd;
-
- hProv = (HCRYPTPROV)NULL;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCRYPT_ALG_HANDLE hAlg = NULL;
+#else
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+#endif
fd = -1;
ws = NULL;
@@ -314,23 +327,42 @@ __archive_mktempx(const char *tmpdir, wchar_t *template)
abort();
}
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
+ NULL, 0))) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#else
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
}
+#endif
for (;;) {
wchar_t *p;
HANDLE h;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
/* Generate a random file name through CryptGenRandom(). */
p = xp;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
+ (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#else
if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
(BYTE*)p)) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
}
+#endif
for (; p < ep; p++)
*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
@@ -347,6 +379,17 @@ __archive_mktempx(const char *tmpdir, wchar_t *template)
/* mkstemp */
attr = FILE_ATTRIBUTE_NORMAL;
}
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = attr & 0xFFFF;
+ createExParams.dwFileFlags = attr & 0xFFF00000;
+ h = CreateFile2(ws,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ 0,/* Not share */
+ CREATE_NEW,
+ &createExParams);
+#else
h = CreateFileW(ws,
GENERIC_READ | GENERIC_WRITE | DELETE,
0,/* Not share */
@@ -354,6 +397,7 @@ __archive_mktempx(const char *tmpdir, wchar_t *template)
CREATE_NEW,/* Create a new file only */
attr,
NULL);
+#endif
if (h == INVALID_HANDLE_VALUE) {
/* The same file already exists. retry with
* a new filename. */
@@ -372,8 +416,13 @@ __archive_mktempx(const char *tmpdir, wchar_t *template)
break;/* success! */
}
exit_tmpfile:
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (hAlg != NULL)
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+#else
if (hProv != (HCRYPTPROV)NULL)
CryptReleaseContext(hProv, 0);
+#endif
free(ws);
if (template == temp_name.s)
archive_wstring_free(&temp_name);
diff --git a/src/libs/3rdparty/libarchive/archive_windows.c b/src/libs/3rdparty/libarchive/archive_windows.c
index 624e27009..ebc5eefb8 100644
--- a/src/libs/3rdparty/libarchive/archive_windows.c
+++ b/src/libs/3rdparty/libarchive/archive_windows.c
@@ -234,7 +234,11 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
{
wchar_t *wpath;
HANDLE handle;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
@@ -242,12 +246,25 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
return (handle);
if (GetLastError() != ERROR_PATH_NOT_FOUND)
return (handle);
+#endif
wpath = __la_win_permissive_name(path);
if (wpath == NULL)
- return (handle);
+ return INVALID_HANDLE_VALUE;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF;
+ createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000;
+ createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000;
+ createExParams.lpSecurityAttributes = lpSecurityAttributes;
+ createExParams.hTemplateFile = hTemplateFile;
+ handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode,
+ dwCreationDisposition, &createExParams);
+#else /* !WINAPI_PARTITION_DESKTOP */
handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
+#endif /* !WINAPI_PARTITION_DESKTOP */
free(wpath);
return (handle);
}
@@ -305,7 +322,10 @@ __la_open(const char *path, int flags, ...)
* "Permission denied" error.
*/
attr = GetFileAttributesA(path);
- if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) {
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
+#endif
+ {
ws = __la_win_permissive_name(path);
if (ws == NULL) {
errno = EINVAL;
@@ -320,7 +340,7 @@ __la_open(const char *path, int flags, ...)
}
if (attr & FILE_ATTRIBUTE_DIRECTORY) {
HANDLE handle;
-
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
if (ws != NULL)
handle = CreateFileW(ws, 0, 0, NULL,
OPEN_EXISTING,
@@ -333,6 +353,15 @@ __la_open(const char *path, int flags, ...)
FILE_FLAG_BACKUP_SEMANTICS |
FILE_ATTRIBUTE_READONLY,
NULL);
+#else /* !WINAPI_PARTITION_DESKTOP */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ handle = CreateFile2(ws, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#endif /* !WINAPI_PARTITION_DESKTOP */
free(ws);
if (handle == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
diff --git a/src/libs/3rdparty/libarchive/archive_write.c b/src/libs/3rdparty/libarchive/archive_write.c
index 66592e826..ec3c95c56 100644
--- a/src/libs/3rdparty/libarchive/archive_write.c
+++ b/src/libs/3rdparty/libarchive/archive_write.c
@@ -201,6 +201,10 @@ __archive_write_allocate_filter(struct archive *_a)
struct archive_write_filter *f;
f = calloc(1, sizeof(*f));
+
+ if (f == NULL)
+ return (NULL);
+
f->archive = _a;
f->state = ARCHIVE_WRITE_FILTER_STATE_NEW;
if (a->filter_first == NULL)
@@ -306,6 +310,25 @@ __archive_write_output(struct archive_write *a, const void *buff, size_t length)
return (__archive_write_filter(a->filter_first, buff, length));
}
+static int
+__archive_write_filters_flush(struct archive_write *a)
+{
+ struct archive_write_filter *f;
+ int ret, ret1;
+
+ ret = ARCHIVE_OK;
+ for (f = a->filter_first; f != NULL; f = f->next_filter) {
+ if (f->flush != NULL && f->bytes_written > 0) {
+ ret1 = (f->flush)(f);
+ if (ret1 < ret)
+ ret = ret1;
+ if (ret1 < ARCHIVE_WARN)
+ f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ }
+ }
+ return (ret);
+}
+
int
__archive_write_nulls(struct archive_write *a, size_t length)
{
@@ -548,6 +571,10 @@ archive_write_open2(struct archive *_a, void *client_data,
a->client_data = client_data;
client_filter = __archive_write_allocate_filter(_a);
+
+ if (client_filter == NULL)
+ return (ARCHIVE_FATAL);
+
client_filter->open = archive_write_client_open;
client_filter->write = archive_write_client_write;
client_filter->close = archive_write_client_close;
@@ -732,6 +759,18 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
return (ARCHIVE_FAILED);
}
+ /* Flush filters at boundary. */
+ r2 = __archive_write_filters_flush(a);
+ if (r2 == ARCHIVE_FAILED) {
+ return (ARCHIVE_FAILED);
+ }
+ if (r2 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (r2 < ret)
+ ret = r2;
+
/* Format and write header. */
r2 = ((a->format_write_header)(a, entry));
if (r2 == ARCHIVE_FAILED) {
diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c
index 7001e9c6b..3e5c0891a 100644
--- a/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c
+++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c
@@ -190,7 +190,7 @@ archive_compressor_bzip2_open(struct archive_write_filter *f)
memset(&data->stream, 0, sizeof(data->stream));
data->stream.next_out = data->compressed;
- data->stream.avail_out = data->compressed_buffer_size;
+ data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
f->write = archive_compressor_bzip2_write;
/* Initialize compression library */
@@ -244,7 +244,7 @@ archive_compressor_bzip2_write(struct archive_write_filter *f,
/* Compress input data to output buffer */
SET_NEXT_IN(data, buff);
- data->stream.avail_in = length;
+ data->stream.avail_in = (uint32_t)length;
if (drive_compressor(f, data, 0))
return (ARCHIVE_FATAL);
return (ARCHIVE_OK);
@@ -313,7 +313,7 @@ drive_compressor(struct archive_write_filter *f,
return (ARCHIVE_FATAL);
}
data->stream.next_out = data->compressed;
- data->stream.avail_out = data->compressed_buffer_size;
+ data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
}
/* If there's nothing to do, we're done. */
diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c
index d404fae7d..3ed269fce 100644
--- a/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c
+++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c
@@ -352,7 +352,7 @@ archive_compressor_compress_write(struct archive_write_filter *f,
while (length--) {
c = *bp++;
state->in_count++;
- state->cur_fcode = (c << 16) + state->cur_code;
+ state->cur_fcode = (c << 16) | state->cur_code;
i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */
if (state->hashtab[i] == state->cur_fcode) {
diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c
index cf19fadd5..6ac450357 100644
--- a/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c
+++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c
@@ -518,10 +518,10 @@ drive_compressor_independence(struct archive_write_filter *f, const char *p,
} else {
/* The buffer is not compressed. The compressed size was
* bigger than its uncompressed size. */
- archive_le32enc(data->out, length | 0x80000000);
+ archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
data->out += 4;
memcpy(data->out, p, length);
- outsize = length;
+ outsize = (uint32_t)length;
}
data->out += outsize;
if (data->block_checksum) {
@@ -603,10 +603,10 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p,
} else {
/* The buffer is not compressed. The compressed size was
* bigger than its uncompressed size. */
- archive_le32enc(data->out, length | 0x80000000);
+ archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
data->out += 4;
memcpy(data->out, p, length);
- outsize = length;
+ outsize = (uint32_t)length;
}
data->out += outsize;
if (data->block_checksum) {
diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c
index e85b7669c..584cfb668 100644
--- a/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c
+++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c
@@ -31,6 +31,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
@@ -50,10 +53,21 @@ __FBSDID("$FreeBSD$");
struct private_data {
int compression_level;
- int threads;
+ int threads;
#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ enum {
+ running,
+ finishing,
+ resetting,
+ } state;
+ int frame_per_file;
+ size_t min_frame_size;
+ size_t max_frame_size;
+ size_t cur_frame;
+ size_t cur_frame_in;
+ size_t cur_frame_out;
+ size_t total_in;
ZSTD_CStream *cstream;
- int64_t total_in;
ZSTD_outBuffer out;
#else
struct archive_write_program_data *pdata;
@@ -75,6 +89,7 @@ static int archive_compressor_zstd_options(struct archive_write_filter *,
static int archive_compressor_zstd_open(struct archive_write_filter *);
static int archive_compressor_zstd_write(struct archive_write_filter *,
const void *, size_t);
+static int archive_compressor_zstd_flush(struct archive_write_filter *);
static int archive_compressor_zstd_close(struct archive_write_filter *);
static int archive_compressor_zstd_free(struct archive_write_filter *);
#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
@@ -103,6 +118,7 @@ archive_write_add_filter_zstd(struct archive *_a)
f->data = data;
f->open = &archive_compressor_zstd_open;
f->options = &archive_compressor_zstd_options;
+ f->flush = &archive_compressor_zstd_flush;
f->close = &archive_compressor_zstd_close;
f->free = &archive_compressor_zstd_free;
f->code = ARCHIVE_FILTER_ZSTD;
@@ -110,6 +126,11 @@ archive_write_add_filter_zstd(struct archive *_a)
data->compression_level = CLEVEL_DEFAULT;
data->threads = 0;
#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ data->frame_per_file = 0;
+ data->min_frame_size = 0;
+ data->max_frame_size = SIZE_MAX;
+ data->cur_frame_in = 0;
+ data->cur_frame_out = 0;
data->cstream = ZSTD_createCStream();
if (data->cstream == NULL) {
free(data);
@@ -147,29 +168,18 @@ archive_compressor_zstd_free(struct archive_write_filter *f)
return (ARCHIVE_OK);
}
-static int string_is_numeric (const char* value)
+static int string_to_number(const char *string, intmax_t *numberp)
{
- size_t len = strlen(value);
- size_t i;
-
- if (len == 0) {
- return (ARCHIVE_WARN);
- }
- else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) {
- return (ARCHIVE_WARN);
- }
- else if (!(value[0] >= '0' && value[0] <= '9') &&
- value[0] != '-' && value[0] != '+') {
- return (ARCHIVE_WARN);
- }
-
- for (i = 1; i < len; i++) {
- if (!(value[i] >= '0' && value[i] <= '9')) {
- return (ARCHIVE_WARN);
- }
- }
-
- return (ARCHIVE_OK);
+ char *end;
+
+ if (string == NULL || *string == '\0')
+ return (ARCHIVE_WARN);
+ *numberp = strtoimax(string, &end, 10);
+ if (end == string || *end != '\0' || errno == EOVERFLOW) {
+ *numberp = 0;
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
}
/*
@@ -182,13 +192,13 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
struct private_data *data = (struct private_data *)f->data;
if (strcmp(key, "compression-level") == 0) {
- int level = atoi(value);
+ intmax_t level;
+ if (string_to_number(value, &level) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
/* If we don't have the library, hard-code the max level */
int minimum = CLEVEL_MIN;
int maximum = CLEVEL_MAX;
- if (string_is_numeric(value) != ARCHIVE_OK) {
- return (ARCHIVE_WARN);
- }
#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
maximum = ZSTD_maxCLevel();
#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
@@ -204,22 +214,43 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
if (level < minimum || level > maximum) {
return (ARCHIVE_WARN);
}
- data->compression_level = level;
+ data->compression_level = (int)level;
return (ARCHIVE_OK);
} else if (strcmp(key, "threads") == 0) {
- int threads = atoi(value);
- if (string_is_numeric(value) != ARCHIVE_OK) {
+ intmax_t threads;
+ if (string_to_number(value, &threads) != ARCHIVE_OK) {
return (ARCHIVE_WARN);
}
-
- int minimum = 0;
-
- if (threads < minimum) {
+ if (threads < 0) {
return (ARCHIVE_WARN);
}
-
- data->threads = threads;
+ data->threads = (int)threads;
+ return (ARCHIVE_OK);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ } else if (strcmp(key, "frame-per-file") == 0) {
+ data->frame_per_file = 1;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "min-frame-size") == 0) {
+ intmax_t min_frame_size;
+ if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (min_frame_size < 0) {
+ return (ARCHIVE_WARN);
+ }
+ data->min_frame_size = min_frame_size;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "max-frame-size") == 0) {
+ intmax_t max_frame_size;
+ if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (max_frame_size < 1024) {
+ return (ARCHIVE_WARN);
+ }
+ data->max_frame_size = max_frame_size;
return (ARCHIVE_OK);
+#endif
}
/* Note: The "warn" return is just to inform the options
@@ -281,15 +312,22 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
struct private_data *data = (struct private_data *)f->data;
- int ret;
- /* Update statistics */
- data->total_in += length;
+ return (drive_compressor(f, data, 0, buff, length));
+}
- if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK)
- return (ret);
+/*
+ * Flush the compressed stream.
+ */
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
- return (ARCHIVE_OK);
+ if (data->frame_per_file && data->state == running &&
+ data->cur_frame_out > data->min_frame_size)
+ data->state = finishing;
+ return (drive_compressor(f, data, 1, NULL, 0));
}
/*
@@ -300,57 +338,72 @@ archive_compressor_zstd_close(struct archive_write_filter *f)
{
struct private_data *data = (struct private_data *)f->data;
- /* Finish zstd frame */
- return drive_compressor(f, data, 1, NULL, 0);
+ if (data->state == running)
+ data->state = finishing;
+ return (drive_compressor(f, data, 1, NULL, 0));
}
/*
* Utility function to push input data through compressor,
* writing full output blocks as necessary.
- *
- * Note that this handles both the regular write case (finishing ==
- * false) and the end-of-archive case (finishing == true).
*/
static int
drive_compressor(struct archive_write_filter *f,
- struct private_data *data, int finishing, const void *src, size_t length)
+ struct private_data *data, int flush, const void *src, size_t length)
{
- ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 };
+ ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
+ size_t ipos, opos, zstdret = 0;
+ int ret;
for (;;) {
- if (data->out.pos == data->out.size) {
- const int ret = __archive_write_filter(f->next_filter,
- data->out.dst, data->out.size);
+ ipos = in.pos;
+ opos = data->out.pos;
+ switch (data->state) {
+ case running:
+ if (in.pos == in.size)
+ return (ARCHIVE_OK);
+ zstdret = ZSTD_compressStream(data->cstream,
+ &data->out, &in);
+ if (ZSTD_isError(zstdret))
+ goto zstd_fatal;
+ break;
+ case finishing:
+ zstdret = ZSTD_endStream(data->cstream, &data->out);
+ if (ZSTD_isError(zstdret))
+ goto zstd_fatal;
+ if (zstdret == 0)
+ data->state = resetting;
+ break;
+ case resetting:
+ ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
+ data->cur_frame++;
+ data->cur_frame_in = 0;
+ data->cur_frame_out = 0;
+ data->state = running;
+ break;
+ }
+ data->total_in += in.pos - ipos;
+ data->cur_frame_in += in.pos - ipos;
+ data->cur_frame_out += data->out.pos - opos;
+ if (data->state == running &&
+ data->cur_frame_in >= data->max_frame_size) {
+ data->state = finishing;
+ }
+ if (data->out.pos == data->out.size ||
+ (flush && data->out.pos > 0)) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out.dst, data->out.pos);
if (ret != ARCHIVE_OK)
- return (ARCHIVE_FATAL);
+ goto fatal;
data->out.pos = 0;
}
-
- /* If there's nothing to do, we're done. */
- if (!finishing && in.pos == in.size)
- return (ARCHIVE_OK);
-
- {
- const size_t zstdret = !finishing ?
- ZSTD_compressStream(data->cstream, &data->out, &in)
- : ZSTD_endStream(data->cstream, &data->out);
-
- if (ZSTD_isError(zstdret)) {
- archive_set_error(f->archive,
- ARCHIVE_ERRNO_MISC,
- "Zstd compression failed: %s",
- ZSTD_getErrorName(zstdret));
- return (ARCHIVE_FATAL);
- }
-
- /* If we're finishing, 0 means nothing left to flush */
- if (finishing && zstdret == 0) {
- const int ret = __archive_write_filter(f->next_filter,
- data->out.dst, data->out.pos);
- return (ret);
- }
- }
}
+zstd_fatal:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Zstd compression failed: %s",
+ ZSTD_getErrorName(zstdret));
+fatal:
+ return (ARCHIVE_FATAL);
}
#else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */
@@ -367,17 +420,9 @@ archive_compressor_zstd_open(struct archive_write_filter *f)
archive_strcpy(&as, "zstd --no-check");
if (data->compression_level < CLEVEL_STD_MIN) {
- struct archive_string as2;
- archive_string_init(&as2);
- archive_string_sprintf(&as2, " --fast=%d", -data->compression_level);
- archive_string_concat(&as, &as2);
- archive_string_free(&as2);
+ archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
} else {
- struct archive_string as2;
- archive_string_init(&as2);
- archive_string_sprintf(&as2, " -%d", data->compression_level);
- archive_string_concat(&as, &as2);
- archive_string_free(&as2);
+ archive_string_sprintf(&as, " -%d", data->compression_level);
}
if (data->compression_level > CLEVEL_STD_MAX) {
@@ -385,11 +430,7 @@ archive_compressor_zstd_open(struct archive_write_filter *f)
}
if (data->threads != 0) {
- struct archive_string as2;
- archive_string_init(&as2);
- archive_string_sprintf(&as2, " --threads=%d", data->threads);
- archive_string_concat(&as, &as2);
- archive_string_free(&as2);
+ archive_string_sprintf(&as, " --threads=%d", data->threads);
}
f->write = archive_compressor_zstd_write;
@@ -408,6 +449,14 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
}
static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+ (void)f; /* UNUSED */
+
+ return (ARCHIVE_OK);
+}
+
+static int
archive_compressor_zstd_close(struct archive_write_filter *f)
{
struct private_data *data = (struct private_data *)f->data;
diff --git a/src/libs/3rdparty/libarchive/archive_write_disk_posix.c b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
index dd7eb9a5e..c8c2e1058 100644
--- a/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
+++ b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
@@ -397,6 +397,7 @@ static int set_times_from_entry(struct archive_write_disk *);
static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
static ssize_t write_data_block(struct archive_write_disk *,
const char *, size_t);
+static void close_file_descriptor(struct archive_write_disk *);
static int _archive_write_disk_close(struct archive *);
static int _archive_write_disk_free(struct archive *);
@@ -514,7 +515,12 @@ lazy_stat(struct archive_write_disk *a)
* XXX At this point, symlinks should not be hit, otherwise
* XXX a race occurred. Do we want to check explicitly for that?
*/
- if (lstat(a->name, &a->st) == 0) {
+#ifdef HAVE_LSTAT
+ if (lstat(a->name, &a->st) == 0)
+#else
+ if (la_stat(a->name, &a->st) == 0)
+#endif
+ {
a->pst = &a->st;
return (ARCHIVE_OK);
}
@@ -1605,12 +1611,12 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff,
"Seek failed");
return (ARCHIVE_FATAL);
} else if (a->offset > a->fd_offset) {
- int64_t skip = a->offset - a->fd_offset;
+ uint64_t skip = a->offset - a->fd_offset;
char nullblock[1024];
memset(nullblock, 0, sizeof(nullblock));
while (skip > 0) {
- if (skip > (int64_t)sizeof(nullblock))
+ if (skip > sizeof(nullblock))
bytes_written = hfs_write_decmpfs_block(
a, nullblock, sizeof(nullblock));
else
@@ -1725,8 +1731,10 @@ _archive_write_disk_finish_entry(struct archive *_a)
else
r = hfs_write_data_block(
a, null_d, a->file_remaining_bytes);
- if (r < 0)
+ if (r < 0) {
+ close_file_descriptor(a);
return ((int)r);
+ }
}
#endif
} else {
@@ -1735,6 +1743,7 @@ _archive_write_disk_finish_entry(struct archive *_a)
a->filesize == 0) {
archive_set_error(&a->archive, errno,
"File size could not be restored");
+ close_file_descriptor(a);
return (ARCHIVE_FAILED);
}
#endif
@@ -1744,8 +1753,10 @@ _archive_write_disk_finish_entry(struct archive *_a)
* to see what happened.
*/
a->pst = NULL;
- if ((ret = lazy_stat(a)) != ARCHIVE_OK)
- return (ret);
+ if ((ret = lazy_stat(a)) != ARCHIVE_OK) {
+ close_file_descriptor(a);
+ return (ret);
+ }
/* We can use lseek()/write() to extend the file if
* ftruncate didn't work or isn't available. */
if (a->st.st_size < a->filesize) {
@@ -1753,11 +1764,13 @@ _archive_write_disk_finish_entry(struct archive *_a)
if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
archive_set_error(&a->archive, errno,
"Seek failed");
+ close_file_descriptor(a);
return (ARCHIVE_FATAL);
}
if (write(a->fd, &nul, 1) < 0) {
archive_set_error(&a->archive, errno,
"Write to restore size failed");
+ close_file_descriptor(a);
return (ARCHIVE_FATAL);
}
a->pst = NULL;
@@ -1996,6 +2009,8 @@ archive_write_disk_new(void)
free(a);
return (NULL);
}
+ a->path_safe.s[0] = 0;
+
#ifdef HAVE_ZLIB_H
a->decmpfs_compression_level = 5;
#endif
@@ -2152,7 +2167,11 @@ restore_entry(struct archive_write_disk *a)
* then don't follow it.
*/
if (r != 0 || !S_ISDIR(a->mode))
+#ifdef HAVE_LSTAT
r = lstat(a->name, &a->st);
+#else
+ r = la_stat(a->name, &a->st);
+#endif
if (r != 0) {
archive_set_error(&a->archive, errno,
"Can't stat existing object");
@@ -2548,7 +2567,12 @@ _archive_write_disk_close(struct archive *_a)
goto skip_fixup_entry;
} else
#endif
- if (lstat(p->name, &st) != 0 ||
+ if (
+#ifdef HAVE_LSTAT
+ lstat(p->name, &st) != 0 ||
+#else
+ la_stat(p->name, &st) != 0 ||
+#endif
la_verify_filetype(st.st_mode,
p->filetype) == 0) {
goto skip_fixup_entry;
@@ -2563,7 +2587,12 @@ _archive_write_disk_close(struct archive *_a)
goto skip_fixup_entry;
} else
#endif
- if (lstat(p->name, &st) != 0 ||
+ if (
+#ifdef HAVE_LSTAT
+ lstat(p->name, &st) != 0 ||
+#else
+ la_stat(p->name, &st) != 0 ||
+#endif
la_verify_filetype(st.st_mode,
p->filetype) == 0) {
goto skip_fixup_entry;
@@ -2783,8 +2812,8 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
!(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT))
/* Platform doesn't have lstat, so we can't look for symlinks. */
(void)path; /* UNUSED */
- (void)error_number; /* UNUSED */
- (void)error_string; /* UNUSED */
+ (void)a_eno; /* UNUSED */
+ (void)a_estr; /* UNUSED */
(void)flags; /* UNUSED */
(void)checking_linkname; /* UNUSED */
return (ARCHIVE_OK);
@@ -2793,7 +2822,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
char *tail;
char *head;
int last;
- char c;
+ char c = '\0';
int r;
struct stat st;
int chdir_fd;
@@ -2857,8 +2886,10 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
/* Check that we haven't hit a symlink. */
#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW);
-#else
+#elif defined(HAVE_LSTAT)
r = lstat(head, &st);
+#else
+ r = la_stat(head, &st);
#endif
if (r != 0) {
tail[0] = c;
@@ -3556,7 +3587,9 @@ set_time(int fd, int mode, const char *name,
(void)fd; /* UNUSED */
(void)mode; /* UNUSED */
(void)name; /* UNUSED */
+ (void)atime; /* UNUSED */
(void)atime_nsec; /* UNUSED */
+ (void)mtime; /* UNUSED */
(void)mtime_nsec; /* UNUSED */
return (ARCHIVE_WARN);
#endif
@@ -4389,7 +4422,12 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
*/
archive_strncpy(&datafork, pathname, p - pathname);
archive_strcat(&datafork, p + 2);
- if (lstat(datafork.s, &st) == -1 ||
+ if (
+#ifdef HAVE_LSTAT
+ lstat(datafork.s, &st) == -1 ||
+#else
+ la_stat(datafork.s, &st) == -1 ||
+#endif
(st.st_mode & AE_IFMT) != AE_IFREG)
goto skip_appledouble;
@@ -4705,5 +4743,17 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
}
#endif
+/*
+ * Close the file descriptor if one is open.
+ */
+static void close_file_descriptor(struct archive_write_disk* a)
+{
+ if (a->fd >= 0) {
+ close(a->fd);
+ a->fd = -1;
+ }
+}
+
+
#endif /* !_WIN32 || __CYGWIN__ */
diff --git a/src/libs/3rdparty/libarchive/archive_write_disk_windows.c b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
index 1b12a299c..7b9ea7493 100644
--- a/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
+++ b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
@@ -254,9 +254,9 @@ static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
* which is high-16-bits of nFileIndexHigh. */
#define bhfi_ino(bhfi) \
((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
- + (bhfi)->nFileIndexLow)
+ | (bhfi)->nFileIndexLow)
#define bhfi_size(bhfi) \
- ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow)
+ ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow)
static int
file_information(struct archive_write_disk *a, wchar_t *path,
@@ -266,6 +266,9 @@ file_information(struct archive_write_disk *a, wchar_t *path,
int r;
DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
WIN32_FIND_DATAW findData;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
if (sim_lstat || mode != NULL) {
h = FindFirstFileW(path, &findData);
@@ -290,14 +293,27 @@ file_information(struct archive_write_disk *a, wchar_t *path,
(findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)))
flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(a->name, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#else
h = CreateFileW(a->name, 0, 0, NULL,
OPEN_EXISTING, flag, NULL);
+#endif
if (h == INVALID_HANDLE_VALUE &&
GetLastError() == ERROR_INVALID_NAME) {
wchar_t *full;
full = __la_win_permissive_name_w(path);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ h = CreateFile2(full, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#else
h = CreateFileW(full, 0, 0, NULL,
OPEN_EXISTING, flag, NULL);
+#endif
free(full);
}
if (h == INVALID_HANDLE_VALUE) {
@@ -559,6 +575,7 @@ la_mktemp(struct archive_write_disk *a)
return (fd);
}
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
static void *
la_GetFunctionKernel32(const char *name)
{
@@ -574,18 +591,24 @@ la_GetFunctionKernel32(const char *name)
}
return (void *)GetProcAddress(lib, name);
}
+#endif
static int
la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
{
- static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES);
- static int set;
+ static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
BOOL ret;
+#if _WIN32_WINNT < _WIN32_WINNT_XP
+ static int set;
+/* CreateHardLinkW is available since XP and always loaded */
if (!set) {
set = 1;
f = la_GetFunctionKernel32("CreateHardLinkW");
}
+#else
+ f = CreateHardLinkW;
+#endif
if (!f) {
errno = ENOTSUP;
return (0);
@@ -624,7 +647,6 @@ static int
la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
int linktype) {
static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
- static int set;
wchar_t *ttarget, *p;
size_t len;
DWORD attrs = 0;
@@ -632,10 +654,20 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
DWORD newflags = 0;
BOOL ret = 0;
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+/* CreateSymbolicLinkW is available since Vista and always loaded */
+ static int set;
if (!set) {
set = 1;
f = la_GetFunctionKernel32("CreateSymbolicLinkW");
}
+#else
+# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ f = CreateSymbolicLinkW;
+# else
+ f = NULL;
+# endif
+#endif
if (!f)
return (0);
@@ -1185,6 +1217,8 @@ _archive_write_disk_finish_entry(struct archive *_a)
if (la_ftruncate(a->fh, a->filesize) == -1) {
archive_set_error(&a->archive, errno,
"File size could not be restored");
+ CloseHandle(a->fh);
+ a->fh = INVALID_HANDLE_VALUE;
return (ARCHIVE_FAILED);
}
}
@@ -1370,6 +1404,7 @@ archive_write_disk_new(void)
free(a);
return (NULL);
}
+ a->path_safe.s[0] = 0;
return (&a->archive);
}
@@ -1655,6 +1690,9 @@ create_filesystem_object(struct archive_write_disk *a)
mode_t final_mode, mode;
int r;
DWORD attrs = 0;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
/* We identify hard/symlinks according to the link names. */
/* Since link(2) and symlink(2) don't handle modes, we're done here. */
@@ -1718,8 +1756,16 @@ create_filesystem_object(struct archive_write_disk *a)
a->todo = 0;
a->deferred = 0;
} else if (r == 0 && a->filesize > 0) {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ a->fh = CreateFile2(namefull, GENERIC_WRITE, 0,
+ TRUNCATE_EXISTING, &createExParams);
+#else
a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,
TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
if (a->fh == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
r = errno;
@@ -1782,14 +1828,27 @@ create_filesystem_object(struct archive_write_disk *a)
a->tmpname = NULL;
fullname = a->name;
/* O_WRONLY | O_CREAT | O_EXCL */
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+ CREATE_NEW, &createExParams);
+#else
a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
if (a->fh == INVALID_HANDLE_VALUE &&
GetLastError() == ERROR_INVALID_NAME &&
fullname == a->name) {
fullname = __la_win_permissive_name_w(a->name);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+ CREATE_NEW, &createExParams);
+#else
a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
}
if (a->fh == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
@@ -2154,6 +2213,8 @@ check_symlinks(struct archive_write_disk *a)
return (ARCHIVE_FAILED);
}
}
+ if (!c)
+ break;
pn[0] = c;
pn++;
}
@@ -2258,6 +2319,9 @@ cleanup_pathname(struct archive_write_disk *a, wchar_t *name)
return (ARCHIVE_FAILED);
} else
p += 4;
+ /* Network drive path like "\\<server-name>\<share-name>\file" */
+ } else if (p[0] == L'\\' && p[1] == L'\\') {
+ p += 2;
}
/* Skip leading drive letter from archives created
@@ -2545,14 +2609,25 @@ set_times(struct archive_write_disk *a,
hw = NULL;
} else {
wchar_t *ws;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
if (S_ISLNK(mode))
return (ARCHIVE_OK);
ws = __la_win_permissive_name_w(name);
if (ws == NULL)
goto settimes_failed;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0,
+ OPEN_EXISTING, &createExParams);
+#else
hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,
0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
free(ws);
if (hw == INVALID_HANDLE_VALUE)
goto settimes_failed;
diff --git a/src/libs/3rdparty/libarchive/archive_write_private.h b/src/libs/3rdparty/libarchive/archive_write_private.h
index 155fdd734..6522e6521 100644
--- a/src/libs/3rdparty/libarchive/archive_write_private.h
+++ b/src/libs/3rdparty/libarchive/archive_write_private.h
@@ -53,6 +53,7 @@ struct archive_write_filter {
const char *key, const char *value);
int (*open)(struct archive_write_filter *);
int (*write)(struct archive_write_filter *, const void *, size_t);
+ int (*flush)(struct archive_write_filter *);
int (*close)(struct archive_write_filter *);
int (*free)(struct archive_write_filter *);
void *data;
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c b/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c
index d5ca9a665..1e40601c4 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c
@@ -165,7 +165,7 @@ struct file {
mode_t mode;
uint32_t crc32;
- signed int dir:1;
+ unsigned dir:1;
};
struct _7zip {
@@ -1809,11 +1809,11 @@ compression_init_encoder_bzip2(struct archive *a,
* of ugly hackery to convert a const * pointer to
* a non-const pointer. */
strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
- strm->avail_in = lastrm->avail_in;
+ strm->avail_in = (uint32_t)lastrm->avail_in;
strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
strm->next_out = (char *)lastrm->next_out;
- strm->avail_out = lastrm->avail_out;
+ strm->avail_out = (uint32_t)lastrm->avail_out;
strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
@@ -1842,11 +1842,11 @@ compression_code_bzip2(struct archive *a,
* of ugly hackery to convert a const * pointer to
* a non-const pointer. */
strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
- strm->avail_in = lastrm->avail_in;
+ strm->avail_in = (uint32_t)lastrm->avail_in;
strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
strm->next_out = (char *)lastrm->next_out;
- strm->avail_out = lastrm->avail_out;
+ strm->avail_out = (uint32_t)lastrm->avail_out;
strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
r = BZ2_bzCompress(strm,
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c
index 58b7216a8..2a3ae07fa 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c
@@ -289,12 +289,12 @@ struct isoent {
struct extr_rec *current;
} extr_rec_list;
- signed int virtual:1;
+ unsigned int virtual:1;
/* If set to one, this file type is a directory.
* A convenience flag to be used as
* "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".
*/
- signed int dir:1;
+ unsigned int dir:1;
};
struct hardlink {
@@ -652,7 +652,7 @@ struct iso_option {
#define VOLUME_IDENTIFIER_SIZE 32
/*
- * Usage : !zisofs [DEFAULT]
+ * Usage : !zisofs [DEFAULT]
* : Disable to generate RRIP 'ZF' extension.
* : zisofs
* : Make files zisofs file and generate RRIP 'ZF'
@@ -689,7 +689,7 @@ struct iso9660 {
uint64_t bytes_remaining;
int need_multi_extent;
- /* Temporary string buffer for Joliet extension. */
+ /* Temporary string buffer for Joliet extension. */
struct archive_string utf16be;
struct archive_string mbs;
@@ -755,9 +755,9 @@ struct iso9660 {
/* Used for making zisofs. */
struct {
- signed int detect_magic:1;
- signed int making:1;
- signed int allzero:1;
+ unsigned int detect_magic:1;
+ unsigned int making:1;
+ unsigned int allzero:1;
unsigned char magic_buffer[64];
int magic_cnt;
@@ -2521,12 +2521,11 @@ get_gmoffset(struct tm *tm)
static void
get_tmfromtime(struct tm *tm, time_t *t)
{
-#if HAVE_LOCALTIME_R
+#if HAVE_LOCALTIME_S
+ localtime_s(tm, t);
+#elif HAVE_LOCALTIME_R
tzset();
localtime_r(t, tm);
-#elif HAVE__LOCALTIME64_S
- __time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits
- _localtime64_s(tm, &tmp_t);
#else
memcpy(tm, localtime(t), sizeof(*tm));
#endif
@@ -4074,11 +4073,8 @@ write_information_block(struct archive_write *a)
}
memset(info.s, 0, info_size);
opt = 0;
-#if defined(HAVE__CTIME64_S)
- {
- __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits
- _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp));
- }
+#if defined(HAVE_CTIME_S)
+ ctime_s(buf, sizeof(buf), &(iso9660->birth_time));
#elif defined(HAVE_CTIME_R)
ctime_r(&(iso9660->birth_time), buf);
#else
@@ -7802,8 +7798,8 @@ struct zisofs_extract {
uint64_t pz_uncompressed_size;
size_t uncompressed_buffer_size;
- signed int initialized:1;
- signed int header_passed:1;
+ unsigned int initialized:1;
+ unsigned int header_passed:1;
uint32_t pz_offset;
unsigned char *block_pointers;
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
index 52911491f..c9c159164 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
@@ -100,6 +100,7 @@ static int has_non_ASCII(const char *);
static void sparse_list_clear(struct pax *);
static int sparse_list_add(struct pax *, int64_t, int64_t);
static char *url_encode(const char *in);
+static time_t get_ustar_max_mtime(void);
/*
* Set output format to 'restricted pax' format.
@@ -595,6 +596,8 @@ archive_write_pax_header(struct archive_write *a,
need_extension = 0;
pax = (struct pax *)a->format_data;
+ const time_t ustar_max_mtime = get_ustar_max_mtime();
+
/* Sanity check. */
if (archive_entry_pathname(entry_original) == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -1116,16 +1119,13 @@ archive_write_pax_header(struct archive_write *a,
}
/*
- * Technically, the mtime field in the ustar header can
- * support 33 bits, but many platforms use signed 32-bit time
- * values. The cutoff of 0x7fffffff here is a compromise.
* Yes, this check is duplicated just below; this helps to
* avoid writing an mtime attribute just to handle a
* high-resolution timestamp in "restricted pax" mode.
*/
if (!need_extension &&
((archive_entry_mtime(entry_main) < 0)
- || (archive_entry_mtime(entry_main) >= 0x7fffffff)))
+ || (archive_entry_mtime(entry_main) >= ustar_max_mtime)))
need_extension = 1;
/* I use a star-compatible file flag attribute. */
@@ -1190,7 +1190,7 @@ archive_write_pax_header(struct archive_write *a,
if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
need_extension) {
if (archive_entry_mtime(entry_main) < 0 ||
- archive_entry_mtime(entry_main) >= 0x7fffffff ||
+ archive_entry_mtime(entry_main) >= ustar_max_mtime ||
archive_entry_mtime_nsec(entry_main) != 0)
add_pax_attr_time(&(pax->pax_header), "mtime",
archive_entry_mtime(entry_main),
@@ -1428,7 +1428,7 @@ archive_write_pax_header(struct archive_write *a,
/* Copy mtime, but clip to ustar limits. */
s = archive_entry_mtime(entry_main);
if (s < 0) { s = 0; }
- if (s >= 0x7fffffff) { s = 0x7fffffff; }
+ if (s > ustar_max_mtime) { s = ustar_max_mtime; }
archive_entry_set_mtime(pax_attr_entry, s, 0);
/* Standard ustar doesn't support atime. */
@@ -1717,7 +1717,7 @@ build_pax_attribute_name(char *dest, const char *src)
* to having clients override it.
*/
#if HAVE_GETPID && 0 /* Disable this for now; see above comment. */
- sprintf(buff, "PaxHeader.%d", getpid());
+ snprintf(buff, sizeof(buff), "PaxHeader.%d", getpid());
#else
/* If the platform can't fetch the pid, don't include it. */
strcpy(buff, "PaxHeader");
@@ -2046,3 +2046,18 @@ sparse_list_add(struct pax *pax, int64_t offset, int64_t length)
return (_sparse_list_add_block(pax, offset, length, 0));
}
+static time_t
+get_ustar_max_mtime(void)
+{
+ /*
+ * Technically, the mtime field in the ustar header can
+ * support 33 bits. We are using all of them to keep
+ * tar/test/test_option_C_mtree.c simple and passing after 2038.
+ * For platforms that use signed 32-bit time values we
+ * use the 32-bit maximum.
+ */
+ if (sizeof(time_t) > sizeof(int32_t))
+ return (time_t)0x1ffffffff;
+ else
+ return (time_t)0x7fffffff;
+}
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c b/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c
index 46b057341..0ef003e2f 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c
@@ -329,30 +329,21 @@ xstrftime(struct archive_string *as, const char *fmt, time_t t)
{
/** like strftime(3) but for time_t objects */
struct tm *rt;
-#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
struct tm timeHere;
#endif
-#if defined(HAVE__GMTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
char strtime[100];
size_t len;
-#ifdef HAVE_GMTIME_R
- if ((rt = gmtime_r(&t, &timeHere)) == NULL)
- return;
-#elif defined(HAVE__GMTIME64_S)
- tmptime = t;
- terr = _gmtime64_s(&timeHere, &tmptime);
- if (terr)
- rt = NULL;
- else
- rt = &timeHere;
+#if defined(HAVE_GMTIME_S)
+ rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+#elif defined(HAVE_GMTIME_R)
+ rt = gmtime_r(&t, &timeHere);
#else
- if ((rt = gmtime(&t)) == NULL)
- return;
+ rt = gmtime(&t);
#endif
+ if (!rt)
+ return;
/* leave the hard yacker to our role model strftime() */
len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
archive_strncat(as, strtime, len);
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c
index d885f5c25..7307757d3 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c
@@ -212,8 +212,8 @@ struct file {
struct heap_data data;
struct archive_string script;
- signed int virtual:1;
- signed int dir:1;
+ unsigned int virtual:1;
+ unsigned int dir:1;
};
struct hardlink {
@@ -906,15 +906,11 @@ xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,
{
char timestr[100];
struct tm tm;
-#if defined(HAVE__GMTIME64_S)
- __time64_t tmptime;
-#endif
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+ gmtime_s(&tm, &t);
+#elif defined(HAVE_GMTIME_R)
gmtime_r(&t, &tm);
-#elif defined(HAVE__GMTIME64_S)
- tmptime = t;
- _gmtime64_s(&tm, &tmptime);
#else
memcpy(&tm, gmtime(&t), sizeof(tm));
#endif
diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c b/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c
index 8c14a7027..6821049c9 100644
--- a/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c
@@ -1382,25 +1382,14 @@ dos_time(const time_t unix_time)
{
struct tm *t;
unsigned int dt;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
struct tm tmbuf;
#endif
-#if defined(HAVE__LOCALTIME64_S)
- errno_t terr;
- __time64_t tmptime;
-#endif
- /* This will not preserve time when creating/extracting the archive
- * on two systems with different time zones. */
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+ t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
t = localtime_r(&unix_time, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
- tmptime = unix_time;
- terr = _localtime64_s(&tmbuf, &tmptime);
- if (terr)
- t = NULL;
- else
- t = &tmbuf;
#else
t = localtime(&unix_time);
#endif
diff --git a/src/libs/3rdparty/libarchive/config/mac/config.h b/src/libs/3rdparty/libarchive/config/mac/config.h
index 80866d915..d20c30769 100644
--- a/src/libs/3rdparty/libarchive/config/mac/config.h
+++ b/src/libs/3rdparty/libarchive/config/mac/config.h
@@ -205,7 +205,7 @@ typedef uint64_t uintmax_t;
/* #undef ARCHIVE_CRYPTO_MD5_LIBC */
/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
-#define ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1
+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */
/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
@@ -229,7 +229,7 @@ typedef uint64_t uintmax_t;
/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */
/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
-#define ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */
/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
@@ -250,7 +250,7 @@ typedef uint64_t uintmax_t;
/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */
/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
-#define ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */
/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
@@ -271,7 +271,7 @@ typedef uint64_t uintmax_t;
/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */
/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
-#define ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */
/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
@@ -292,7 +292,7 @@ typedef uint64_t uintmax_t;
/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */
/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
-#define ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */
/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
diff --git a/src/libs/3rdparty/libarchive/config_freebsd.h b/src/libs/3rdparty/libarchive/config_freebsd.h
index 758621c4b..669f27246 100644
--- a/src/libs/3rdparty/libarchive/config_freebsd.h
+++ b/src/libs/3rdparty/libarchive/config_freebsd.h
@@ -111,6 +111,8 @@
#define HAVE_FCNTL 1
#define HAVE_FCNTL_H 1
#define HAVE_FDOPENDIR 1
+#define HAVE_FNMATCH 1
+#define HAVE_FNMATCH_H 1
#define HAVE_FORK 1
#define HAVE_FSEEKO 1
#define HAVE_FSTAT 1
@@ -123,6 +125,8 @@
#define HAVE_GETEUID 1
#define HAVE_GETGRGID_R 1
#define HAVE_GETGRNAM_R 1
+#define HAVE_GETLINE 1
+#define HAVE_GETOPT_OPTRESET 1
#define HAVE_GETPID 1
#define HAVE_GETPWNAM_R 1
#define HAVE_GETPWUID_R 1
@@ -201,6 +205,7 @@
#define HAVE_SYS_MOUNT_H 1
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_POLL_H 1
+#define HAVE_SYS_QUEUE_H 1
#define HAVE_SYS_SELECT_H 1
#define HAVE_SYS_STATVFS_H 1
#define HAVE_SYS_STAT_H 1
@@ -234,7 +239,7 @@
#define HAVE_WMEMCPY 1
#define HAVE_WMEMMOVE 1
#define HAVE_ZLIB_H 1
-#define TIME_WITH_SYS_TIME 1
+#define HAVE_SYS_TIME_H 1
#if __FreeBSD_version >= 800505
#define HAVE_LIBLZMA 1
diff --git a/src/libs/3rdparty/libarchive/filter_fork_posix.c b/src/libs/3rdparty/libarchive/filter_fork_posix.c
index ac255c4f8..62085a709 100644
--- a/src/libs/3rdparty/libarchive/filter_fork_posix.c
+++ b/src/libs/3rdparty/libarchive/filter_fork_posix.c
@@ -76,7 +76,7 @@ int
__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
pid_t *out_child)
{
- pid_t child;
+ pid_t child = -1;
int stdin_pipe[2], stdout_pipe[2], tmp;
#if HAVE_POSIX_SPAWNP
posix_spawn_file_actions_t actions;
diff --git a/src/libs/3rdparty/libarchive/filter_fork_windows.c b/src/libs/3rdparty/libarchive/filter_fork_windows.c
index 0b963975b..9e49c5655 100644
--- a/src/libs/3rdparty/libarchive/filter_fork_windows.c
+++ b/src/libs/3rdparty/libarchive/filter_fork_windows.c
@@ -31,6 +31,7 @@
#include "filter_fork.h"
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
/* There are some editions of Windows ("nano server," for example) that
* do not host user32.dll. If we want to keep running on those editions,
* we need to delay-load WaitForInputIdle. */
@@ -224,6 +225,14 @@ fail:
__archive_cmdline_free(acmd);
return ARCHIVE_FAILED;
}
+#else /* !WINAPI_PARTITION_DESKTOP */
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child)
+{
+ (void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child;
+ return ARCHIVE_FAILED;
+}
+#endif /* !WINAPI_PARTITION_DESKTOP */
void
__archive_check_child(int in, int out)
diff --git a/src/libs/3rdparty/libarchive/libarchive.pro b/src/libs/3rdparty/libarchive/libarchive.pro
index a647d6572..0cf6f3097 100644
--- a/src/libs/3rdparty/libarchive/libarchive.pro
+++ b/src/libs/3rdparty/libarchive/libarchive.pro
@@ -166,6 +166,10 @@ SOURCES += $$PWD/archive_acl.c \
$$PWD/filter_fork_posix.c \
$$PWD/xxhash.c
+if (isEmpty(IFW_ZLIB_LIBRARY):contains(QT_MODULES, zlib)) {
+ INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtZlib
+}
+
linux {
INCLUDEPATH += ./config/linux
HEADERS += $$PWD/config/linux/config.h
diff --git a/src/libs/3rdparty/libarchive/qt_attribution.json b/src/libs/3rdparty/libarchive/qt_attribution.json
index 336051d47..76db5e26a 100644
--- a/src/libs/3rdparty/libarchive/qt_attribution.json
+++ b/src/libs/3rdparty/libarchive/qt_attribution.json
@@ -5,7 +5,7 @@
"Description": "Multi-format archive and compression library.",
"QtUsage": "Used for reading and writing archive files in Qt Installer Framework",
"Homepage": "https://www.libarchive.org",
- "Version": "3.6.1",
+ "Version": "3.7.1",
"License": "BSD 2-clause \"Simplified\" License",
"LicenseId": "BSD-2-Clause",
"LicenseFile": "COPYING",
diff --git a/src/libs/3rdparty/libarchive/xxhash.c b/src/libs/3rdparty/libarchive/xxhash.c
index f96e9d934..beacd2391 100644
--- a/src/libs/3rdparty/libarchive/xxhash.c
+++ b/src/libs/3rdparty/libarchive/xxhash.c
@@ -149,6 +149,10 @@ typedef struct _U32_S { U32 v; } _PACKED U32_S;
#if GCC_VERSION >= 409
__attribute__((__no_sanitize_undefined__))
+#else
+# if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+# endif
#endif
#if defined(_MSC_VER)
static __inline U32 A32(const void * x)
diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp
index 5bef651a8..341052650 100644
--- a/src/libs/ifwtools/binarycreator.cpp
+++ b/src/libs/ifwtools/binarycreator.cpp
@@ -43,7 +43,7 @@
#include <QDirIterator>
#include <QDomDocument>
#include <QProcess>
-#include <QRegExp>
+#include <QRegularExpression>
#include <QSettings>
#include <QTemporaryFile>
#include <QTemporaryDir>
@@ -223,7 +223,7 @@ static QVersionNumber readMachOMinimumSystemVersion(QIODevice *device)
}
#endif
-static int assemble(Input input, const QInstaller::Settings &settings, const QString &signingIdentity)
+static int assemble(Input input, const QInstaller::Settings &settings, const BinaryCreatorArgs &args)
{
#ifdef Q_OS_MACOS
if (QInstaller::isInBundle(input.installerExePath)) {
@@ -262,7 +262,7 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt
QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo"));
pkgInfo.open(QIODevice::WriteOnly);
QTextStream pkgInfoStream(&pkgInfo);
- pkgInfoStream << QLatin1String("APPL????") << endl;
+ pkgInfoStream << QLatin1String("APPL????") << Qt::endl;
}
QString iconFile;
@@ -282,44 +282,44 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt
QFile infoPList(fi.filePath() + QLatin1String("/Contents/Info.plist"));
infoPList.open(QIODevice::WriteOnly);
QTextStream plistStream(&infoPList);
- plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << endl;
+ plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << Qt::endl;
plistStream << QLatin1String("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
- "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << endl;
- plistStream << QLatin1String("<plist version=\"1.0\">") << endl;
- plistStream << QLatin1String("<dict>") << endl;
- plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << endl;
+ "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << Qt::endl;
+ plistStream << QLatin1String("<plist version=\"1.0\">") << Qt::endl;
+ plistStream << QLatin1String("<dict>") << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << Qt::endl;
plistStream << QLatin1String("\t<string>") << iconTargetFile << QLatin1String("</string>")
- << endl;
- plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << endl;
- plistStream << QLatin1String("\t<string>APPL</string>") << endl;
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>APPL</string>") << Qt::endl;
#define QUOTE_(x) #x
#define QUOTE(x) QUOTE_(x)
- plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << endl;
+ plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << Qt::endl;
plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>")
- << endl;
- plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << endl;
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << Qt::endl;
plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>")
- << endl;
+ << Qt::endl;
#undef QUOTE
#undef QUOTE_
- plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << endl;
- plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << endl;
- plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << endl;
+ plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << Qt::endl;
plistStream << QLatin1String("\t<string>") << fi.completeBaseName() << QLatin1String("</string>")
- << endl;
- plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << endl;
- plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << endl;
- plistStream << QLatin1String("\t<key>NOTE</key>") << endl;
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << Qt::endl;
+ plistStream << QLatin1String("\t<key>NOTE</key>") << Qt::endl;
plistStream << QLatin1String("\t<string>This file was generated by Qt Installer Framework.</string>")
- << endl;
- plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << endl;
- plistStream << QLatin1String("\t<string>NSApplication</string>") << endl;
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>NSApplication</string>") << Qt::endl;
if (!minimumSystemVersion.isEmpty()) {
- plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << endl;
- plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << endl;
+ plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << Qt::endl;
}
- plistStream << QLatin1String("</dict>") << endl;
- plistStream << QLatin1String("</plist>") << endl;
+ plistStream << QLatin1String("</dict>") << Qt::endl;
+ plistStream << QLatin1String("</plist>") << Qt::endl;
input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath)
.arg(fi.completeBaseName());
@@ -405,22 +405,26 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt
QInstaller::appendData(&out, &exe, exe.size());
#endif
- foreach (const QInstallerTools::PackageInfo &info, input.packages) {
- QInstaller::ResourceCollection collection;
- collection.setName(info.name.toUtf8());
-
- qDebug() << "Creating resource archive for" << info.name;
- foreach (const QString &copiedFile, info.copiedFiles) {
- const QSharedPointer<Resource> resource(new Resource(copiedFile));
- qDebug().nospace() << "Appending " << copiedFile << " (" << humanReadableSize(resource->size()) << ")";
- collection.appendResource(resource);
+ if (!args.createMaintenanceTool) {
+ foreach (const QInstallerTools::PackageInfo &info, input.packages) {
+ QInstaller::ResourceCollection collection;
+ collection.setName(info.name.toUtf8());
+ qDebug() << "Creating resource archive for" << info.name;
+ foreach (const QString &copiedFile, info.copiedFiles) {
+ const QSharedPointer<Resource> resource(new Resource(copiedFile));
+ qDebug().nospace() << "Appending " << copiedFile << " (" << humanReadableSize(resource->size()) << ")";
+ collection.appendResource(resource);
+ }
+ input.manager.insertCollection(collection);
}
- input.manager.insertCollection(collection);
+
+ const QList<QInstaller::OperationBlob> operations;
+ BinaryContent::writeBinaryContent(&out, operations, input.manager,
+ BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie);
+ } else {
+ createMTDatFile(out);
}
- const QList<QInstaller::OperationBlob> operations;
- BinaryContent::writeBinaryContent(&out, operations, input.manager,
- BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie);
} catch (const Error &e) {
qCritical("Error occurred while assembling the installer: %s", qPrintable(e.message()));
QFile::remove(tempFile);
@@ -445,14 +449,14 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt
QFile::remove(tempFile);
#ifdef Q_OS_MACOS
- if (isBundle && !signingIdentity.isEmpty()) {
+ if (isBundle && !args.signingIdentity.isEmpty()) {
qDebug() << "Signing .app bundle...";
QProcess p;
p.start(QLatin1String("codesign"),
QStringList() << QLatin1String("--force")
<< QLatin1String("--deep")
- << QLatin1String("--sign") << signingIdentity
+ << QLatin1String("--sign") << args.signingIdentity
<< bundle);
if (!p.waitForFinished(-1)) {
@@ -503,8 +507,6 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt
QDir(bundle).removeRecursively();
qDebug() << "done.";
}
-#else
- Q_UNUSED(signingIdentity)
#endif
return EXIT_SUCCESS;
}
@@ -635,8 +637,8 @@ void QInstallerTools::copyConfigData(const QString &configFile, const QString &t
continue;
}
- QString newName = domElement.text().replace(QRegExp(QLatin1String("\\\\|/|\\.|:")),
- QLatin1String("_"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:"));
+ QString newName = domElement.text().replace(regex, QLatin1String("_"));
QString targetFile;
QFileInfo elementFileInfo;
@@ -702,13 +704,13 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
// Begin check arguments
foreach (const QString &packageDir, args.packagesDirectories) {
- if (!QFileInfo(packageDir).exists()) {
+ if (!QFileInfo::exists(packageDir)) {
argumentError = QString::fromLatin1("Error: Package directory not found at the specified location.");
return EXIT_FAILURE;
}
}
foreach (const QString &repositoryDir, args.repositoryDirectories) {
- if (!QFileInfo(repositoryDir).exists()) {
+ if (!QFileInfo::exists(repositoryDir)) {
argumentError = QString::fromLatin1("Error: Only local filesystem repositories now supported.");
return EXIT_FAILURE;
}
@@ -719,12 +721,12 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
"contain any components apart from the root component.");
return EXIT_FAILURE;
}
- if (!QFileInfo(args.templateBinary).exists()) {
+ if (!QFileInfo::exists(args.templateBinary)) {
#ifdef Q_OS_WIN
if (!args.templateBinary.endsWith(suffix))
args.templateBinary = args.templateBinary + suffix;
// Try again with added executable suffix
- if (!QFileInfo(args.templateBinary).exists()) {
+ if (!QFileInfo::exists(args.templateBinary)) {
argumentError = QString::fromLatin1("Error: Template base binary not found at the specified location.");
return EXIT_FAILURE;
}
@@ -754,7 +756,7 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
"--offline-only at the same time.");
return EXIT_FAILURE;
}
- if (args.target.isEmpty() && !args.compileResource) {
+ if (args.target.isEmpty() && !args.compileResource && !args.createMaintenanceTool) {
argumentError = QString::fromLatin1("Error: Target parameter missing.");
return EXIT_FAILURE;
}
@@ -762,7 +764,9 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
argumentError = QString::fromLatin1("Error: No configuration file selected.");
return EXIT_FAILURE;
}
- if (args.packagesDirectories.isEmpty() && args.repositoryDirectories.isEmpty() && !args.compileResource) {
+ if (args.packagesDirectories.isEmpty() && args.repositoryDirectories.isEmpty()
+ && !args.compileResource
+ && !args.createMaintenanceTool) {
argumentError = QString::fromLatin1("Error: Both Package directory and Repository parameters missing.");
return EXIT_FAILURE;
}
@@ -838,11 +842,6 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
confInternal.setValue(QLatin1String("offlineOnly"), args.offlineOnly);
}
-#ifdef Q_OS_MACOS
- // on mac, we enforce building a bundle
- if (!args.target.endsWith(QLatin1String(".app")) && !args.target.endsWith(QLatin1String(".dmg")))
- args.target += QLatin1String(".app");
-#endif
if (!args.compileResource) {
// 5; put the copied resources into a resource file
ResourceCollection metaCollection("QResources");
@@ -852,11 +851,20 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
input.manager.insertCollection(metaCollection);
input.packages = packages;
- input.outputPath = args.target;
+ if (args.createMaintenanceTool)
+ input.outputPath = settings.maintenanceToolName();
+ else
+ input.outputPath = args.target;
input.installerExePath = args.templateBinary;
+#ifdef Q_OS_MACOS
+ // on mac, we enforce building a bundle
+ if (!input.outputPath.endsWith(QLatin1String(".app")) && !input.outputPath.endsWith(QLatin1String(".dmg")))
+ input.outputPath += QLatin1String(".app");
+#endif
+
qDebug() << "Creating the binary";
- exitCode = assemble(input, settings, args.signingIdentity);
+ exitCode = assemble(input, settings, args);
} else {
createDefaultResourceFile(tmpMetaDir, QDir::currentPath() + QLatin1String("/update.rcc"));
exitCode = EXIT_SUCCESS;
@@ -878,3 +886,13 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError
return exitCode;
}
+
+void QInstallerTools::createMTDatFile(QFile &datFile)
+{
+ QInstaller::appendInt64(&datFile, 0); // operations start
+ QInstaller::appendInt64(&datFile, 0); // operations end
+ QInstaller::appendInt64(&datFile, 0); // resource count
+ QInstaller::appendInt64(&datFile, 4 * sizeof(qint64)); // data block size
+ QInstaller::appendInt64(&datFile, BinaryContent::MagicUninstallerMarker);
+ QInstaller::appendInt64(&datFile, BinaryContent::MagicCookie);
+}
diff --git a/src/libs/ifwtools/binarycreator.h b/src/libs/ifwtools/binarycreator.h
index 7c14ea039..387195742 100644
--- a/src/libs/ifwtools/binarycreator.h
+++ b/src/libs/ifwtools/binarycreator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -68,6 +68,7 @@ struct IFWTOOLS_EXPORT BinaryCreatorArgs
FilterType ftype = QInstallerTools::Exclude;
bool compileResource = false;
QString signingIdentity;
+ bool createMaintenanceTool = false;
};
class BundleBackup
@@ -76,7 +77,7 @@ public:
explicit BundleBackup(const QString &bundle = QString())
: bundle(bundle)
{
- if (!bundle.isEmpty() && QFileInfo(bundle).exists()) {
+ if (!bundle.isEmpty() && QFileInfo::exists(bundle)) {
backup = QInstaller::generateTemporaryFileName(bundle);
QFile::rename(bundle, backup);
}
@@ -124,6 +125,7 @@ void copyConfigData(const QString &configFile, const QString &targetDir);
void copyHighDPIImage(const QFileInfo &childFileInfo, const QString &childName, const QString &targetFile);
int IFWTOOLS_EXPORT createBinary(BinaryCreatorArgs args, QString &argumentError);
+void IFWTOOLS_EXPORT createMTDatFile(QFile &datFile);
} // namespace QInstallerTools
diff --git a/src/libs/ifwtools/rcc/rcc.cpp b/src/libs/ifwtools/rcc/rcc.cpp
index 16203e2cd..caef84433 100644
--- a/src/libs/ifwtools/rcc/rcc.cpp
+++ b/src/libs/ifwtools/rcc/rcc.cpp
@@ -36,7 +36,7 @@
#include <QtCore/QFile>
#include <QtCore/QIODevice>
#include <QtCore/QLocale>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QStack>
#include <QXmlStreamReader>
@@ -664,7 +664,7 @@ QStringList RCCResourceLibrary::dataFiles() const
pending.push(m_root);
while (!pending.isEmpty()) {
RCCFileInfo *file = pending.pop();
- for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
it != file->m_children.end(); ++it) {
RCCFileInfo *child = it.value();
if (child->m_flags & RCCFileInfo::Directory)
@@ -678,7 +678,7 @@ QStringList RCCResourceLibrary::dataFiles() const
// Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
{
- typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
+ typedef QMultiHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
const QChar slash = QLatin1Char('/');
const ChildConstIterator cend = m_root->m_children.constEnd();
for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) {
@@ -815,7 +815,7 @@ bool RCCResourceLibrary::writeDataBlobs()
QString errorMessage;
while (!pending.isEmpty()) {
RCCFileInfo *file = pending.pop();
- for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
it != file->m_children.end(); ++it) {
RCCFileInfo *child = it.value();
if (child->m_flags & RCCFileInfo::Directory)
@@ -851,7 +851,7 @@ bool RCCResourceLibrary::writeDataNames()
qint64 offset = 0;
while (!pending.isEmpty()) {
RCCFileInfo *file = pending.pop();
- for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
it != file->m_children.end(); ++it) {
RCCFileInfo *child = it.value();
if (child->m_flags & RCCFileInfo::Directory)
@@ -958,7 +958,7 @@ bool RCCResourceLibrary::writeInitializer()
QString initName = m_initName;
if (!initName.isEmpty()) {
initName.prepend(QLatin1Char('_'));
- initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
+ initName.replace(QRegularExpression(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
}
//init
diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp
index 874d88494..9232a02d4 100644
--- a/src/libs/ifwtools/repositorygen.cpp
+++ b/src/libs/ifwtools/repositorygen.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,7 @@
#include "errors.h"
#include "globals.h"
#include "archivefactory.h"
+#include "metadata.h"
#include "settings.h"
#include "qinstallerglobal.h"
#include "utils.h"
@@ -42,7 +43,7 @@
#include "updater.h"
#include <QtCore/QDirIterator>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtXml/QDomDocument>
#include <QTemporaryDir>
@@ -128,6 +129,75 @@ static QStringList copyFilesFromNode(const QString &parentNode, const QString &c
return copiedFiles;
}
+/*
+ Returns \c true if the \a file is an archive or an SHA1 checksum
+ file for an archive, /c false otherwise.
+*/
+static bool isArchiveOrChecksum(const QString &file)
+{
+ if (file.endsWith(QLatin1String(".sha1")))
+ return true;
+
+ for (auto &supportedSuffix : ArchiveFactory::supportedTypes()) {
+ if (file.endsWith(supportedSuffix))
+ return true;
+ }
+ return false;
+}
+
+/*
+ Fills the package \a info with the name of the metadata archive when applicable. Returns
+ \c true if the component has metadata compressed in an archive or uncompressed to cache, or
+ if the metadata archive is redundant. Returns \c false if the component should have metadata
+ but none was found.
+*/
+static bool findMetaFile(const QString &repositoryDir, const QDomElement &packageUpdate, PackageInfo &info)
+{
+ // Note: the order here is important, when updating from an existing
+ // repository we shouldn't drop the empty metadata archives.
+
+ // 1. First, try with normal repository structure
+ QString metaFile = QString::fromLatin1("%1/%3%2").arg(info.directory,
+ QString::fromLatin1("meta.7z"), info.version);
+
+ if (QFileInfo::exists(metaFile)) {
+ info.metaFile = metaFile;
+ return true;
+ }
+
+ // 2. If that does not work, check for fetched temporary repository structure
+ metaFile = QString::fromLatin1("%1/%2-%3-%4").arg(repositoryDir,
+ info.name, info.version, QString::fromLatin1("meta.7z"));
+
+ if (QFileInfo::exists(metaFile)) {
+ info.metaFile = metaFile;
+ return true;
+ }
+
+ // 3. Try with the cached metadata directory structure
+ const QDir packageDir(info.directory);
+ const QStringList cachedMetaFiles = packageDir.entryList(QDir::Files);
+ for (auto &file : cachedMetaFiles) {
+ if (!isArchiveOrChecksum(file))
+ return true; // Return for first non-archive file
+ }
+
+ // 4. The meta archive may be redundant, skip in that case (cached item from a
+ // repository that has empty meta archive)
+ bool metaElementFound = false;
+ const QDomNodeList c1 = packageUpdate.childNodes();
+ for (int i = 0; i < c1.count(); ++i) {
+ const QDomElement e1 = c1.at(i).toElement();
+ for (const QString &meta : scMetaElements) {
+ if (e1.tagName() == meta) {
+ metaElementFound = true;
+ break;
+ }
+ }
+ }
+ return !metaElementFound;
+}
+
void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &metaDataDir,
const PackageInfoVector &packages, const QString &appName, const QString &appVersion,
const QStringList &uniteMetadatas)
@@ -297,39 +367,8 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
root.appendChild(update);
- // copy script file
- const QString script = package.firstChildElement(QLatin1String("Script")).text();
- if (!script.isEmpty()) {
- QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script));
- if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".")
- .arg(QDir::toNativeSeparators(scriptFile.fileName())));
- }
-
- const QString scriptContent = QLatin1String("(function() {")
- + QString::fromUtf8(scriptFile.readAll())
- + QLatin1String(";"
- " if (typeof Component == \"undefined\")"
- " throw \"Missing Component constructor. Please check your script.\";"
- "})();");
-
- // if the user isn't aware of the downloadable archives value we will add it automatically later
- foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive"))
- || scriptContent.contains(QLatin1String("removeDownloadableArchive"));
-
- static QInstaller::ScriptEngine testScriptEngine;
- const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName());
- if (value.isError()) {
- throw QInstaller::Error(QString::fromLatin1("Exception while loading component "
- "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()),
- value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") :
- value.toString() + QStringLiteral(" on line number: ") +
- value.property(QStringLiteral("lineNumber")).toString()));
- }
-
- const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script));
- copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript);
- }
+ // copy script files
+ copyScriptFiles(childNodes, info, foundDownloadableArchives, targetDir);
// write DownloadableArchives tag if that is missed by the user
if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) {
@@ -338,7 +377,7 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) {
const QString fileName = QFileInfo(filePath).fileName();
// remove unnecessary version string from filename and add it to the list
- realContentFiles.append(fileName.mid(info.version.count()));
+ realContentFiles.append(fileName.mid(info.version.size()));
}
}
@@ -398,6 +437,19 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met
throw Error(QString::fromLatin1("Could not extract archive \"%1\": %2").arg(
QDir::toNativeSeparators(info.metaFile), metaFile->errorString()));
}
+ } else {
+ // The metadata may have been already extracted, i.e. when reading from a
+ // local repository cache.
+ const QDir packageDir(info.directory);
+ const QStringList metaFiles = packageDir.entryList(QDir::Files);
+ for (auto &file : metaFiles) {
+ if (isArchiveOrChecksum(file))
+ continue; // Skip data archives
+
+ const QString source(QString::fromLatin1("%1/%2").arg(info.directory, file));
+ const QString target(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, file));
+ copyWithException(source, target, QLatin1String("cached metadata"));
+ }
}
// Restore "PackageUpdate" node;
@@ -517,8 +569,8 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa
info.version = packageElement.firstChildElement(QLatin1String("Version")).text();
// Version cannot start with comparison characters, be an empty string
// or have whitespaces at the beginning or at the end
- if (!QRegExp(QLatin1String("(?![<=>\\s]+)(.+)")).exactMatch(info.version) ||
- (info.version != info.version.trimmed())) {
+ static const QRegularExpression regex(QLatin1String("^(?![<=>\\s]+)(.+)$"));
+ if (!regex.match(info.version).hasMatch() || (info.version != info.version.trimmed())) {
if (ignoreInvalidPackages)
continue;
throw QInstaller::Error(QString::fromLatin1("Component version for \"%1\" is invalid! <Version>%2</Version>")
@@ -639,22 +691,9 @@ PackageInfoVector QInstallerTools::createListOfRepositoryPackages(const QStringL
info.directory = QString::fromLatin1("%1/%2").arg(it->filePath(), info.name);
if (!hasUnifiedMetaFile) {
const QDomElement sha1 = el.firstChildElement(QInstaller::scSHA1);
- if (!sha1.isNull()) {
- // 1. First, try with normal repository structure
- QString metaFile = QString::fromLatin1("%1/%3%2").arg(info.directory,
- QString::fromLatin1("meta.7z"), info.version);
-
- if (!QFileInfo(metaFile).exists()) {
- // 2. If that does not work, check for fetched temporary repository structure
- metaFile = QString::fromLatin1("%1/%2-%3-%4").arg(it->filePath(),
- info.name, info.version, QString::fromLatin1("meta.7z"));
-
- if (!QFileInfo(metaFile).exists()) {
- throw QInstaller::Error(QString::fromLatin1("Could not find meta archive for component "
- "%1 %2 in repository %3.").arg(info.name, info.version, it->filePath()));
- }
- }
- info.metaFile = metaFile;
+ if (!sha1.isNull() && !findMetaFile(it->filePath(), el, info)) {
+ throw QInstaller::Error(QString::fromLatin1("Could not find metadata archive for component "
+ "%1 %2 in repository %3.").arg(info.name, info.version, it->filePath()));
}
}
@@ -907,6 +946,50 @@ void QInstallerTools::splitMetadata(const QStringList &entryList, const QString
}
}
+void QInstallerTools::copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir)
+{
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomNode node = childNodes.at(i);
+ const QString key = node.nodeName();
+
+ if (key != QLatin1String("Script"))
+ continue;
+ const QString script = node.toElement().text();
+ if (script.isEmpty())
+ continue;
+
+ QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script));
+ if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".")
+ .arg(QDir::toNativeSeparators(scriptFile.fileName())));
+ }
+
+ const QString scriptContent = QLatin1String("(function() {")
+ + QString::fromUtf8(scriptFile.readAll())
+ + QLatin1String(";"
+ " if (typeof Component == \"undefined\")"
+ " throw \"Missing Component constructor. Please check your script.\";"
+ "})();");
+
+ // if the user isn't aware of the downloadable archives value we will add it automatically later
+ foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive"))
+ || scriptContent.contains(QLatin1String("removeDownloadableArchive"));
+
+ static QInstaller::ScriptEngine testScriptEngine;
+ const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName());
+ if (value.isError()) {
+ throw QInstaller::Error(QString::fromLatin1("Exception while loading component "
+ "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()),
+ value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") :
+ value.toString() + QStringLiteral(" on line number: ") +
+ value.property(QStringLiteral("lineNumber")).toString()));
+ }
+
+ const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script));
+ copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript);
+ }
+}
+
void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QString &repoDir,
PackageInfoVector *const infos, const QString &archiveSuffix, Compression compression)
{
diff --git a/src/libs/ifwtools/repositorygen.h b/src/libs/ifwtools/repositorygen.h
index 054c023f4..7ad3dd073 100644
--- a/src/libs/ifwtools/repositorygen.h
+++ b/src/libs/ifwtools/repositorygen.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -88,6 +88,7 @@ void IFWTOOLS_EXPORT compressMetaDirectories(const QString &repoDir, const QStri
QStringList unifyMetadata(const QString &repoDir, const QString &existingRepoDir, QDomDocument doc);
void splitMetadata(const QStringList &entryList, const QString &repoDir, QDomDocument doc,
const QHash<QString, QString> &versionMapping);
+void copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir);
void IFWTOOLS_EXPORT copyMetaData(const QString &outDir, const QString &dataDir, const PackageInfoVector &packages,
const QString &appName, const QString& appVersion, const QStringList &uniteMetadatas);
diff --git a/src/libs/installer/abstracttask.h b/src/libs/installer/abstracttask.h
index 7daff848f..5ee23bef5 100644
--- a/src/libs/installer/abstracttask.h
+++ b/src/libs/installer/abstracttask.h
@@ -31,7 +31,9 @@
#include "runextensions.h"
+#include <QException>
#include <QFutureInterface>
+#include <QVariant>
namespace QInstaller {
diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp
index 9ed7742db..ad80ba7bb 100644
--- a/src/libs/installer/binaryformat.cpp
+++ b/src/libs/installer/binaryformat.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -153,12 +153,19 @@ void Resource::setName(const QByteArray &name)
Opens a resource in QIODevice::ReadOnly mode. The function returns \c true
if successful.
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool Resource::open()
+#else
+bool Resource::open(std::optional<QFile::Permissions> permissions)
+#endif
{
if (isOpen())
return false;
-
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
+#else
+ if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered, permissions)) {
+#endif
setErrorString(m_file.errorString());
return false;
}
diff --git a/src/libs/installer/binaryformat.h b/src/libs/installer/binaryformat.h
index 26d510530..e7505a341 100644
--- a/src/libs/installer/binaryformat.h
+++ b/src/libs/installer/binaryformat.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,7 +58,11 @@ public:
Resource(const QString &path, const Range<qint64> &segment);
~Resource();
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open();
+#else
+ bool open(std::optional<QFile::Permissions> permissions = std::nullopt);
+#endif
void close() override;
bool seek(qint64 pos) override;
diff --git a/src/libs/installer/binaryformatengine.cpp b/src/libs/installer/binaryformatengine.cpp
index ec6926031..7f00c8d47 100644
--- a/src/libs/installer/binaryformatengine.cpp
+++ b/src/libs/installer/binaryformatengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,7 +28,7 @@
#include "binaryformatengine.h"
-#include <QRegExp>
+#include <QRegularExpression>
namespace {
@@ -127,9 +127,16 @@ bool BinaryFormatEngine::close()
/*!
\internal
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool BinaryFormatEngine::open(QIODevice::OpenMode mode)
+#else
+bool BinaryFormatEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions)
+#endif
{
Q_UNUSED(mode)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
+ Q_UNUSED(permissions)
+#endif
return m_resource.isNull() ? false : m_resource->open();
}
@@ -263,15 +270,17 @@ QStringList BinaryFormatEngine::entryList(QDir::Filters filters, const QStringLi
if (filterNames.isEmpty())
return result;
- QList<QRegExp> regexps;
- foreach (const QString &i, filterNames)
- regexps.append(QRegExp(i, Qt::CaseInsensitive, QRegExp::Wildcard));
+ QList<QRegularExpression> regexps;
+ for (const QString &i : filterNames) {
+ regexps.append(QRegularExpression(QRegularExpression::wildcardToRegularExpression(i),
+ QRegularExpression::CaseInsensitiveOption));
+ }
QStringList entries;
- foreach (const QString &i, result) {
+ for (const QString &i : qAsConst(result)) {
bool matched = false;
- foreach (const QRegExp &reg, regexps) {
- matched = reg.exactMatch(i);
+ for (const QRegularExpression &reg : qAsConst(regexps)) {
+ matched = reg.match(i).hasMatch();
if (matched)
break;
}
diff --git a/src/libs/installer/binaryformatengine.h b/src/libs/installer/binaryformatengine.h
index bf72e5f1f..9321e9d9c 100644
--- a/src/libs/installer/binaryformatengine.h
+++ b/src/libs/installer/binaryformatengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,7 +47,12 @@ public:
bool copy(const QString &newName) override;
bool close() override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open(QIODevice::OpenMode mode) override;
+#else
+ bool open(QIODevice::OpenMode mode,
+ std::optional<QFile::Permissions> permissions = std::nullopt) override;
+#endif
qint64 pos() const override;
qint64 read(char *data, qint64 maxlen) override;
bool seek(qint64 offset) override;
diff --git a/src/libs/installer/calculatorbase.cpp b/src/libs/installer/calculatorbase.cpp
new file mode 100644
index 000000000..4f1732677
--- /dev/null
+++ b/src/libs/installer/calculatorbase.cpp
@@ -0,0 +1,76 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "calculatorbase.h"
+
+#include "component.h"
+
+namespace QInstaller {
+
+CalculatorBase::CalculatorBase(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+CalculatorBase::~CalculatorBase()
+{
+}
+
+void CalculatorBase::insertResolution(Component *component, const Resolution resolutionType
+ , const QString &referencedComponent)
+{
+ // Keep the first reason
+ if (m_componentNameResolutionHash.contains(component->name()))
+ return;
+
+ m_componentNameResolutionHash.insert(component->name(),
+ QPair<Resolution, QString>(resolutionType, referencedComponent));
+}
+
+QList<Component *> CalculatorBase::resolvedComponents() const
+{
+ return m_resolvedComponents;
+}
+
+CalculatorBase::Resolution CalculatorBase::resolutionType(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).first;
+}
+
+QString CalculatorBase::error() const
+{
+ return m_errorString;
+}
+
+QString CalculatorBase::referencedComponent(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).second;
+}
+
+} // namespace QInstaller
+
diff --git a/src/libs/installer/calculatorbase.h b/src/libs/installer/calculatorbase.h
new file mode 100644
index 000000000..351658f99
--- /dev/null
+++ b/src/libs/installer/calculatorbase.h
@@ -0,0 +1,85 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef CALCULATORBASE_H
+#define CALCULATORBASE_H
+
+#include "installer_global.h"
+
+#include <QList>
+#include <QString>
+#include <QMetaEnum>
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCore;
+
+class INSTALLER_EXPORT CalculatorBase
+{
+public:
+ enum class Resolution {
+ Selected = 0, // "Selected Component(s) without Dependencies" / "Deselected Component(s)"
+ Replaced, // "Component(s) replaced by other components"
+ VirtualDependent, // "No dependencies to virtual component"
+ Dependent, // "Added as dependency for %1." / "Removed as dependency component is removed"
+ Automatic, // "Component(s) added as automatic dependencies" / "Removed as autodependency component is removed"
+ Resolved, // "Component(s) that have resolved Dependencies"
+ Alias // "Components added from selected alias"
+ };
+
+ CalculatorBase(PackageManagerCore *core);
+ virtual ~CalculatorBase() = 0;
+
+ virtual bool solve(const QList<Component *> &components) = 0;
+ void insertResolution(Component *component, const Resolution resolutionType,
+ const QString &referencedComponent = QString());
+
+ QList<Component *> resolvedComponents() const;
+ virtual QString resolutionText(Component *component) const = 0;
+ Resolution resolutionType(Component *component) const;
+
+ QString error() const;
+
+protected:
+ virtual bool solveComponent(Component *component, const QString &version = QString()) = 0;
+ QString referencedComponent(Component *component) const;
+
+protected:
+ PackageManagerCore *m_core;
+ QString m_errorString;
+
+ QList<Component *> m_resolvedComponents;
+ QHash<QString, QPair<Resolution, QString> > m_componentNameResolutionHash;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::CalculatorBase::Resolution)
+
+#endif // CALCULATORBASE_H
diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp
index a04891303..f9e1f663e 100644
--- a/src/libs/installer/commandlineparser.cpp
+++ b/src/libs/installer/commandlineparser.cpp
@@ -48,7 +48,7 @@ CommandLineParser::CommandLineParser()
"headless mode. The installation operations can be invoked with the following commands and "
"options. Note that the options marked with \"CLI\" are available in the headless mode only.\n")
+ QLatin1String("\nCommands:\n")
- + indent + QString::fromLatin1("%1, %2 - install default or selected packages - <pkg ...>\n")
+ + indent + QString::fromLatin1("%1, %2 - install default or selected packages and aliases - <pkg|alias ...>\n")
.arg(CommandLineOptions::scInstallShort, CommandLineOptions::scInstallLong)
+ indent + QString::fromLatin1("%1, %2 - show available updates information on maintenance tool\n")
.arg(CommandLineOptions::scCheckUpdatesShort, CommandLineOptions::scCheckUpdatesLong)
@@ -56,15 +56,20 @@ CommandLineParser::CommandLineParser()
.arg(CommandLineOptions::scUpdateShort, CommandLineOptions::scUpdateLong)
+ indent + QString::fromLatin1("%1, %2 - uninstall packages and their child components - <pkg ...>\n")
.arg(CommandLineOptions::scRemoveShort, CommandLineOptions::scRemoveLong)
- + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp>\n")
+ + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp for pkg>\n")
.arg(CommandLineOptions::scListShort, CommandLineOptions::scListLong)
- + indent + QString::fromLatin1("%1, %2 - search available packages - <regexp>\n")
+ + indent + QString::fromLatin1("%1, %2 - search available aliases or packages - <regexp for pkg|alias>\n")
.arg(CommandLineOptions::scSearchShort, CommandLineOptions::scSearchLong)
+ indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
.arg(CommandLineOptions::scFilterPackagesLong)
+ indent + indent + QLatin1String("additional filters for the search operation\n")
+ + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
+ .arg(CommandLineOptions::scTypeLong)
+ + indent + indent + QLatin1String("the content type to search\n")
+ indent + QString::fromLatin1("%1, %2 - create offline installer from selected packages - <pkg ...>\n")
.arg(CommandLineOptions::scCreateOfflineShort, CommandLineOptions::scCreateOfflineLong)
+ + indent + QString::fromLatin1("%1, %2 - clear contents of the local metadata cache\n")
+ .arg(CommandLineOptions::scClearCacheShort, CommandLineOptions::scClearCacheLong)
+ indent + QString::fromLatin1("%1, %2 - uninstall all packages and remove entire program directory")
.arg(CommandLineOptions::scPurgeShort, CommandLineOptions::scPurgeLong);
@@ -171,6 +176,16 @@ CommandLineParser::CommandLineParser()
"search command. The keys can be any of the possible package information elements, like "
"\"DisplayName\" and \"Description\"."),
QLatin1String("element=regex,...")), CommandLineOnly);
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scLocalCachePathShort << CommandLineOptions::scLocalCachePathLong,
+ QLatin1String("Sets the path used for local metadata cache. The path must be writable by the current user."),
+ QLatin1String("path")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scTypeLong,
+ QLatin1String("[CLI] Sets the type of the given arguments for commands supporting multiple argument types, "
+ "like \"search\". By default aliases are searched first, and if no matching aliases are found, "
+ "then packages are searched with the same search pattern."),
+ QLatin1String("package|alias")));
// Message query options
addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptMessageQueryShort
@@ -195,7 +210,7 @@ CommandLineParser::CommandLineParser()
QLatin1String("[CLI] Automatically sets the QFileDialog values getExistingDirectory() or getOpenFileName() "
"requested by install script. "
"Several identifier=value pairs can be given separated with comma, "
- "for example --file-query filedialog.id=C:\Temp,filedialog.id2=C:\Temp2"),
+ "for example --file-query filedialog.id=C:/Temp,filedialog.id2=C:/Temp2"),
QLatin1String("identifier=value")), CommandLineOnly);
addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scConfirmCommandShort
<< CommandLineOptions::scConfirmCommandLong, QLatin1String("[CLI] Confirms starting of "
@@ -220,8 +235,8 @@ CommandLineParser::CommandLineParser()
QLatin1String("socketname, key")));
addOption(QCommandLineOption(QStringList()
<< CommandLineOptions::scSquishPortShort << CommandLineOptions::scSquishPortLong,
- QLatin1String("Give a port where Squish can connect to. If no port is given, default port 11233 is "
- "used. Note: To enable Squish support you first need to build IFW with SQUISH_PATH "
+ QLatin1String("Give a port where Squish can connect to. If no port is given, attach to squish "
+ "not done. Note: To enable Squish support you first need to build IFW with SQUISH_PATH "
"parameter where SQUISH_PATH is pointing to your Squish installation folder: "
"<path_to_qt>/bin/qmake -r SQUISH_PATH=<pat_to_squish>"),
QLatin1String("port number")));
@@ -233,13 +248,23 @@ CommandLineParser::CommandLineParser()
"processor cores in the system."),
QLatin1String("threads")));
+ QCommandLineOption cleanupUpdate(CommandLineOptions::scCleanupUpdate);
+ cleanupUpdate.setValueName(QLatin1String("path"));
+ cleanupUpdate.setFlags(QCommandLineOption::HiddenFromHelp);
+ addOption(cleanupUpdate);
+
+ QCommandLineOption cleanupUpdateOnly(CommandLineOptions::scCleanupUpdateOnly);
+ cleanupUpdateOnly.setValueName(QLatin1String("path"));
+ cleanupUpdateOnly.setFlags(QCommandLineOption::HiddenFromHelp);
+ addOption(cleanupUpdateOnly);
+
// Deprecated options
QCommandLineOption deprecatedUpdater(CommandLineOptions::scDeprecatedUpdater);
- deprecatedUpdater.setHidden(true);
+ deprecatedUpdater.setFlags(QCommandLineOption::HiddenFromHelp);
addOption(deprecatedUpdater);
QCommandLineOption deprecatedCheckUpdates(CommandLineOptions::scDeprecatedCheckUpdates);
- deprecatedCheckUpdates.setHidden(true);
+ deprecatedCheckUpdates.setFlags(QCommandLineOption::HiddenFromHelp);
addOption(deprecatedCheckUpdates); // Behaves like check-updates but does not default to verbose output
// Custom extension options
@@ -277,3 +302,42 @@ CommandLineParser::OptionContextFlags CommandLineParser::optionContextFlags(cons
{
return m_optionContextFlagsNameHash.value(option);
}
+
+/*
+ Returns the command line arguments of the application. The returned list
+ is context-aware, i.e. options that are set on the parser with
+ \c OptionContextFlag::NoEchoValue are returned with their value hidden.
+*/
+QStringList CommandLineParser::arguments() const
+{
+ const QStringList arguments = QCoreApplication::arguments();
+ QStringList returnArguments;
+ bool skipNext = false;
+ for (const QString &arg : arguments) {
+ if (skipNext) {
+ skipNext = false;
+ continue;
+ }
+ returnArguments << arg;
+ // Append positional arguments as-is
+ if (!arg.startsWith(QLatin1String("--")) && !arg.startsWith(QLatin1Char('-')))
+ continue;
+
+ QString normalizedOption = arg;
+ while (normalizedOption.startsWith(QLatin1Char('-')))
+ normalizedOption.remove(QLatin1Char('-'));
+
+ const OptionContextFlags flags = optionContextFlags(normalizedOption);
+ if (!flags.testFlag(OptionContextFlag::NoEchoValue))
+ continue;
+
+ QString nextArg = arguments.value(arguments.indexOf(arg) + 1);
+ if (!nextArg.isEmpty() && !nextArg.startsWith(QLatin1String("--"))
+ && !nextArg.startsWith(QLatin1Char('-'))) {
+ nextArg = QLatin1String("******");
+ returnArguments << nextArg;
+ skipNext = true;
+ }
+ }
+ return returnArguments;
+}
diff --git a/src/libs/installer/commandlineparser.h b/src/libs/installer/commandlineparser.h
index f1bedf92e..3c14d9f45 100644
--- a/src/libs/installer/commandlineparser.h
+++ b/src/libs/installer/commandlineparser.h
@@ -37,7 +37,8 @@ class CommandLineParser
{
public:
enum OptionContextFlag {
- CommandLineOnly = 0x1
+ CommandLineOnly = 0x1,
+ NoEchoValue = 0x2
};
Q_DECLARE_FLAGS(OptionContextFlags, OptionContextFlag)
@@ -48,7 +49,7 @@ public:
bool addOptionWithContext(const QCommandLineOption &option, OptionContextFlags flags);
QString helpText() const { return m_parser.helpText(); }
- bool isSet(const QString &option) { return m_parser.isSet(option); }
+ bool isSet(const QString &option) const { return m_parser.isSet(option); }
QStringList unknownOptionNames() const { return m_parser.unknownOptionNames(); }
QStringList positionalArguments() const { return m_parser.positionalArguments(); }
bool parse(const QStringList &argumens) { return m_parser.parse(argumens); }
@@ -56,6 +57,7 @@ public:
QStringList optionNames() const { return m_parser.optionNames(); }
OptionContextFlags optionContextFlags(const QString &option) const;
+ QStringList arguments() const;
private:
QCommandLineParser m_parser;
@@ -64,4 +66,6 @@ private:
QHash<QString, OptionContextFlags> m_optionContextFlagsNameHash;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(CommandLineParser::OptionContextFlags)
+
#endif // COMMANDLINEPARSER_H
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index c7dfa65da..ce76a2927 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,15 +37,18 @@
#include "remoteclient.h"
#include "settings.h"
#include "utils.h"
+#include "constants.h"
#include "updateoperationfactory.h"
#include <productkeycheck.h>
#include <QtCore/QDirIterator>
-#include <QtCore/QRegExp>
#include <QtCore/QTranslator>
#include <QtCore/QRegularExpression>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore/QTextCodec>
+#endif
#include <QApplication>
#include <QtConcurrentFilter>
@@ -61,20 +64,14 @@
#include <private/qv4object_p.h>
#include <algorithm>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+#include <QJSEngine>
+#else
+#include <QQmlEngine>
+#endif
using namespace QInstaller;
-static const QLatin1String scScriptTag("Script");
-static const QLatin1String scVirtual("Virtual");
-static const QLatin1String scInstalled("Installed");
-static const QLatin1String scUpdateText("UpdateText");
-static const QLatin1String scUninstalled("Uninstalled");
-static const QLatin1String scCurrentState("CurrentState");
-static const QLatin1String scForcedInstallation("ForcedInstallation");
-static const QLatin1String scCheckable("Checkable");
-static const QLatin1String scExpandedByDefault("ExpandedByDefault");
-static const QLatin1String scUnstable("Unstable");
-
/*!
\enum QInstaller::Component::UnstableError
@@ -90,6 +87,8 @@ static const QLatin1String scUnstable("Unstable");
Component has dependencies to missing components.
\value InvalidTreeName
Component has an invalid tree name.
+ \value DescendantOfUnstable
+ Component is descendant of an unstable component.
*/
/*!
@@ -251,8 +250,13 @@ static const QLatin1String scUnstable("Unstable");
*/
Component::Component(PackageManagerCore *core)
: d(new ComponentPrivate(core, this))
- , m_defaultArchivePath(QLatin1String("@TargetDir@"))
+ , m_defaultArchivePath(scTargetDirPlaceholder)
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ QJSEngine::setObjectOwnership(this, QJSEngine::CppOwnership);
+#else
+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
+#endif
setPrivate(d);
connect(this, &Component::valueChanged, this, &Component::updateModelData);
@@ -292,11 +296,11 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package)
setValue(scVersion, package.version);
setValue(scInheritVersion, package.inheritVersionFrom);
setValue(scInstalledVersion, package.version);
- setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString());
- setValue(QLatin1String("InstallDate"), package.installDate.toString());
+ setValue(scLastUpdateDate, package.lastUpdateDate.toString());
+ setValue(scInstallDate, package.installDate.toString());
setValue(scUncompressedSize, QString::number(package.uncompressedSize));
- setValue(scDependencies, package.dependencies.join(QLatin1String(",")));
- setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(",")));
+ setValue(scDependencies, package.dependencies.join(scCommaWithSpace));
+ setValue(scAutoDependOn, package.autoDependencies.join(scCommaWithSpace));
setValue(scSortingPriority, QString::number(package.sortingPriority));
setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse);
@@ -343,8 +347,7 @@ void Component::loadDataFromPackage(const Package &package)
setValue(scUpdateText, package.data(scUpdateText).toString());
setValue(scNewComponent, package.data(scNewComponent).toString());
setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString());
-
- setValue(scScriptTag, package.data(scScriptTag).toString());
+ d->m_scriptHash = package.data(scScriptTag).toHash();
setValue(scReplaces, package.data(scReplaces).toString());
setValue(scReleaseDate, package.data(scReleaseDate).toString());
setValue(scCheckable, package.data(scCheckable).toString());
@@ -355,8 +358,9 @@ void Component::loadDataFromPackage(const Package &package)
forced = scFalse;
setValue(scForcedInstallation, forced);
setValue(scContentSha1, package.data(scContentSha1).toString());
+ setValue(scCheckSha1CheckSum, package.data(scCheckSha1CheckSum, scTrue).toString().toLower());
- const auto treeNamePair = package.data(QLatin1String(scTreeName)).value<QPair<QString, bool>>();
+ const auto treeNamePair = package.data(scTreeName).value<QPair<QString, bool>>();
setValue(scTreeName, treeNamePair.first);
d->m_treeNameMoveChildren = treeNamePair.second;
@@ -364,20 +368,20 @@ void Component::loadDataFromPackage(const Package &package)
return;
setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url));
- const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
- if (!uis.isEmpty())
- loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis);
+
+ const QStringList uiList = QInstaller::splitStringWithComma(package.data(scUserInterfaces).toString());
+ if (!uiList.isEmpty())
+ loadUserInterfaces(QDir(scTwoArgs.arg(localTempPath(), name())), uiList);
+
#ifndef IFW_DISABLE_TRANSLATIONS
- const QStringList qms = package.data(QLatin1String("Translations")).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ const QStringList qms = QInstaller::splitStringWithComma(package.data(scTranslations).toString());
if (!qms.isEmpty())
- loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms);
+ loadTranslations(QDir(scTwoArgs.arg(localTempPath(), name())), qms);
#endif
- QHash<QString, QVariant> licenseHash = package.data(QLatin1String("Licenses")).toHash();
+ QHash<QString, QVariant> licenseHash = package.data(scLicenses).toHash();
if (!licenseHash.isEmpty())
- loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash);
- QVariant operationsVariant = package.data(QLatin1String("Operations"));
+ loadLicenses(scTwoArgs.arg(localTempPath(), name()), licenseHash);
+ QVariant operationsVariant = package.data(scOperations);
if (operationsVariant.canConvert<QList<QPair<QString, QVariant>>>())
m_operationsList = operationsVariant.value<QList<QPair<QString, QVariant>>>();
}
@@ -463,12 +467,14 @@ void Component::setValue(const QString &key, const QString &value)
if (key == scName)
d->m_componentName = normalizedValue;
- if (key == scCheckable)
- this->setCheckable(normalizedValue.toLower() == scTrue);
+ if (key == scCheckable) // Non-checkable components can still be toggled in updater
+ this->setCheckable(normalizedValue.toLower() == scTrue || d->m_core->isUpdater());
if (key == scExpandedByDefault)
this->setExpandedByDefault(normalizedValue.toLower() == scTrue);
if (key == scForcedInstallation) {
- if (value == scTrue && !PackageManagerCore::noForceInstallation()) {
+ if (value == scTrue && !d->m_core->isUpdater() && !PackageManagerCore::noForceInstallation()) {
+ // Forced installation components can still be toggled in updater or when
+ // core is set to ignore forced installations.
setCheckable(false);
setCheckState(Qt::Checked);
}
@@ -595,38 +601,45 @@ bool Component::treeNameMoveChildren() const
}
/*!
- Loads the component script into the script engine.
+ Loads the component script into the script engine. Call this method with
+ \a postLoad \c true to a list of components that are updated or installed
+ to improve performance if the amount of components is huge and there are no script
+ functions that need to be called before the installation starts.
*/
-void Component::loadComponentScript()
+void Component::loadComponentScript(const bool postLoad)
{
- const QString script = d->m_vars.value(scScriptTag);
- if (!localTempPath().isEmpty() && !script.isEmpty())
- loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script));
+ const QString installScript(!postLoad ? d->m_scriptHash.value(scInstallScript).toString()
+ : d->m_scriptHash.value(scPostLoadScript).toString());
+
+ if (!localTempPath().isEmpty() && !installScript.isEmpty()) {
+ evaluateComponentScript(scThreeArgs.arg(localTempPath(), name()
+ , installScript), postLoad);
+ }
}
/*!
- Loads the script at \a fileName into the script engine. The installer and all its
- components as well as other useful things are being exported into the script.
- For more information, see \l{Component Scripting}.
-
- Throws an error when either the script at \a fileName could not be opened, or QScriptEngine
- could not evaluate the script.
+ \internal
*/
-void Component::loadComponentScript(const QString &fileName)
+void Component::evaluateComponentScript(const QString &fileName, const bool postScriptContent)
{
// introduce the component object as javascript value and call the name to check that it
// was successful
try {
- d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
- } catch (const Error &error) {
- if (packageManagerCore()->settings().allowUnstableComponents()) {
- setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
- qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ if (postScriptContent) {
+ d->m_postScriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
} else {
- throw error;
+ d->m_scriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
}
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
+ // evaluateComponentScript is called with postScriptContent after we have selected components
+ // and are about to install. Do not allow install if unstable components are allowed
+ // as we then end up installing a component which has invalid script.
+ if (!packageManagerCore()->settings().allowUnstableComponents() || postScriptContent)
+ throw error;
}
emit loaded();
@@ -640,7 +653,7 @@ void Component::loadComponentScript(const QString &fileName)
*/
void Component::languageChanged()
{
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("retranslateUi"));
+ callScriptMethod(scRetranslateUi);
}
/*!
@@ -652,7 +665,7 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
{
QDirIterator it(directory.path(), qms, QDir::Files);
const QStringList translations = d->m_core->settings().translations();
- const QString uiLanguage = QLocale().uiLanguages().value(0, QLatin1String("en"));
+ const QString uiLanguage = QLocale().uiLanguages().value(0, scEn);
while (it.hasNext()) {
const QString filename = it.next();
const QString basename = QFileInfo(filename).baseName();
@@ -660,18 +673,18 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
if (!translations.isEmpty()) {
bool found = false;
foreach (const QString &translation, translations)
- found |= translation.startsWith(QLatin1String("ifw_") + basename, Qt::CaseInsensitive);
+ found |= translation.startsWith(scIfw_ + basename, Qt::CaseInsensitive);
if (!found) // don't load the file if it does match the UI language but is not allowed to be used
continue;
} else if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive)) {
continue; // do not load the file if it does not match the UI language
}
- QScopedPointer<QTranslator> translator(new QTranslator(this));
+ std::unique_ptr<QTranslator> translator(new QTranslator(this));
if (translator->load(filename)) {
// Do not throw if translator returns false as it may just be an intentionally
// empty file. See also QTBUG-31031
- qApp->installTranslator(translator.take());
+ qApp->installTranslator(translator.release());
}
}
}
@@ -689,17 +702,17 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
while (it.hasNext()) {
QFile file(it.next());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg(
- it.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
- static QUiLoader loader;
- loader.setTranslationEnabled(true);
- loader.setLanguageChangeEnabled(true);
- QWidget *const widget = loader.load(&file, 0);
+ QUiLoader *const loader = ProductKeyCheck::instance()->uiLoader();
+ loader->setTranslationEnabled(true);
+ loader->setLanguageChangeEnabled(true);
+ QWidget *const widget = loader->load(&file, 0);
if (!widget) {
- throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg(
- it.fileName(), loader.errorString()));
+ throw Error(tr("Cannot load the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), loader->errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
d->scriptEngine()->newQObject(widget);
d->m_userInterfaces.insert(widget->objectName(), widget);
@@ -715,7 +728,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QHash<QString, QVariant>::const_iterator it;
for (it = licenseHash.begin(); it != licenseHash.end(); ++it) {
QVariantMap license = it.value().toMap();
- const QString &fileName = license.value(QLatin1String("file")).toString();
+ const QString &fileName = license.value(scFile).toString();
if (!ProductKeyCheck::instance()->isValidLicenseTextFile(fileName))
continue;
@@ -727,7 +740,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QList<QFileInfo> fileCandidates;
foreach (const QString &locale, QInstaller::localeCandidates(lang)) {
- fileCandidates << QFileInfo(QString::fromLatin1("%1%2_%3.%4").arg(
+ fileCandidates << QFileInfo(scLocalesArgs.arg(
directory, fileInfo.baseName(), locale,
fileInfo.completeSuffix()));
}
@@ -744,12 +757,14 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QFile file(fileInfo.filePath());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested license file \"%1\": %2").arg(
- file.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested license file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ file.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
QTextStream stream(&file);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream.setCodec("UTF-8");
- license.insert(QLatin1String("content"), stream.readAll());
+#endif
+ license.insert(scContent, stream.readAll());
d->m_licenses.insert(it.key(), license);
}
}
@@ -760,8 +775,8 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
*/
void Component::loadXMLOperations()
{
- for (auto operation: m_operationsList) {
- if (operation.first != QLatin1String("Extract"))
+ for (auto operation: qAsConst(m_operationsList)) {
+ if (operation.first != scExtract)
addOperation(operation.first, operation.second.toStringList());
}
}
@@ -772,14 +787,14 @@ void Component::loadXMLOperations()
*/
void Component::loadXMLExtractOperations()
{
- for (auto operation: m_operationsList) {
- if (operation.first == QLatin1String("Extract")) {
+ for (auto &operation: qAsConst(m_operationsList)) {
+ if (operation.first == scExtract) {
// Create hash for Extract operations. Operation has a mandatory extract folder as
// first argument and optional archive name as second argument.
const QStringList &operationArgs = operation.second.toStringList();
if (operationArgs.count() == 2) {
const QString archiveName = value(scVersion) + operationArgs.at(1);
- const QString archivePath = QString::fromLatin1("installer://%1/%2").arg(name()).arg(archiveName);
+ const QString archivePath = scInstallerPrefixWithTwoArgs.arg(name()).arg(archiveName);
m_archivesHash.insert(archivePath, operationArgs.at(0));
} else if (operationArgs.count() == 1) {
m_defaultArchivePath = operationArgs.at(0);
@@ -837,25 +852,23 @@ void Component::createOperationsForPath(const QString &path)
const QFileInfo fi(path);
// don't copy over a checksum file
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(scCreateOperationsForPath, QJSValueList() << path).isUndefined())
+ return;
QString target;
- static const QString prefix = QString::fromLatin1("installer://");
- target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length()));
+ static const QString prefix = scInstallerPrefix;
+ target = scTargetDirPlaceholderWithArg.arg(path.mid(prefix.length() + name().length()));
if (fi.isFile()) {
- static const QString copy = QString::fromLatin1("Copy");
+ static const QString copy = scCopy;
addOperation(copy, QStringList() << fi.filePath() << target);
} else if (fi.isDir()) {
qApp->processEvents();
- static const QString mkdir = QString::fromLatin1("Mkdir");
+ static const QString mkdir = scMkdir;
addOperation(mkdir, QStringList(target));
QDirIterator it(fi.filePath());
@@ -883,14 +896,12 @@ void Component::createOperationsForArchive(const QString &archive)
const QFileInfo fi(archive);
// don't do anything with sha1 files
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(scCreateOperationsForArchive, QJSValueList() << archive).isUndefined())
+ return;
QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive));
const bool isZip = (archiveFile && archiveFile->open(QIODevice::ReadOnly) && archiveFile->isSupported());
@@ -898,9 +909,9 @@ void Component::createOperationsForArchive(const QString &archive)
if (isZip) {
// component.xml can override this value
if (m_archivesHash.contains(archive))
- addOperation(QLatin1String("Extract"), QStringList() << archive << m_archivesHash.value(archive));
+ addOperation(scExtract, QStringList() << archive << m_archivesHash.value(archive));
else
- addOperation(QLatin1String("Extract"), QStringList() << archive << m_defaultArchivePath);
+ addOperation(scExtract, QStringList() << archive << m_defaultArchivePath);
} else {
createOperationsForPath(archive);
}
@@ -912,7 +923,7 @@ void Component::createOperationsForArchive(const QString &archive)
void Component::beginInstallation()
{
// the script can override this method
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("beginInstallation"));
+ callScriptMethod(scBeginInstallation);
}
/*!
@@ -922,10 +933,9 @@ void Component::beginInstallation()
void Component::createOperations()
{
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperations"))
- .isUndefined()) {
- d->m_operationsCreated = true;
- return;
+ if (!callScriptMethod(scCreateOperations).isUndefined()) {
+ d->m_operationsCreated = true;
+ return;
}
loadXMLExtractOperations();
foreach (const QString &archive, archives())
@@ -962,10 +972,17 @@ QList<QPair<QString, bool> > Component::pathsForUninstallation() const
*/
QStringList Component::archives() const
{
- QString pathString = QString::fromLatin1("installer://%1/").arg(name());
+ static const QRegularExpression regExp(scCaretSymbol);
+ QString pathString = scInstallerPrefixWithOneArgs.arg(name());
QStringList archivesNameList = QDir(pathString).entryList();
+
+ // In resources we may have older version of archives, this can happen
+ // when there is offline installer with same component with lower version
+ // number and newer version is available online
+ archivesNameList = archivesNameList.filter(value(scVersion));
+
//RegExp "^" means line beginning
- archivesNameList.replaceInStrings(QRegExp(QLatin1String("^")), pathString);
+ archivesNameList.replaceInStrings(regExp, pathString);
return archivesNameList;
}
@@ -985,6 +1002,15 @@ void Component::addDownloadableArchive(const QString &path)
}
/*!
+ \internal
+*/
+void Component::addDownloadableArchives(const QString& archives)
+{
+ Q_ASSERT(isFromOnlineRepository());
+ d->m_downloadableArchivesVariable = archives;
+}
+
+/*!
Removes the archive \a path previously added via addDownloadableArchive() from this component.
This can only be called if this component was downloaded from an online repository.
@@ -999,9 +1025,15 @@ void Component::removeDownloadableArchive(const QString &path)
/*!
Returns the archives to be downloaded from the online repository before installation.
+ Should be called only once when the installation starts.
*/
-QStringList Component::downloadableArchives() const
+QStringList Component::downloadableArchives()
{
+ const QStringList downloadableArchives = d->m_downloadableArchivesVariable
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ foreach (const QString downloadableArchive, downloadableArchives)
+ addDownloadableArchive(downloadableArchive);
+
return d->m_downloadableArchives;
}
@@ -1062,30 +1094,32 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons
if (!d->m_minimumProgressOperation) {
d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("MinimumProgress"), d->m_core);
- d->m_minimumProgressOperation->setValue(QLatin1String("component"), name());
+ .create(scMinimumProgress, d->m_core);
+ d->m_minimumProgressOperation->setValue(scComponentSmall, name());
d->m_operations.append(d->m_minimumProgressOperation);
}
if (!d->m_licenses.isEmpty()) {
d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("License"), d->m_core);
- d->m_licenseOperation->setValue(QLatin1String("component"), name());
+ .create(scLicense, d->m_core);
+ d->m_licenseOperation->setValue(scComponentSmall, name());
QVariantMap licenses;
const QList<QVariantMap> values = d->m_licenses.values();
for (int i = 0; i < values.count(); ++i) {
- licenses.insert(values.at(i).value(QLatin1String("file")).toString(),
- values.at(i).value(QLatin1String("content")));
+ licenses.insert(values.at(i).value(scFile).toString(),
+ values.at(i).value(scContent));
}
- d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses);
+ d->m_licenseOperation->setValue(scLicensesValue, licenses);
d->m_operations.append(d->m_licenseOperation);
}
}
- OperationList operations = d->m_operations;
- QtConcurrent::blockingFilter(operations, [&](const Operation *op) {
- return mask.testFlag(op->group());
- });
+ OperationList operations;
+ std::copy_if(d->m_operations.begin(), d->m_operations.end(), std::back_inserter(operations),
+ [&](const Operation *op) {
+ return mask.testFlag(op->group());
+ }
+ );
return operations;
}
@@ -1096,7 +1130,7 @@ void Component::addOperation(Operation *operation)
{
d->m_operations.append(operation);
if (RemoteClient::instance().isActive())
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1106,7 +1140,7 @@ void Component::addOperation(Operation *operation)
void Component::addElevatedOperation(Operation *operation)
{
addOperation(operation);
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1161,9 +1195,6 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
return operation;
}
- if (operation->name() == QLatin1String("Delete"))
- operation->setValue(QLatin1String("performUndo"), false);
-
// Operation can contain variables which are resolved when performing the operation
if (operation->requiresUnreplacedVariables())
operation->setArguments(parameters);
@@ -1171,7 +1202,7 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
operation->setArguments(d->m_core->replaceVariables(parameters));
- operation->setValue(QLatin1String("component"), name());
+ operation->setValue(scComponentSmall, name());
return operation;
}
@@ -1190,6 +1221,21 @@ void Component::markComponentUnstable(Component::UnstableError error, const QStr
setValue(scUnstable, scTrue);
QMetaEnum metaEnum = QMetaEnum::fromType<Component::UnstableError>();
emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name());
+
+ // Update the description and tooltip texts to contain
+ // information about the unstable error.
+ updateModelData(scDescription, QString());
+}
+
+QJSValue Component::callScriptMethod(const QString &methodName, const QJSValueList &arguments) const
+{
+ QJSValue scriptContext;
+ if (!d->m_postScriptContext.isUndefined() && d->m_postScriptContext.property(methodName).isCallable())
+ scriptContext = d->m_postScriptContext;
+ else
+ scriptContext = d->m_scriptContext;
+ return d->scriptEngine()->callScriptMethod(scriptContext,
+ methodName, arguments);
}
namespace {
@@ -1211,7 +1257,7 @@ inline bool convert(QQmlV4Function *func, QStringList *toArgs)
QV4::Object *array = val->as<QV4::Object>();
uint length = array->getLength();
for (uint ii = 0; ii < length; ++ii) {
- valtmp = array->getIndexed(ii);
+ valtmp = array->get(ii);
*toArgs << valtmp->toQStringNoThrow();
}
} else {
@@ -1326,6 +1372,15 @@ bool Component::forcedInstallation() const
}
/*!
+ Returns whether this component is essential. Essential components
+ are always installed, and updated before other components.
+*/
+bool Component::isEssential() const
+{
+ return d->m_vars.value(scEssential, scFalse).toLower() == scTrue;
+}
+
+/*!
Sets the validator callback name to \a name.
*/
void Component::setValidatorCallbackName(const QString &name)
@@ -1340,7 +1395,7 @@ void Component::setValidatorCallbackName(const QString &name)
bool Component::validatePage()
{
if (!validatorCallbackName.isEmpty())
- return d->scriptEngine()->callScriptMethod(d->m_scriptContext, validatorCallbackName).toBool();
+ return callScriptMethod(validatorCallbackName).toBool();
return true;
}
@@ -1359,7 +1414,7 @@ void Component::addDependency(const QString &newDependency)
if (oldDependencies.isEmpty())
setValue(scDependencies, newDependency);
else
- setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency);
+ setValue(scDependencies, oldDependencies + scCommaWithSpace + newDependency);
}
/*!
@@ -1367,7 +1422,7 @@ void Component::addDependency(const QString &newDependency)
*/
QStringList Component::dependencies() const
{
- return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scDependencies));
}
/*!
@@ -1375,7 +1430,7 @@ QStringList Component::dependencies() const
*/
QStringList Component::localDependencies() const
{
- return d->m_vars.value(scLocalDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scLocalDependencies));
}
/*!
@@ -1393,7 +1448,7 @@ void Component::addAutoDependOn(const QString &newDependOn)
if (oldDependOn.isEmpty())
setValue(scAutoDependOn, newDependOn);
else
- setValue(scAutoDependOn, oldDependOn + QLatin1String(", ") + newDependOn);
+ setValue(scAutoDependOn, oldDependOn + scCommaWithSpace + newDependOn);
}
QStringList Component::autoDependencies() const
@@ -1485,8 +1540,7 @@ bool Component::isDefault() const
if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) {
QJSValue valueFromScript;
try {
- valueFromScript = d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("isDefault"));
+ valueFromScript = callScriptMethod(scIsDefault);
} catch (const Error &error) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()),
@@ -1720,23 +1774,26 @@ void Component::updateModelData(const QString &key, const QString &data)
setData(humanReadableSize(size), UncompressedSize);
}
- QString tooltipText;
- const QString &updateInfo = d->m_vars.value(scUpdateText);
- if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
- tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
- } else {
- tooltipText = d->m_vars.value(scDescription) + QLatin1String("<br><br>")
- + tr("Update Info: ") + updateInfo;
- }
- if (isUnstable()) {
- tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
+ if (key == scUpdateText || key == scDescription) {
+ QString tooltipText;
+ const QString &updateInfo = d->m_vars.value(scUpdateText);
+ if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
+ tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
+ } else {
+ tooltipText = d->m_vars.value(scDescription) + scBr + scBr
+ + tr("Update Info: ") + updateInfo;
+ }
+ if (isUnstable()) {
+ tooltipText += scBr + tr("There was an error loading the selected component. "
"This component cannot be installed.");
- }
- // replace {external-link}='' fields in component description with proper link tags
- tooltipText.replace(QRegularExpression(QLatin1String("{external-link}='(.*?)'")),
- QLatin1String("<a href=\"\\1\">\\1</a>"));
+ }
+ static const QRegularExpression externalLinkRegexp(QLatin1String("{external-link}='(.*?)'"));
+ static const QLatin1String externalLinkElement(QLatin1String("<a href=\"\\1\">\\1</a>"));
+ // replace {external-link}='' fields in component description with proper link tags
+ tooltipText.replace(externalLinkRegexp, externalLinkElement);
- setData(tooltipText, Qt::ToolTipRole);
+ setData(tooltipText, Qt::ToolTipRole);
+ }
}
/*!
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
index e5f1b38da..8f17b2d98 100644
--- a/src/libs/installer/component.h
+++ b/src/libs/installer/component.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,7 +32,6 @@
#include "constants.h"
#include "component_p.h"
#include "qinstallerglobal.h"
-#include "packagemanagercore.h"
#include <QtCore/QDir>
#include <QtCore/QMetaType>
@@ -44,6 +43,8 @@ QT_FORWARD_DECLARE_CLASS(QQmlV4Function)
namespace QInstaller {
+class PackageManagerCore;
+
class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper
{
Q_OBJECT
@@ -118,10 +119,9 @@ public:
void removeComponent(Component *component);
QList<Component*> descendantComponents() const;
- void loadComponentScript();
+ void loadComponentScript(const bool postLoad = false);
+ void evaluateComponentScript(const QString &fileName, const bool postScriptContext = false);
- //move this to private
- void loadComponentScript(const QString &fileName);
void loadTranslations(const QDir &directory, const QStringList &qms);
void loadUserInterfaces(const QDir &directory, const QStringList &uis);
void loadLicenses(const QString &directory, const QHash<QString, QVariant> &hash);
@@ -150,9 +150,10 @@ public:
Q_INVOKABLE bool addElevatedOperation(QQmlV4Function *args);
bool addElevatedOperation(const QString &operation, const QStringList &parameters);
- QStringList downloadableArchives() const;
+ QStringList downloadableArchives();
Q_INVOKABLE void addDownloadableArchive(const QString &path);
Q_INVOKABLE void removeDownloadableArchive(const QString &path);
+ void addDownloadableArchives(const QString& archives);
QStringList stopProcessForUpdateRequests() const;
Q_INVOKABLE void addStopProcessForUpdateRequest(const QString &process);
@@ -208,6 +209,7 @@ public:
bool isVirtual() const;
bool isSelected() const;
bool forcedInstallation() const;
+ bool isEssential() const;
void setValidatorCallbackName(const QString &name);
@@ -236,6 +238,8 @@ private:
Operation *createOperation(const QString &operationName, const QStringList &parameters);
void markComponentUnstable(const Component::UnstableError error, const QString &errorMessage);
+ QJSValue callScriptMethod(const QString &methodName, const QJSValueList &arguments = QJSValueList()) const;
+
private:
QString validatorCallbackName;
ComponentPrivate *d;
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 4030d266e..bf3941274 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,6 +55,9 @@ ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq)
, m_operationsCreatedSuccessfully(true)
, m_updateIsAvailable(false)
, m_treeNameMoveChildren(false)
+ , m_postLoadScript(false)
+ , m_scriptContext(QJSValue::UndefinedValue)
+ , m_postScriptContext(QJSValue::UndefinedValue)
{
}
@@ -150,7 +153,7 @@ void ComponentModelHelper::setEnabled(bool enabled)
*/
bool ComponentModelHelper::isTristate() const
{
- return (flags() & Qt::ItemIsTristate) != 0;
+ return (flags() & Qt::ItemIsAutoTristate) != 0;
}
/*!
@@ -161,7 +164,7 @@ bool ComponentModelHelper::isTristate() const
*/
void ComponentModelHelper::setTristate(bool tristate)
{
- changeFlags(tristate, Qt::ItemIsTristate);
+ changeFlags(tristate, Qt::ItemIsAutoTristate);
}
/*!
@@ -185,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/component_p.h b/src/libs/installer/component_p.h
index bdc67898d..dea2d4935 100644
--- a/src/libs/installer/component_p.h
+++ b/src/libs/installer/component_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -64,17 +64,21 @@ public:
bool m_operationsCreatedSuccessfully;
bool m_updateIsAvailable;
bool m_treeNameMoveChildren;
+ bool m_postLoadScript;
QString m_componentName;
QUrl m_repositoryUrl;
QString m_localTempPath;
QJSValue m_scriptContext;
+ QJSValue m_postScriptContext;
QHash<QString, QString> m_vars;
QList<Component*> m_childComponents;
QList<Component*> m_allChildComponents;
QStringList m_downloadableArchives;
+ QString m_downloadableArchivesVariable;
QStringList m_stopProcessForUpdateRequests;
QHash<QString, QPointer<QWidget> > m_userInterfaces;
+ QHash<QString, QVariant> m_scriptHash;
// < display name, < file name, file content > >
QHash<QString, QVariantMap> m_licenses;
diff --git a/src/libs/installer/componentalias.cpp b/src/libs/installer/componentalias.cpp
new file mode 100644
index 000000000..955d715fd
--- /dev/null
+++ b/src/libs/installer/componentalias.cpp
@@ -0,0 +1,649 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "componentalias.h"
+
+#include "constants.h"
+#include "globals.h"
+#include "packagemanagercore.h"
+#include "updater.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+namespace QInstaller {
+
+static const QStringList scPossibleElements {
+ scName,
+ scDisplayName,
+ scDescription,
+ scVersion,
+ scVirtual,
+ scRequiredComponents,
+ scRequiredAliases,
+ scOptionalComponents,
+ scOptionalAliases,
+ scReleaseDate
+};
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AliasSource
+ \brief Describes a source for alias declarations.
+*/
+
+/*!
+ \enum QInstaller::AliasSource::SourceFileFormat
+
+ This enum type holds the possible file formats for alias source:
+
+ \value Unknown
+ Invalid or unknown file format.
+ \value Xml
+ XML file format.
+ \value Json
+ JSON file format.
+*/
+
+/*!
+ Constructs an alias source with empty information.
+*/
+AliasSource::AliasSource()
+ : priority(-1)
+{}
+
+/*!
+ Constructs an alias source with source file format \a aFormat, filename \a aFilename, and priority \a aPriority.
+*/
+AliasSource::AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority)
+ : format(aFormat)
+ , filename(aFilename)
+ , priority(aPriority)
+{}
+
+/*!
+ Copy-constructs an alias source from \a other.
+*/
+AliasSource::AliasSource(const AliasSource &other)
+{
+ format = other.format;
+ filename = other.filename;
+ priority = other.priority;
+}
+
+/*!
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+hashValue qHash(const AliasSource &key, hashValue seed)
+{
+ return qHash(key.filename, seed) ^ key.priority;
+}
+
+/*!
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
+*/
+bool operator==(const AliasSource &lhs, const AliasSource &rhs)
+{
+ return lhs.filename == rhs.filename && lhs.priority == rhs.priority && lhs.format == rhs.format;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AliasFinder
+ \brief Creates component alias objects from parsed alias source files, based
+ on version and source priorities.
+*/
+
+/*!
+ Constructs a new alias finder with \a core as the package manager instance.
+*/
+AliasFinder::AliasFinder(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+/*!
+ Destroys the finder and cleans unreleased results.
+*/
+AliasFinder::~AliasFinder()
+{
+ clear();
+}
+
+/*!
+ Runs the finder. Parses the alias source files and creates component alias
+ objects based on the parsed data. Same alias may be declared in multiple source
+ files, thus source priority and version information is used to decide which
+ source is used for creating the alias object.
+
+ Any previous results are cleared when calling this.
+
+ Returns \c true if at least one alias was found, \c false otherwise.
+*/
+bool AliasFinder::run()
+{
+ clear();
+
+ if (m_sources.isEmpty())
+ return false;
+
+ // 1. Parse source files
+ for (auto &source : qAsConst(m_sources)) {
+ if (source.format == AliasSource::SourceFileFormat::Unknown) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unknown alias source format for file:" << source.filename;
+ continue;
+ }
+ if (source.format == AliasSource::SourceFileFormat::Xml)
+ parseXml(source);
+ else if (source.format == AliasSource::SourceFileFormat::Json)
+ parseJson(source);
+ }
+
+ // 2. Create aliases based on priority & version
+ for (auto &data : qAsConst(m_aliasData)) {
+ const QString name = data.value(scName).toString();
+ const Resolution resolution = checkPriorityAndVersion(data);
+ if (resolution == Resolution::KeepExisting)
+ continue;
+
+ if (resolution == Resolution::RemoveExisting)
+ delete m_aliases.take(name);
+
+ ComponentAlias *alias = new ComponentAlias(m_core);
+ AliasData::const_iterator it;
+ for (it = data.cbegin(); it != data.cend(); ++it) {
+ if (it.value().canConvert<QString>())
+ alias->setValue(it.key(), it.value().toString());
+ }
+ m_aliases.insert(name, alias);
+ }
+
+ return !m_aliases.isEmpty();
+}
+
+/*!
+ Returns a list of the found aliases.
+*/
+QList<ComponentAlias *> AliasFinder::aliases() const
+{
+ return m_aliases.values();
+}
+
+/*!
+ Sets the alias sources to look alias information from to \a sources.
+*/
+void AliasFinder::setAliasSources(const QSet<AliasSource> &sources)
+{
+ clear();
+ m_sources = sources;
+}
+
+/*!
+ Clears the results of the finder.
+*/
+void AliasFinder::clear()
+{
+ qDeleteAll(m_aliases);
+
+ m_aliases.clear();
+ m_aliasData.clear();
+}
+
+/*!
+ Reads an XML file specified by \a filename, and constructs a variant map of
+ the data for each alias.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool AliasFinder::parseXml(AliasSource source)
+{
+ QFile file(source.filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open alias definition for reading:" << file.errorString();
+ return false;
+ }
+
+ QString error;
+ int errorLine;
+ int errorColumn;
+
+ QDomDocument doc;
+ if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot read alias definition document:" << error
+ << "line:" << errorLine << "column:" << errorColumn;
+ return false;
+ }
+ file.close();
+
+ const QDomElement root = doc.documentElement();
+ const QDomNodeList children = root.childNodes();
+
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ const QString tag = el.tagName();
+ if (el.isNull() || tag != scAlias) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag;
+ continue;
+ }
+
+ AliasData data;
+ data.insert(QLatin1String("source"), QVariant::fromValue(source));
+
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ const QDomElement el2 = c2.at(j).toElement();
+ const QString tag2 = el2.tagName();
+ if (!scPossibleElements.contains(tag2)) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag2;
+ continue;
+ }
+ data.insert(tag2, el2.text());
+ }
+
+ m_aliasData.insert(data.value(scName).toString(), data);
+ }
+
+ return true;
+}
+
+/*!
+ Reads a JSON file specified by \a source, and constructs a variant map of
+ the data for each alias.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool AliasFinder::parseJson(AliasSource source)
+{
+ QFile file(source.filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open alias definition for reading:" << file.errorString();
+ return false;
+ }
+
+ const QByteArray jsonData = file.readAll();
+ const QJsonDocument doc(QJsonDocument::fromJson(jsonData));
+ const QJsonObject docJsonObject = doc.object();
+
+ const QJsonArray aliases = docJsonObject.value(QLatin1String("alias-packages")).toArray();
+ for (auto &it : aliases) {
+ AliasData data;
+ data.insert(QLatin1String("source"), QVariant::fromValue(source));
+
+ QJsonObject aliasObj = it.toObject();
+ for (const auto &key : aliasObj.keys()) {
+ if (!scPossibleElements.contains(key)) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << key;
+ continue;
+ }
+
+ const QJsonValue jsonValue = aliasObj.value(key);
+ if (key == scRequiredComponents || key == scRequiredAliases
+ || key == scOptionalComponents || key == scOptionalAliases) {
+ const QJsonArray requirements = jsonValue.toArray();
+ QString requiresString;
+
+ for (const auto &it2 : requirements) {
+ requiresString.append(it2.toString());
+ if (it2 != requirements.last())
+ requiresString.append(QLatin1Char(','));
+ }
+
+ data.insert(key, requiresString);
+ } else if (key == scVirtual) {
+ data.insert(key, QVariant(jsonValue.toBool()))->toString();
+ } else {
+ data.insert(key, jsonValue.toString());
+ }
+ }
+
+ m_aliasData.insert(data.value(scName).toString(), data);
+ }
+
+ return true;
+}
+
+/*!
+ Checks whether \a data should be used for creating a new alias object,
+ based on version and source priority.
+
+ If an alias of the same name exists, always use the one with the higher
+ version. If the new alias has the same version but a higher
+ priority, use the new new alias. Otherwise keep the already existing alias.
+
+ Returns the resolution of the check.
+*/
+AliasFinder::Resolution AliasFinder::checkPriorityAndVersion(const AliasData &data) const
+{
+ for (const auto &existingData : m_aliasData.values(data.value(scName).toString())) {
+ if (existingData == data)
+ continue;
+
+ const int versionMatch = KDUpdater::compareVersion(data.value(scVersion).toString(),
+ existingData.value(scVersion).toString());
+
+ const AliasSource newSource = data.value(QLatin1String("source")).value<AliasSource>();
+ const AliasSource oldSource = existingData.value(QLatin1String("source")).value<AliasSource>();
+
+ if (versionMatch > 0) {
+ // new alias has higher version, use
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: "
+ << data.value(scName).toString() << ", Version: " << existingData.value(scVersion).toString()
+ << ", Source: " << oldSource.filename
+ << "' found an alias with higher version 'Name: "
+ << data.value(scName).toString() << ", Version: " << data.value(scVersion).toString()
+ << ", Source: " << newSource.filename << "'";
+
+ return Resolution::RemoveExisting;
+ }
+
+ if ((versionMatch == 0) && (newSource.priority > oldSource.priority)) {
+ // new alias version equals but priority is higher, use
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: "
+ << data.value(scName).toString() << ", Priority: " << oldSource.priority
+ << ", Source: " << oldSource.filename
+ << "' found an alias with higher priority 'Name: "
+ << data.value(scName).toString() << ", Priority: " << newSource.priority
+ << ", Source: " << newSource.filename << "'";
+
+ return Resolution::RemoveExisting;
+ }
+
+ return Resolution::KeepExisting; // otherwise keep existing
+ }
+
+ return Resolution::AddNew;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ComponentAlias
+ \brief The ComponentAlias class represents an alias for single or multiple components.
+*/
+
+/*!
+ \enum QInstaller::ComponentAlias::UnstableError
+
+ This enum type holds the possible reasons for marking an alias unstable:
+
+ \value ReferenceToUnstable
+ Alias requires another alias that is marked unstable.
+ \value MissingComponent
+ Alias requires a component that is missing.
+ \value UnselectableComponent
+ Alias requires a component that cannot be selected.
+ \value MissingAlias
+ Alias requires another alias that is missing.
+ \value ComponentNameConfict
+ Alias has a name that conflicts with a name of a component
+*/
+
+/*!
+ Constructs a new component alias with \a core as the package manager instance.
+*/
+ComponentAlias::ComponentAlias(PackageManagerCore *core)
+ : m_core(core)
+ , m_selected(false)
+ , m_unstable(false)
+{
+}
+
+/*!
+ Destructs the alias.
+*/
+ComponentAlias::~ComponentAlias()
+{
+}
+
+/*!
+ Returns the name of the alias.
+*/
+QString ComponentAlias::name() const
+{
+ return m_variables.value(scName);
+}
+
+/*!
+ Returns the display name of the alias.
+*/
+QString ComponentAlias::displayName() const
+{
+ return m_variables.value(scDisplayName);
+}
+
+/*!
+ Returns the description text of the alias.
+*/
+QString ComponentAlias::description() const
+{
+ return m_variables.value(scDescription);
+}
+
+/*!
+ Returns the version of the alias.
+*/
+QString ComponentAlias::version() const
+{
+ return m_variables.value(scVersion);
+}
+
+/*!
+ Returns \c true if the alias is virtual, \c false otherwise.
+
+ Virtual aliases are aliases that cannot be selected by the
+ user, and are invisible. They can be required by other aliases however.
+*/
+bool ComponentAlias::isVirtual() const
+{
+ return m_variables.value(scVirtual, scFalse).toLower() == scTrue;
+}
+
+/*!
+ Returns \c true if the alias is selected for installation, \c false otherwise.
+*/
+bool ComponentAlias::isSelected() const
+{
+ return m_selected;
+}
+
+/*!
+ Sets the selection state of the alias to \a selected. The selection
+ does not have an effect if the alias is unselectable.
+*/
+void ComponentAlias::setSelected(bool selected)
+{
+ if (selected && (isUnstable() || isVirtual()))
+ return;
+
+ m_selected = selected;
+}
+
+/*!
+ Returns the list of components required by this alias, or an
+ empty list if this alias does not require any components.
+*/
+QList<Component *> ComponentAlias::components()
+{
+ if (m_components.isEmpty()) {
+ const QStringList componentList = QInstaller::splitStringWithComma(
+ m_variables.value(scRequiredComponents));
+
+ const QStringList optionalComponentList = QInstaller::splitStringWithComma(
+ m_variables.value(scOptionalComponents));
+
+ addRequiredComponents(componentList, false);
+ addRequiredComponents(optionalComponentList, true);
+ }
+
+ return m_components;
+}
+
+/*!
+ Returns the list of other aliases required by this alias, or an
+ empty list if this alias does not require any other aliases.
+*/
+QList<ComponentAlias *> ComponentAlias::aliases()
+{
+ if (m_aliases.isEmpty()) {
+ const QStringList aliasList = QInstaller::splitStringWithComma(
+ m_variables.value(scRequiredAliases));
+
+ const QStringList optionalAliasList = QInstaller::splitStringWithComma(
+ m_variables.value(scOptionalAliases));
+
+ addRequiredAliases(aliasList, false);
+ addRequiredAliases(optionalAliasList, true);
+ }
+
+ return m_aliases;
+}
+
+/*!
+ Returns the value specified by \a key, with an optional default value \a defaultValue.
+*/
+QString ComponentAlias::value(const QString &key, const QString &defaultValue) const
+{
+ return m_variables.value(key, defaultValue);
+}
+
+/*!
+ Sets the value specified by \a key to \a value. If the value exists already,
+ it is replaced with the new value.
+*/
+void ComponentAlias::setValue(const QString &key, const QString &value)
+{
+ const QString normalizedValue = m_core->replaceVariables(value);
+ if (m_variables.value(key) == normalizedValue)
+ return;
+
+ m_variables[key] = normalizedValue;
+}
+
+/*!
+ Returns all keys for the component alias values.
+*/
+QStringList ComponentAlias::keys() const
+{
+ return m_variables.keys();
+}
+
+/*!
+ Returns \c true if the alias is marked unstable, \c false otherwise.
+*/
+bool ComponentAlias::isUnstable() const
+{
+ return m_unstable;
+}
+
+/*!
+ Sets the alias unstable with \a error, and a \a message describing the error.
+*/
+void ComponentAlias::setUnstable(UnstableError error, const QString &message)
+{
+ setSelected(false);
+ m_unstable = true;
+
+ const QMetaEnum metaEnum = QMetaEnum::fromType<ComponentAlias::UnstableError>();
+ emit m_core->unstableComponentFound(
+ QLatin1String(metaEnum.valueToKey(error)), message, name());
+}
+
+/*!
+ \internal
+
+ Adds the \a aliases to the list of required aliases by this alias. If \a optional
+ is \c true, missing alias references are ignored.
+*/
+void ComponentAlias::addRequiredAliases(const QStringList &aliases, const bool optional)
+{
+ for (const auto &aliasName : aliases) {
+ ComponentAlias *alias = m_core->aliasByName(aliasName);
+ if (!alias) {
+ if (optional)
+ continue;
+
+ const QString error = QLatin1String("No required alias found by name: ") + aliasName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::MissingAlias, error);
+ continue;
+ }
+
+ if (alias->isUnstable()) {
+ const QString error = QLatin1String("Alias requires another alias "
+ "that is marked unstable: ") + aliasName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::ReferenceToUnstable, error);
+ continue;
+ }
+
+ m_aliases.append(alias);
+ }
+}
+
+/*!
+ \internal
+
+ Adds the \a components to the list of required components by this alias. If \a optional
+ is \c true, missing component references are ignored.
+*/
+void ComponentAlias::addRequiredComponents(const QStringList &components, const bool optional)
+{
+ for (const auto &componentName : components) {
+ Component *component = m_core->componentByName(componentName);
+ if (!component) {
+ if (optional)
+ continue;
+
+ const QString error = QLatin1String("No required component found by name: ")
+ + componentName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::MissingComponent, error);
+ continue;
+ }
+
+ if (component->isUnstable() || !component->isCheckable()) {
+ const QString error = QLatin1String("Alias requires component that is uncheckable or unstable: ")
+ + componentName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::UnselectableComponent, error);
+ continue;
+ }
+
+ m_components.append(component);
+ }
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/componentalias.h b/src/libs/installer/componentalias.h
new file mode 100644
index 000000000..e99c343d9
--- /dev/null
+++ b/src/libs/installer/componentalias.h
@@ -0,0 +1,160 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMPONENTALIAS_H
+#define COMPONENTALIAS_H
+
+#include "installer_global.h"
+
+#include <QHash>
+#include <QMetaEnum>
+#include <QString>
+#include <QSet>
+
+namespace QInstaller {
+
+class Component;
+class ComponentAlias;
+class PackageManagerCore;
+
+struct INSTALLER_EXPORT AliasSource
+{
+ enum class SourceFileFormat {
+ Unknown = -1,
+ Xml = 0,
+ Json
+ };
+
+ AliasSource();
+ AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority);
+ AliasSource(const AliasSource &other);
+
+ SourceFileFormat format;
+ QString filename;
+ int priority;
+};
+
+INSTALLER_EXPORT hashValue qHash(const AliasSource &key, hashValue seed);
+INSTALLER_EXPORT bool operator==(const AliasSource &lhs, const AliasSource &rhs);
+
+class INSTALLER_EXPORT AliasFinder
+{
+public:
+ using AliasData = QVariantMap;
+ using AliasDataHash = QMultiHash<QString, AliasData>;
+
+ enum struct Resolution {
+ AddNew,
+ KeepExisting,
+ RemoveExisting
+ };
+
+ explicit AliasFinder(PackageManagerCore *core);
+ ~AliasFinder();
+
+ bool run();
+ QList<ComponentAlias *> aliases() const;
+
+ void setAliasSources(const QSet<AliasSource> &sources);
+
+private:
+ void clear();
+ Resolution checkPriorityAndVersion(const AliasData &data) const;
+
+ bool parseXml(AliasSource source);
+ bool parseJson(AliasSource source);
+
+private:
+ PackageManagerCore *const m_core;
+
+ QSet<AliasSource> m_sources;
+ AliasDataHash m_aliasData;
+ QHash<QString, ComponentAlias *> m_aliases;
+};
+
+class INSTALLER_EXPORT ComponentAlias : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(ComponentAlias)
+
+public:
+ enum UnstableError {
+ ReferenceToUnstable = 0,
+ MissingComponent,
+ UnselectableComponent,
+ MissingAlias,
+ ComponentNameConfict
+ };
+ Q_ENUM(UnstableError)
+
+ ComponentAlias(PackageManagerCore *core);
+ ~ComponentAlias();
+
+ QString name() const;
+ QString displayName() const;
+ QString description() const;
+
+ QString version() const;
+
+ bool isVirtual() const;
+
+ bool isSelected() const;
+ void setSelected(bool selected);
+
+ QList<Component *> components();
+ QList<ComponentAlias *> aliases();
+
+ QString value(const QString &key, const QString &defaultValue = QString()) const;
+ void setValue(const QString &key, const QString &value);
+ QStringList keys() const;
+
+ bool isUnstable() const;
+ void setUnstable(UnstableError error, const QString &message = QString());
+
+private:
+ void addRequiredAliases(const QStringList &aliases, const bool optional);
+ void addRequiredComponents(const QStringList &components, const bool optional);
+
+private:
+ PackageManagerCore *const m_core;
+
+ QHash<QString, QString> m_variables;
+
+ bool m_selected;
+ bool m_unstable;
+
+ QList<Component *> m_components;
+ QList<ComponentAlias *> m_aliases;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::ComponentAlias *)
+Q_DECLARE_METATYPE(QInstaller::AliasSource);
+
+#endif // COMPONENTALIAS_H
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index e60ba92ae..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.
@@ -47,6 +47,8 @@ namespace QInstaller {
This enum value holds the checked state of the components available for
installation.
+ \value Empty
+ The model does not contain any components.
\value AllChecked
All components are checked.
\value AllUnchecked
@@ -58,14 +60,7 @@ namespace QInstaller {
*/
/*!
- \fn void QInstaller::ComponentModel::componentsCheckStateChanged(const QList<QModelIndex> &indexes)
-
- This signal is emitted whenever the checked state of components are changed. The \a indexes value
- indicates the QModelIndexes representation of the components as seen from the model.
-*/
-
-/*!
- \fn void QInstaller::ComponentModel::modelCheckStateChanged(QInstaller::ComponentModel::ModelState state)
+ \fn void QInstaller::ComponentModel::checkStateChanged(QInstaller::ComponentModel::ModelState state)
This signal is emitted whenever the checked state of a model is changed after all state
calculations have taken place. The \a state is a combination of \c ModelStateFlag values
@@ -221,7 +216,10 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
return component->data(Qt::UserRole + index.column());
}
if (role == Qt::CheckStateRole) {
- if (!component->isCheckable() || !component->autoDependencies().isEmpty() || component->isUnstable())
+ if (!component->isCheckable() || component->isUnstable())
+ return QVariant();
+
+ if (!m_core->isUpdater() && !component->autoDependencies().isEmpty())
return QVariant();
}
if (role == ComponentModelHelper::ExpandedByDefault) {
@@ -238,7 +236,6 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
/*!
Sets the \a role data for the item at \a index to \a value. Returns true if successful;
otherwise returns false. The dataChanged() signal is emitted if the data was successfully set.
- The componentsCheckStateChanged() signal is emitted in addition if the checked state of the item is set.
*/
bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
@@ -260,13 +257,10 @@ bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, in
const Qt::CheckState oldValue = component->checkState();
newValue = (oldValue == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
}
- const QList<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
- foreach (const QModelIndex &changedIndex, changed) {
+ const QSet<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
+ foreach (const QModelIndex &changedIndex, changed)
emit dataChanged(changedIndex, changedIndex);
- }
- updateModelState();
- if (changed.count() > 0)
- emit componentsCheckStateChanged(changed);
+ updateAndEmitModelState(); // update the internal state
} else {
component->setData(value, role);
emit dataChanged(index, index);
@@ -341,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.
*/
@@ -400,7 +410,7 @@ void ComponentModel::reset(QList<Component *> rootComponents)
m_uncheckable.clear();
m_indexByNameCache.clear();
m_rootComponentList.clear();
- m_modelState = DefaultChecked;
+ m_modelState = !rootComponents.isEmpty() ? DefaultChecked : Empty;
// Initialize these with an empty set for every possible state, cause we compare the hashes later in
// updateAndEmitModelState(). The comparison than might lead to wrong results if one of the checked
@@ -446,8 +456,7 @@ void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag
default:
break;
}
- updateModelState();
- emit modelCheckStateChanged(m_modelState);
+ updateAndEmitModelState(); // update the internal state
}
@@ -485,12 +494,15 @@ void ComponentModel::postModelReset()
}
m_currentCheckedState = m_initialCheckedState;
- updateModelState(); // update the internal state
- emit modelCheckStateChanged(m_modelState);
+ updateAndEmitModelState(); // update the internal state
}
-void ComponentModel::updateModelState()
+void ComponentModel::updateAndEmitModelState()
{
+ if (m_rootComponentList.isEmpty()) {
+ m_modelState = ComponentModel::Empty;
+ return;
+ }
m_modelState = ComponentModel::DefaultChecked;
if (m_initialCheckedState != m_currentCheckedState)
m_modelState = ComponentModel::PartiallyChecked;
@@ -504,6 +516,8 @@ void ComponentModel::updateModelState()
m_modelState |= ComponentModel::AllChecked;
m_modelState &= ~ComponentModel::PartiallyChecked;
}
+
+ emit checkStateChanged(m_modelState);
}
void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const
@@ -547,7 +561,7 @@ static Qt::CheckState verifyPartiallyChecked(Component *component)
} // namespace ComponentModelPrivate
-QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, const Qt::CheckState state)
+QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, const Qt::CheckState state)
{
// get all parent nodes for the components we're going to update
QMultiMap<QString, Component *> sortedNodesMap;
@@ -558,20 +572,22 @@ QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compon
}
}
- QList<QModelIndex> changed;
+ QSet<QModelIndex> changed;
const ComponentList sortedNodes = sortedNodesMap.values();
// we can start in descending order to check node and tri-state nodes properly
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->autoDependencies().isEmpty() || 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())
+ continue;
+
Qt::CheckState newState = state;
const Qt::CheckState recentState = node->checkState();
if (node->isTristate())
@@ -580,10 +596,7 @@ QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compon
continue;
node->setCheckState(newState);
- QModelIndex index = indexFromComponentName(node->treeName());
- //Prepend to the list so that install order is correct (parent first)
- if (!changed.contains(index))
- changed.prepend(indexFromComponentName(node->treeName()));
+ changed.insert(indexFromComponentName(node->treeName()));
m_currentCheckedState[Qt::Checked].remove(node);
m_currentCheckedState[Qt::Unchecked].remove(node);
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index f9fbae47a..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.
@@ -31,6 +31,8 @@
#include "qinstallerglobal.h"
+#include "component.h"
+
#include <QtCore/QAbstractItemModel>
#include <QtCore/QList>
#include <QtCore/QSet>
@@ -38,7 +40,6 @@
namespace QInstaller {
-class Component;
class PackageManagerCore;
class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
@@ -49,6 +50,7 @@ class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
public:
enum ModelStateFlag {
+ Empty = -0x01,
AllChecked = 0x01,
AllUnchecked = 0x02,
DefaultChecked = 0x04,
@@ -78,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;
@@ -90,17 +93,16 @@ public Q_SLOTS:
void setCheckedState(QInstaller::ComponentModel::ModelStateFlag state);
Q_SIGNALS:
- void componentsCheckStateChanged(const QList<QModelIndex> &indexes);
- void modelCheckStateChanged(QInstaller::ComponentModel::ModelState state);
+ void checkStateChanged(QInstaller::ComponentModel::ModelState state);
private Q_SLOTS:
void onVirtualStateChanged();
private:
void postModelReset();
- void updateModelState();
+ void updateAndEmitModelState();
void collectComponents(Component *const component, const QModelIndex &parent) const;
- QList<QModelIndex> updateCheckedState(const ComponentSet &components, const Qt::CheckState state);
+ QSet<QModelIndex> updateCheckedState(const ComponentSet &components, const Qt::CheckState state);
private:
PackageManagerCore *m_core;
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index 5e44d153c..b68eebf06 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.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.
@@ -28,12 +28,14 @@
#include "componentselectionpage_p.h"
+#include "globals.h"
#include "packagemanagergui.h"
#include "componentmodel.h"
#include "settings.h"
#include "component.h"
#include "fileutils.h"
#include "messageboxhandler.h"
+#include "customcombobox.h"
#include <QTreeView>
#include <QLabel>
@@ -44,13 +46,13 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QCheckBox>
-#include <QHeaderView>
#include <QStandardPaths>
#include <QFileDialog>
#include <QStackedLayout>
#include <QStackedWidget>
-#include <QToolBox>
#include <QLineEdit>
+#include <QStandardItemModel>
+#include <QStyledItemDelegate>
namespace QInstaller {
@@ -60,23 +62,29 @@ namespace QInstaller {
\internal
*/
+constexpr int scNoCheckSelectionIndex = -1;
+constexpr int scCheckDefaultIndex = 0;
+constexpr int scCheckAllIndex = 1;
+constexpr int scUncheckAllIndex = 2;
+
ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core)
: q(qq)
, m_core(core)
, m_treeView(new QTreeView(q))
- , m_allModel(m_core->defaultComponentModel())
- , m_updaterModel(m_core->updaterComponentModel())
- , m_currentModel(m_allModel)
- , m_allowCompressedRepositoryInstall(false)
- , m_toolBox(nullptr)
+ , m_tabWidget(nullptr)
, m_descriptionBaseWidget(nullptr)
, m_categoryWidget(Q_NULLPTR)
+ , m_allowCreateOfflineInstaller(false)
, m_categoryLayoutVisible(false)
- , m_proxyModel(new ComponentSortFilterProxyModel(q))
+ , m_allModel(m_core->defaultComponentModel())
+ , m_updaterModel(m_core->updaterComponentModel())
+ , m_currentModel(m_allModel)
+ , m_proxyModel(m_core->componentSortFilterProxyModel())
+ , m_componentsResolved(false)
+ , m_headerStretchLastSection(false)
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
- m_proxyModel->setRecursiveFilteringEnabled(true);
- m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_treeView->setUniformRowHeights(true);
m_descriptionBaseWidget = new QWidget(q);
m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
@@ -85,8 +93,12 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
descriptionVLayout->setContentsMargins(0, 0, 0, 0);
- m_toolBox = new QToolBox(q);
- m_toolBox->setObjectName(QLatin1String("ToolBox"));
+ m_tabWidget = new QTabWidget(q);
+ m_tabWidget->setObjectName(QLatin1String("ComponentSelectionTabWidget"));
+ m_tabWidget->tabBar()->setObjectName(QLatin1String("ComponentSelectionTabBar"));
+ m_tabWidget->hide();
+
+ m_rightSideVLayout = new QVBoxLayout;
QScrollArea *descriptionScrollArea = new QScrollArea(q);
descriptionScrollArea->setWidgetResizable(true);
@@ -107,46 +119,67 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
descriptionVLayout->addWidget(m_sizeLabel);
- QHBoxLayout *buttonHLayout = new QHBoxLayout;
- m_checkDefault = new QPushButton;
- connect(m_checkDefault, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectDefault);
+ m_createOfflinePushButton = new QPushButton(q);
+ m_createOfflinePushButton->setVisible(false);
+ m_createOfflinePushButton->setText(ComponentSelectionPage::tr("Create Offline Installer"));
+ m_createOfflinePushButton->setToolTip(
+ ComponentSelectionPage::tr("Create offline installer from selected components, instead "
+ "of installing now."));
+
+ connect(m_createOfflinePushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::createOfflineButtonClicked);
+ connect(q, &ComponentSelectionPage::completeChanged,
+ this, [&]() { m_createOfflinePushButton->setEnabled(q->isComplete()); });
+
+ m_qbspPushButton = new QPushButton(q);
+ m_qbspPushButton->setVisible(false);
+ m_qbspPushButton->setText(ComponentSelectionPage::tr("Browse &QBSP files"));
+ m_qbspPushButton->setToolTip(
+ ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
+ "additional content that is not directly available from the online repositories."));
+
+ connect(m_qbspPushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::qbspButtonClicked);
+
+ m_rightSideVLayout->addWidget(m_descriptionBaseWidget);
+ m_rightSideVLayout->addWidget(m_createOfflinePushButton);
+ m_rightSideVLayout->addWidget(m_qbspPushButton);
+
+ QHBoxLayout *topHLayout = new QHBoxLayout;
+
+ // Using custom combobox to workaround QTBUG-90595
+ m_checkStateComboBox = new CustomComboBox(q);
+#ifdef Q_OS_MACOS
+ QStyledItemDelegate *delegate = new QStyledItemDelegate(this);
+ m_checkStateComboBox->setItemDelegate(delegate);
+#endif
+ m_checkStateComboBox->setObjectName(QLatin1String("CheckStateComboBox"));
+ topHLayout->addWidget(m_checkStateComboBox);
+
+ connect(m_checkStateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &ComponentSelectionPagePrivate::updateAllCheckStates);
+
+ // Workaround invisible placeholder text
+ QPalette palette = m_checkStateComboBox->palette();
+ palette.setColor(QPalette::PlaceholderText, palette.color(QPalette::Text));
+ m_checkStateComboBox->setPalette(palette);
+
+ m_checkStateComboBox->setPlaceholderText(ComponentSelectionPage::tr("Select"));
if (m_core->isInstaller()) {
- m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A",
- "Select default components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("Def&ault"));
- m_checkDefault->setToolTip(ComponentSelectionPage::tr("Select default components in the tree view."));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Default"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Select default components in the tree view."), Qt::ToolTipRole);
} else {
- m_checkDefault->setEnabled(false);
- m_checkDefault->setObjectName(QLatin1String("ResetComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+R",
- "Reset to already installed components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("&Reset"));
- m_checkDefault->setToolTip(
- ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Reset"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."), Qt::ToolTipRole);
}
- buttonHLayout->addWidget(m_checkDefault);
-
- m_checkAll = new QPushButton;
- connect(m_checkAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectAll);
- m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton"));
- m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S",
- "Select all components")));
- m_checkAll->setText(ComponentSelectionPage::tr("&Select All"));
- m_checkAll->setToolTip(ComponentSelectionPage::tr("Select all components in the tree view."));
- buttonHLayout->addWidget(m_checkAll);
-
- m_uncheckAll = new QPushButton;
- connect(m_uncheckAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::deselectAll);
- m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton"));
- m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D",
- "Deselect all components")));
- m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All"));
- m_uncheckAll->setToolTip(ComponentSelectionPage::tr("Deselect all components in the tree view."));
- buttonHLayout->addWidget(m_uncheckAll);
+ m_checkStateComboBox->insertItem(scCheckAllIndex, ComponentSelectionPage::tr("Select All"));
+ m_checkStateComboBox->setItemData(scCheckAllIndex,
+ ComponentSelectionPage::tr("Select all components in the tree view."), Qt::ToolTipRole);
+ m_checkStateComboBox->insertItem(scUncheckAllIndex, ComponentSelectionPage::tr("Deselect All"));
+ m_checkStateComboBox->setItemData(scUncheckAllIndex,
+ ComponentSelectionPage::tr("Deselect all components in the tree view."), Qt::ToolTipRole);
QWidget *progressStackedWidget = new QWidget();
QVBoxLayout *metaLayout = new QVBoxLayout(progressStackedWidget);
@@ -166,17 +199,24 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
connect(m_searchLineEdit, &QLineEdit::textChanged,
this, &ComponentSelectionPagePrivate::setSearchPattern);
connect(q, &ComponentSelectionPage::entered, m_searchLineEdit, &QLineEdit::clear);
+ topHLayout->addWidget(m_searchLineEdit);
QVBoxLayout *treeViewVLayout = new QVBoxLayout;
treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
treeViewVLayout->addWidget(m_treeView, 3);
- treeViewVLayout->addWidget(m_searchLineEdit);
QWidget *mainStackedWidget = new QWidget();
m_mainGLayout = new QGridLayout(mainStackedWidget);
- m_mainGLayout->addLayout(buttonHLayout, 0, 0);
+ {
+ int left = 0;
+ int top = 0;
+ int bottom = 0;
+ m_mainGLayout->getContentsMargins(&left, &top, nullptr, &bottom);
+ m_mainGLayout->setContentsMargins(left, top, 0, bottom);
+ }
+ m_mainGLayout->addLayout(topHLayout, 0, 0);
m_mainGLayout->addLayout(treeViewVLayout, 1, 0);
- m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_mainGLayout->addLayout(m_rightSideVLayout, 0, 1, 0, -1);
m_mainGLayout->setColumnStretch(0, 3);
m_mainGLayout->setColumnStretch(1, 2);
@@ -185,25 +225,15 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
m_stackedLayout->addWidget(progressStackedWidget);
m_stackedLayout->setCurrentIndex(0);
- connect(m_allModel, &ComponentModel::modelCheckStateChanged,
- this, &ComponentSelectionPagePrivate::onModelStateChanged);
- connect(m_updaterModel, &ComponentModel::modelCheckStateChanged,
- this, &ComponentSelectionPagePrivate::onModelStateChanged);
-
- connect(m_allModel, &ComponentModel::componentsCheckStateChanged, this,
- [=]() { onModelStateChanged(m_allModel->checkedState());});
-
- connect(m_updaterModel, &ComponentModel::componentsCheckStateChanged, this,
- [=]() { onModelStateChanged(m_allModel->checkedState());});
+ connect(m_allModel, &ComponentModel::checkStateChanged,
+ this, &ComponentSelectionPagePrivate::onModelStateChanged);
+ connect(m_updaterModel, &ComponentModel::checkStateChanged,
+ this, &ComponentSelectionPagePrivate::onModelStateChanged);
connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int)));
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
connect(m_core, &PackageManagerCore::metaJobTotalProgress, this,
&ComponentSelectionPagePrivate::setTotalProgress);
-
-#ifdef INSTALLCOMPRESSED
- allowCompressedRepositoryInstall();
-#endif
}
ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
@@ -211,36 +241,28 @@ ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
}
-void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall()
+void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow)
{
- m_allowCompressedRepositoryInstall = true;
+ m_allowCreateOfflineInstaller = allow;
}
void ComponentSelectionPagePrivate::showCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && !(wizard->options() & QWizard::HaveCustomButton2) && m_allowCompressedRepositoryInstall) {
- wizard->setOption(QWizard::HaveCustomButton2, true);
- wizard->setButtonText(QWizard::CustomButton2,
- ComponentSelectionPage::tr("&Browse QBSP files"));
- wizard->button(QWizard::CustomButton2)->setToolTip(
- ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
- "additional content that is not directly available from the online repositories."));
- connect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ if (m_core->allowCompressedRepositoryInstall())
+ m_qbspPushButton->setVisible(true);
}
void ComponentSelectionPagePrivate::hideCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && (wizard->options() & QWizard::HaveCustomButton2)) {
- wizard->setOption(QWizard::HaveCustomButton2, false);
- disconnect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ m_qbspPushButton->setVisible(false);
+}
+
+void ComponentSelectionPagePrivate::showCreateOfflineInstallerButton(bool show)
+{
+ if (show && m_allowCreateOfflineInstaller)
+ m_createOfflinePushButton->setVisible(m_core->isInstaller() && !m_core->isOfflineOnly());
+ else
+ m_createOfflinePushButton->setVisible(false);
}
void ComponentSelectionPagePrivate::setupCategoryLayout()
@@ -274,7 +296,7 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
vLayout->addWidget(m_categoryGroupBox);
vLayout->addStretch();
- m_toolBox->insertItem(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
+ m_tabWidget->insertTab(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
}
void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
@@ -287,27 +309,27 @@ void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
setupCategoryLayout();
if (show) {
- m_mainGLayout->removeWidget(m_descriptionBaseWidget);
- m_toolBox->insertItem(0, m_descriptionBaseWidget, tr("Component Information"));
- m_mainGLayout->addWidget(m_toolBox, 1, 1);
+ m_rightSideVLayout->removeWidget(m_descriptionBaseWidget);
+ m_tabWidget->insertTab(0, m_descriptionBaseWidget, tr("Information"));
+ m_rightSideVLayout->insertWidget(0, m_tabWidget);
} else {
- m_toolBox->removeItem(0);
- m_mainGLayout->removeWidget(m_toolBox);
- m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_tabWidget->removeTab(0);
+ m_rightSideVLayout->removeWidget(m_tabWidget);
+ m_rightSideVLayout->insertWidget(0, m_descriptionBaseWidget);
+ m_descriptionBaseWidget->setVisible(true);
}
- m_toolBox->setVisible(show);
+ m_tabWidget->setVisible(show);
m_categoryLayoutVisible = show;
}
void ComponentSelectionPagePrivate::updateTreeView()
{
- m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager());
+ setComboBoxItemEnabled(scCheckDefaultIndex, m_core->isInstaller() || m_core->isPackageManager());
if (m_treeView->selectionModel()) {
disconnect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
}
- m_searchLineEdit->setVisible(!m_core->isUpdater());
m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel;
m_proxyModel->setSourceModel(m_currentModel);
m_treeView->setModel(m_proxyModel);
@@ -375,6 +397,9 @@ void ComponentSelectionPagePrivate::expandDefault()
*/
void ComponentSelectionPagePrivate::expandSearchResults()
{
+ // Avoid resizing the sections after each expand of a node
+ storeHeaderResizeModes();
+
// Expand parents of root indexes accepted by filter
const QVector<QModelIndex> acceptedIndexes = m_proxyModel->directlyAcceptedIndexes();
for (auto proxyModelIndex : acceptedIndexes) {
@@ -390,6 +415,16 @@ void ComponentSelectionPagePrivate::expandSearchResults()
index = index.parent();
}
}
+ restoreHeaderResizeModes();
+}
+
+/*!
+ Returns \c true if the components to install and uninstall are calculated
+ successfully, \c false otherwise.
+*/
+bool ComponentSelectionPagePrivate::componentsResolved() const
+{
+ return m_componentsResolved;
}
void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &current)
@@ -415,6 +450,32 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu
}
}
+/*!
+ Updates the checkstate of the components based on the value of \c which.
+*/
+void ComponentSelectionPagePrivate::updateAllCheckStates(int which)
+{
+ switch (which) {
+ case scNoCheckSelectionIndex:
+ // A 'helper' text index, no selection
+ return;
+ case scCheckDefaultIndex:
+ selectDefault();
+ break;
+ case scCheckAllIndex:
+ selectAll();
+ break;
+ case scUncheckAllIndex:
+ deselectAll();
+ break;
+ default:
+ qCWarning(lcInstallerInstallLog) << "Invalid index for check state selection!";
+ break;
+ }
+ // Reset back to 'helper' text index
+ m_checkStateComboBox->setCurrentIndex(scNoCheckSelectionIndex);
+}
+
void ComponentSelectionPagePrivate::selectAll()
{
m_currentModel->setCheckedState(ComponentModel::AllChecked);
@@ -425,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)
@@ -453,8 +493,12 @@ void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
else
m_stackedLayout->setCurrentIndex(0);
- if (QAbstractButton *bspButton = q->gui()->button(QWizard::CustomButton2))
- bspButton->setEnabled(!show);
+ m_qbspPushButton->setEnabled(!show);
+
+ if (show) {
+ q->gui()->button(QWizard::NextButton)->setEnabled(false);
+ q->gui()->button(QWizard::BackButton)->setEnabled(false);
+ }
// In macOS 10.12 the widgets are not hidden if those are not updated immediately
#ifdef Q_OS_MACOS
@@ -469,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()) {
@@ -480,31 +524,28 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults();
}
-void ComponentSelectionPagePrivate::customButtonClicked(int which)
+void ComponentSelectionPagePrivate::createOfflineButtonClicked()
{
- if (QWizard::WizardButton(which) == QWizard::CustomButton2) {
- QString defaultDownloadDirectory =
- QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
- QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
- ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
- QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
-
- QSet<Repository> set;
- foreach (QString fileName, fileNames) {
- Repository repository = Repository::fromUserInput(fileName, true);
- repository.setEnabled(true);
- set.insert(repository);
- }
- if (set.count() > 0) {
- updateWidgetVisibility(true);
- m_core->settings().addTemporaryRepositories(set, false);
- if (!m_core->fetchCompressedPackagesTree()) {
- MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
- QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
- }
+ m_core->setOfflineGenerator();
+ q->gui()->button(QWizard::NextButton)->click();
+}
+
+void ComponentSelectionPagePrivate::qbspButtonClicked()
+{
+ QString defaultDownloadDirectory =
+ QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
+ ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
+ QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
+
+ if (m_core->addQBspRepositories(fileNames)) {
+ updateWidgetVisibility(true);
+ if (!m_core->fetchCompressedPackagesTree()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
}
- updateWidgetVisibility(false);
}
+ updateWidgetVisibility(false);
}
/*!
@@ -538,6 +579,21 @@ void ComponentSelectionPagePrivate::selectDefault()
void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentModel::ModelState state)
{
+ if (state.testFlag(ComponentModel::Empty)) {
+ setComboBoxItemEnabled(scCheckAllIndex, false);
+ setComboBoxItemEnabled(scUncheckAllIndex, false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, false);
+ return;
+ }
+
+ m_componentsResolved = m_core->recalculateAllComponents();
+ if (!m_componentsResolved) {
+ const QString error = !m_core->componentsToInstallError().isEmpty()
+ ? m_core->componentsToInstallError() : m_core->componentsToUninstallError();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("CalculateComponentsError"), tr("Error"), error);
+ }
+
q->setModified(state.testFlag(ComponentModel::DefaultChecked) == false);
// If all components in the checked list are only checkable when run without forced
// installation, set ComponentModel::AllUnchecked as well, as we cannot uncheck anything.
@@ -547,9 +603,9 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod
state |= ComponentModel::AllUnchecked;
}
// enable the button if the corresponding flag is not set
- m_checkAll->setEnabled(state.testFlag(ComponentModel::AllChecked) == false);
- m_uncheckAll->setEnabled(state.testFlag(ComponentModel::AllUnchecked) == false);
- m_checkDefault->setEnabled(state.testFlag(ComponentModel::DefaultChecked) == false);
+ setComboBoxItemEnabled(scCheckAllIndex, state.testFlag(ComponentModel::AllChecked) == false);
+ setComboBoxItemEnabled(scUncheckAllIndex, state.testFlag(ComponentModel::AllUnchecked) == false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, state.testFlag(ComponentModel::DefaultChecked) == false);
// update the current selected node (important to reflect possible sub-node changes)
if (m_treeView->selectionModel())
@@ -578,4 +634,45 @@ void ComponentSelectionPagePrivate::setSearchPattern(const QString &text)
}
}
+/*!
+ Stores the current resize modes of the tree view header's columns, and sets
+ the new resize modes to \c QHeaderView::Fixed.
+*/
+void ComponentSelectionPagePrivate::storeHeaderResizeModes()
+{
+ m_headerStretchLastSection = m_treeView->header()->stretchLastSection();
+ for (int i = 0; i < ComponentModelHelper::LastColumn; ++i)
+ m_headerResizeModes.insert(i, m_treeView->header()->sectionResizeMode(i));
+
+ m_treeView->header()->setStretchLastSection(false);
+ m_treeView->header()->setSectionResizeMode(QHeaderView::Fixed);
+}
+
+/*!
+ Restores the resize modes of the tree view header's columns, that were
+ stored when calling \l storeHeaderResizeModes().
+*/
+void ComponentSelectionPagePrivate::restoreHeaderResizeModes()
+{
+ m_treeView->header()->setStretchLastSection(m_headerStretchLastSection);
+ for (int i = 0; i < ComponentModelHelper::LastColumn; ++i)
+ m_treeView->header()->setSectionResizeMode(i, m_headerResizeModes.value(i));
+}
+
+/*!
+ Sets the enabled state of the combo box item in \a index to \a enabled.
+*/
+void ComponentSelectionPagePrivate::setComboBoxItemEnabled(int index, bool enabled)
+{
+ auto *model = qobject_cast<QStandardItemModel *>(m_checkStateComboBox->model());
+ if (!model)
+ return;
+
+ QStandardItem *item = model->item(index);
+ if (!item)
+ return;
+
+ item->setEnabled(enabled);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index fc37ebdaa..187fce61d 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -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.
@@ -31,6 +31,7 @@
#include <QObject>
#include <QWidget>
+#include <QHeaderView>
#include "componentmodel.h"
#include "packagemanagergui.h"
@@ -47,13 +48,13 @@ class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QStackedLayout;
-class QToolBox;
namespace QInstaller {
class PackageManagerCore;
class ComponentModel;
class ComponentSelectionPage;
+class CustomComboBox;
class ComponentSelectionPagePrivate : public QObject
{
@@ -65,23 +66,26 @@ public:
explicit ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core);
~ComponentSelectionPagePrivate();
- void allowCompressedRepositoryInstall();
+ void setAllowCreateOfflineInstaller(bool allow);
void showCompressedRepositoryButton();
void hideCompressedRepositoryButton();
+ void showCreateOfflineInstallerButton(bool show);
void setupCategoryLayout();
void showCategoryLayout(bool show);
void updateTreeView();
void expandDefault();
void expandSearchResults();
+ bool componentsResolved() const;
public slots:
void currentSelectedChanged(const QModelIndex &current);
+ void updateAllCheckStates(int which);
void selectAll();
void deselectAll();
- void enableRepositoryCategory(const QString &repositoryName, bool enable);
void updateWidgetVisibility(bool show);
void fetchRepositoryCategories();
- void customButtonClicked(int which);
+ void createOfflineButtonClicked();
+ void qbspButtonClicked();
void onProgressChanged(int progress);
void setMessage(const QString &msg);
void setTotalProgress(int totalProgress);
@@ -90,22 +94,28 @@ public slots:
void setSearchPattern(const QString &text);
private:
+ void storeHeaderResizeModes();
+ void restoreHeaderResizeModes();
+ void setComboBoxItemEnabled(int index, bool enabled);
+
+private:
ComponentSelectionPage *q;
PackageManagerCore *m_core;
QTreeView *m_treeView;
- QToolBox *m_toolBox;
+ QTabWidget *m_tabWidget;
QWidget *m_descriptionBaseWidget;
QLabel *m_sizeLabel;
QLabel *m_descriptionLabel;
- QPushButton *m_checkAll;
- QPushButton *m_uncheckAll;
- QPushButton *m_checkDefault;
+ QPushButton *m_createOfflinePushButton;
+ QPushButton *m_qbspPushButton;
+ CustomComboBox *m_checkStateComboBox;
QWidget *m_categoryWidget;
QGroupBox *m_categoryGroupBox;
QLabel *m_metadataProgressLabel;
QProgressBar *m_progressBar;
QGridLayout *m_mainGLayout;
- bool m_allowCompressedRepositoryInstall;
+ QVBoxLayout *m_rightSideVLayout;
+ bool m_allowCreateOfflineInstaller;
bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
ComponentModel *m_updaterModel;
@@ -113,6 +123,10 @@ private:
QStackedLayout *m_stackedLayout;
ComponentSortFilterProxyModel *m_proxyModel;
QLineEdit *m_searchLineEdit;
+ bool m_componentsResolved;
+
+ bool m_headerStretchLastSection;
+ QHash<int, QHeaderView::ResizeMode> m_headerResizeModes;
};
} // namespace QInstaller
diff --git a/src/libs/installer/concurrentoperationrunner.cpp b/src/libs/installer/concurrentoperationrunner.cpp
index dfcae44dd..b0eb5f582 100644
--- a/src/libs/installer/concurrentoperationrunner.cpp
+++ b/src/libs/installer/concurrentoperationrunner.cpp
@@ -156,7 +156,7 @@ QHash<Operation *, bool> ConcurrentOperationRunner::run()
this, &ConcurrentOperationRunner::onOperationfinished);
futureWatcher->setFuture(QtConcurrent::run(m_threadPool,
- this, &ConcurrentOperationRunner::runOperation, operation));
+ [this, operation] { return runOperation(operation); }));
}
if (!m_operationWatchers.isEmpty()) {
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 7fa54c4ec..7bf816b5f 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -41,6 +41,7 @@ static const QLatin1String scScript("script");
static const QLatin1String scAllUsersStartMenuProgramsPath("AllUsersStartMenuProgramsPath");
static const QLatin1String scUserStartMenuProgramsPath("UserStartMenuProgramsPath");
static const QLatin1String scUILanguage("UILanguage");
+static const QLatin1String scUpdatesXML("Updates.xml");
static const QLatin1String scName("Name");
static const QLatin1String scVersion("Version");
@@ -59,6 +60,11 @@ static const QLatin1String scDisplayName("DisplayName");
static const QLatin1String scTreeName("TreeName");
static const QLatin1String scAutoTreeName("AutoTreeName");
static const QLatin1String scDependencies("Dependencies");
+static const QLatin1String scAlias("Alias");
+static const QLatin1String scRequiredAliases("RequiredAliases");
+static const QLatin1String scRequiredComponents("RequiredComponents");
+static const QLatin1String scOptionalAliases("OptionalAliases");
+static const QLatin1String scOptionalComponents("OptionalComponents");
static const QLatin1String scLocalDependencies("LocalDependencies");
static const QLatin1String scAutoDependOn("AutoDependOn");
static const QLatin1String scNewComponent("NewComponent");
@@ -70,12 +76,68 @@ static const QLatin1String scUncompressedSizeSum("UncompressedSizeSum");
static const QLatin1String scRequiresAdminRights("RequiresAdminRights");
static const QLatin1String scOfflineBinaryName("OfflineBinaryName");
static const QLatin1String scSHA1("SHA1");
+static const QLatin1String scMetadataName("MetadataName");
static const QLatin1String scContentSha1("ContentSha1");
+static const QLatin1String scCheckSha1CheckSum("CheckSha1CheckSum");
+
+static const char * const scClearCacheHint = QT_TR_NOOP(
+ "This may be solved by restarting the application after clearing the cache from:");
+
+// symbols
+static const QLatin1String scCaretSymbol("^");
+static const QLatin1String scCommaWithSpace(", ");
+static const QLatin1String scBr("<br>");
// constants used throughout the components class
static const QLatin1String scVirtual("Virtual");
static const QLatin1String scSortingPriority("SortingPriority");
static const QLatin1String scCheckable("Checkable");
+static const QLatin1String scScriptTag("Script");
+static const QLatin1String scInstalled("Installed");
+static const QLatin1String scUpdateText("UpdateText");
+static const QLatin1String scUninstalled("Uninstalled");
+static const QLatin1String scCurrentState("CurrentState");
+static const QLatin1String scForcedInstallation("ForcedInstallation");
+static const QLatin1String scExpandedByDefault("ExpandedByDefault");
+static const QLatin1String scUnstable("Unstable");
+static const QLatin1String scTargetDirPlaceholder("@TargetDir@");
+static const QLatin1String scTargetDirPlaceholderWithArg("@TargetDir@%1");
+static const QLatin1String scLastUpdateDate("LastUpdateDate");
+static const QLatin1String scInstallDate("InstallDate");
+static const QLatin1String scUserInterfaces("UserInterfaces");
+static const QLatin1String scTranslations("Translations");
+static const QLatin1String scLicenses("Licenses");
+static const QLatin1String scLicensesValue("licenses");
+static const QLatin1String scLicense("License");
+static const QLatin1String scOperations("Operations");
+static const QLatin1String scInstallScript("installScript");
+static const QLatin1String scPostLoadScript("postLoadScript");
+static const QLatin1String scComponent("Component");
+static const QLatin1String scComponentSmall("component");
+static const QLatin1String scRetranslateUi("retranslateUi");
+static const QLatin1String scEn("en");
+static const QLatin1String scIfw_("ifw_");
+static const QLatin1String scFile("file");
+static const QLatin1String scContent("content");
+static const QLatin1String scExtract("Extract");
+static const QLatin1String scSha1("sha1");
+static const QLatin1String scCreateOperationsForPath("createOperationsForPath");
+static const QLatin1String scCreateOperationsForArchive("createOperationsForArchive");
+static const QLatin1String scCreateOperations("createOperations");
+static const QLatin1String scBeginInstallation("beginInstallation");
+static const QLatin1String scMinimumProgress("MinimumProgress");
+static const QLatin1String scDelete("Delete");
+static const QLatin1String scCopy("Copy");
+static const QLatin1String scMkdir("Mkdir");
+static const QLatin1String scIsDefault("isDefault");
+static const QLatin1String scAdmin("admin");
+static const QLatin1String scTwoArgs("%1/%2/");
+static const QLatin1String scThreeArgs("%1/%2/%3");
+static const QLatin1String scComponentScriptTest("var component = installer.componentByName('%1'); component.name;");
+static const QLatin1String scInstallerPrefix("installer://");
+static const QLatin1String scInstallerPrefixWithOneArgs("installer://%1/");
+static const QLatin1String scInstallerPrefixWithTwoArgs("installer://%1/%2");
+static const QLatin1String scLocalesArgs("%1%2_%3.%4");
// constants used throughout the settings and package manager core class
static const QLatin1String scTitle("Title");
@@ -84,6 +146,8 @@ static const QLatin1String scRunProgram("RunProgram");
static const QLatin1String scRunProgramArguments("RunProgramArguments");
static const QLatin1String scStartMenuDir("StartMenuDir");
static const QLatin1String scRemoveTargetDir("RemoveTargetDir");
+static const QLatin1String scLocalCacheDir("LocalCacheDir");
+static const QLatin1String scPersistentLocalCache("PersistentLocalCache");
static const QLatin1String scRunProgramDescription("RunProgramDescription");
static const QLatin1String scTargetConfigurationFile("TargetConfigurationFile");
static const QLatin1String scAllowNonAsciiCharacters("AllowNonAsciiCharacters");
@@ -93,6 +157,7 @@ static const QLatin1String scRemoteRepositories("RemoteRepositories");
static const QLatin1String scRepositoryCategories("RepositoryCategories");
static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible");
static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath");
+static const QLatin1String scAllowRepositoriesForOfflineInstaller("AllowRepositoriesForOfflineInstaller");
static const QLatin1String scWizardStyle("WizardStyle");
static const QLatin1String scStyleSheet("StyleSheet");
static const QLatin1String scTitleColor("TitleColor");
@@ -115,7 +180,15 @@ static const QLatin1String scBanner("Banner");
static const QLatin1String scLogo("Logo");
static const QLatin1String scBackground("Background");
static const QLatin1String scPageListPixmap("PageListPixmap");
+static const QLatin1String scAliasDefinitionsFile("AliasDefinitionsFile");
const char scRelocatable[] = "@RELOCATABLE_PATH@";
+
+static const QStringList scMetaElements = {
+ QLatin1String("Script"),
+ QLatin1String("Licenses"),
+ QLatin1String("UserInterfaces"),
+ QLatin1String("Translations")
+};
}
namespace CommandLineOptions {
@@ -147,6 +220,8 @@ static const QLatin1String scSearchShort("se");
static const QLatin1String scSearchLong("search");
static const QLatin1String scCreateOfflineShort("co");
static const QLatin1String scCreateOfflineLong("create-offline");
+static const QLatin1String scClearCacheShort("cc");
+static const QLatin1String scClearCacheLong("clear-cache");
static const QLatin1String scPurgeShort("pr");
static const QLatin1String scPurgeLong("purge");
@@ -208,6 +283,9 @@ static const QLatin1String scNoDefaultInstallationShort("nd");
static const QLatin1String scNoDefaultInstallationLong("no-default-installations");
static const QLatin1String scFilterPackagesShort("fp");
static const QLatin1String scFilterPackagesLong("filter-packages");
+static const QLatin1String scLocalCachePathShort("cp");
+static const QLatin1String scLocalCachePathLong("cache-path");
+static const QLatin1String scTypeLong("type");
// Developer options
static const QLatin1String scScriptShort("s");
@@ -220,6 +298,8 @@ static const QLatin1String scSquishPortShort("q");
static const QLatin1String scSquishPortLong("squish-port");
static const QLatin1String scMaxConcurrentOperationsShort("mco");
static const QLatin1String scMaxConcurrentOperationsLong("max-concurrent-operations");
+static const QLatin1String scCleanupUpdate("cleanup-update");
+static const QLatin1String scCleanupUpdateOnly("cleanup-update-only");
// Deprecated options, provided only for backward compatibility
static const QLatin1String scDeprecatedUpdater("updater");
@@ -242,7 +322,9 @@ static const QStringList scCommandLineInterfaceOptions = {
scCreateOfflineShort,
scCreateOfflineLong,
scPurgeShort,
- scPurgeLong
+ scPurgeLong,
+ scClearCacheShort,
+ scClearCacheLong
};
} // namespace CommandLineOptions
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/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp
index 72b28d896..856feda01 100644
--- a/src/libs/installer/copyfiletask.cpp
+++ b/src/libs/installer/copyfiletask.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -112,7 +112,7 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi)
observer.addSample(read);
observer.timerEvent(nullptr);
observer.addBytesTransfered(read);
- observer.addCheckSumData(buffer.data(), read);
+ observer.addCheckSumData(buffer.left(read));
fi.setProgressValueAndText(observer.progressValue(), observer.progressText());
}
diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp
index 6c92cc178..a19fd773a 100644
--- a/src/libs/installer/createdesktopentryoperation.cpp
+++ b/src/libs/installer/createdesktopentryoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "globals.h"
#include "adminauthorization.h"
#include "remoteclient.h"
+#include "packagemanagercore.h"
#include <QDir>
#include <QFile>
@@ -64,7 +65,7 @@ QString CreateDesktopEntryOperation::absoluteFileName()
XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path
- if (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive())
+ if (packageManager() && packageManager()->hasAdminRights())
XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path
const QStringList directories = XDG_DATA_HOME;
@@ -151,15 +152,16 @@ bool CreateDesktopEntryOperation::performOperation()
setDefaultFilePermissions(filename, DefaultFilePermissions::Executable);
- QTextStream stream(&file);
- stream.setCodec("UTF-8");
- stream << QLatin1String("[Desktop Entry]") << endl;
+ QString outString;
+ QTextStream stream(&outString);
+ stream << QLatin1String("[Desktop Entry]") << Qt::endl;
// Type=Application\nExec=qtcreator\nPath=...
const QStringList pairs = values.split(QLatin1Char('\n'));
for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it)
- stream << *it << endl;
+ stream << *it << Qt::endl;
+ file.write(outString.toUtf8());
return true;
}
diff --git a/src/libs/installer/createlinkoperation.cpp b/src/libs/installer/createlinkoperation.cpp
index 3f29367b6..65ff8fc5d 100644
--- a/src/libs/installer/createlinkoperation.cpp
+++ b/src/libs/installer/createlinkoperation.cpp
@@ -88,7 +88,7 @@ bool CreateLinkOperation::undoOperation()
return false;
}
- return !QFileInfo(linkPath).exists();
+ return !QFileInfo::exists(linkPath);
}
bool CreateLinkOperation::testOperation()
diff --git a/src/libs/installer/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/createshortcutoperation.cpp b/src/libs/installer/createshortcutoperation.cpp
index 57f901c2f..894b5843b 100644
--- a/src/libs/installer/createshortcutoperation.cpp
+++ b/src/libs/installer/createshortcutoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -110,6 +110,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString
IUnknown *iunkn = nullptr;
if (fileName.toLower().startsWith(QLatin1String("http:"))
+ || fileName.toLower().startsWith(QLatin1String("https:"))
|| fileName.toLower().startsWith(QLatin1String("ftp:"))) {
IUniformResourceLocator *iurl = nullptr;
if (FAILED(CoCreateInstance(CLSID_InternetShortcut, nullptr, CLSCTX_INPROC_SERVER,
@@ -176,6 +177,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString
Q_UNUSED(linkName)
Q_UNUSED(iconPath)
Q_UNUSED(iconId)
+ Q_UNUSED(description)
return true;
#endif
}
diff --git a/src/libs/installer/customcombobox.cpp b/src/libs/installer/customcombobox.cpp
new file mode 100644
index 000000000..998364fe4
--- /dev/null
+++ b/src/libs/installer/customcombobox.cpp
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "customcombobox.h"
+
+#include <QStylePainter>
+
+using namespace QInstaller;
+
+CustomComboBox::CustomComboBox(QWidget *parent)
+ : QComboBox(parent)
+{
+}
+
+void CustomComboBox::paintEvent(QPaintEvent *e)
+{
+ if (currentIndex() < 0 && !placeholderText().isEmpty()) {
+ QStylePainter painter(this);
+ painter.setPen(palette().color(QPalette::Text));
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+ opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText());
+ opt.currentText = placeholderText();
+ painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+ } else {
+ QComboBox::paintEvent(e);
+ }
+}
diff --git a/src/libs/installer/customcombobox.h b/src/libs/installer/customcombobox.h
new file mode 100644
index 000000000..e022da5a8
--- /dev/null
+++ b/src/libs/installer/customcombobox.h
@@ -0,0 +1,48 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef CUSTOMCOMBOBOX_H
+#define CUSTOMCOMBOBOX_H
+
+#include <QComboBox>
+
+namespace QInstaller {
+
+class CustomComboBox : public QComboBox
+{
+ Q_OBJECT
+public:
+ CustomComboBox(QWidget *parent = nullptr);
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+};
+
+} // namespace QInstaller
+
+#endif // CUSTOMCOMBOBOX_H
diff --git a/src/libs/installer/directoryguard.cpp b/src/libs/installer/directoryguard.cpp
index 9c97130a4..014d213d7 100644
--- a/src/libs/installer/directoryguard.cpp
+++ b/src/libs/installer/directoryguard.cpp
@@ -28,6 +28,7 @@
#include "directoryguard.h"
+#include "fileutils.h"
#include "globals.h"
#include "errors.h"
@@ -92,8 +93,7 @@ QStringList DirectoryGuard::tryCreate()
toCreate = QDir(p);
}
- QDir dir(m_path);
- m_created = dir.mkpath(m_path);
+ m_created = QInstaller::createDirectoryWithParents(m_path);
if (!m_created) {
throw Error(QCoreApplication::translate("DirectoryGuard",
"Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path)));
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
index 4d17ef4a2..65eead1f9 100644
--- a/src/libs/installer/downloadarchivesjob.cpp
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -44,10 +44,12 @@ using namespace QInstaller;
using namespace KDUpdater;
+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)
@@ -58,8 +60,10 @@ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
, m_progressChangedTimerId(0)
, m_totalSizeToDownload(0)
, m_totalSizeDownloaded(0)
+ , m_retryCount(scMaxRetries)
{
setCapabilities(Cancelable);
+ setObjectName(objectName);
}
/*!
@@ -75,7 +79,7 @@ DownloadArchivesJob::~DownloadArchivesJob()
Sets the \a archives to download. The first value of each pair contains the file name to register
the file in the installer's internal file system, the second one the source url.
*/
-void DownloadArchivesJob::setArchivesToDownload(const QList<QPair<QString, QString> > &archives)
+void DownloadArchivesJob::setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives)
{
m_archivesToDownload = archives;
m_archivesToDownloadCount = archives.count();
@@ -111,17 +115,17 @@ void DownloadArchivesJob::doCancel()
void DownloadArchivesJob::fetchNextArchiveHash()
{
- if (m_core->testChecksum()) {
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_archivesToDownload.first().checkSha1CheckSum) {
if (m_canceled) {
finishWithError(tr("Canceled"));
return;
}
- if (m_archivesToDownload.isEmpty()) {
- emitFinished();
- return;
- }
-
if (m_downloader)
m_downloader->deleteLater();
@@ -146,6 +150,7 @@ void DownloadArchivesJob::finishedHashDownload()
QFile sha1HashFile(m_downloader->downloadedFileName());
if (sha1HashFile.open(QFile::ReadOnly)) {
+ emit hashDownloadReady(m_downloader->downloadedFileName());
m_currentHash = sha1HashFile.readAll();
fetchNextArchive();
} else {
@@ -280,25 +285,32 @@ void DownloadArchivesJob::registerFile()
{
Q_ASSERT(m_downloader != nullptr);
- if (m_canceled)
+ if (m_canceled || m_archivesToDownload.isEmpty())
return;
- if (m_core->testChecksum() && m_currentHash != m_downloader->sha1Sum().toHex()) {
+ if (m_archivesToDownload.first().checkSha1CheckSum && m_currentHash != m_downloader->sha1Sum().toHex()) {
//TODO: Maybe we should try to download the file again automatically
const QMessageBox::Button res =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while "
- "downloading failed. This is a temporary error, please retry."),
- QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel);
-
- // If run from command line instance, do not continue if hash verification failed.
- // Same download is tried again and again causing infinite loop if hash not
- // fixed to repositories.
- if (res == QMessageBox::Cancel || m_core->isCommandLineInstance()) {
- finishWithError(tr("Cannot verify Hash"));
+ "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\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
+ // infinite loop in case the automatic answer for the messagebox is "Retry"
+ if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) {
+ finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries));
+ return;
+ }
} else {
+ m_retryCount = scMaxRetries;
+
++m_archivesDownloaded;
m_totalSizeDownloaded += QFile(m_downloader->downloadedFileName()).size();
if (m_progressChangedTimerId) {
@@ -307,9 +319,11 @@ void DownloadArchivesJob::registerFile()
emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
}
- const QPair<QString, QString> pair = m_archivesToDownload.takeFirst();
- BinaryFormatEngineHandler::instance()->registerResource(pair.first,
+ const PackageManagerCore::DownloadItem item = m_archivesToDownload.takeFirst();
+ BinaryFormatEngineHandler::instance()->registerResource(item.fileName,
m_downloader->downloadedFileName());
+
+ emit fileDownloadReady(m_downloader->downloadedFileName());
}
fetchNextArchiveHash();
}
@@ -327,14 +341,21 @@ void DownloadArchivesJob::downloadFailed(const QString &error)
const QMessageBox::StandardButton b =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2")
- .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel);
+ .arg(m_archivesToDownload.first().sourceUrl, error), QMessageBox::Retry | QMessageBox::Cancel,
+ QMessageBox::Retry);
+
+ if (b == QMessageBox::Retry) {
+ // When using command line instance, only retry a number of times to avoid
+ // infinite loop in case the automatic answer for the messagebox is "Retry"
+ if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) {
+ finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries));
+ return;
+ }
- // Do not call fetchNextArchiveHash when using command line instance,
- // installer tries to download the same archive causing infinite loop
- if (b == QMessageBox::Retry && !m_core->isCommandLineInstance())
QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
- else
+ } else {
downloadCanceled();
+ }
}
void DownloadArchivesJob::finishWithError(const QString &error)
@@ -350,13 +371,13 @@ void DownloadArchivesJob::finishWithError(const QString &error)
KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix, const QString &queryString)
{
KDUpdater::FileDownloader *downloader = nullptr;
- const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
+ const QFileInfo fi = QFileInfo(m_archivesToDownload.first().fileName);
const Component *const component = m_core->componentByName(PackageManagerCore::checkableName(QFileInfo(fi.path()).fileName()));
if (component) {
QString fullQueryString;
if (!queryString.isEmpty())
fullQueryString = QLatin1String("?") + queryString;
- const QUrl url(m_archivesToDownload.first().second + suffix + fullQueryString);
+ const QUrl url(m_archivesToDownload.first().sourceUrl + suffix + fullQueryString);
const QString &scheme = url.scheme();
downloader = FileDownloaderFactory::instance().create(scheme, this);
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
index c69291d69..5155c881a 100644
--- a/src/libs/installer/downloadarchivesjob.h
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -30,7 +30,7 @@
#define DOWNLOADARCHIVESJOB_H
#include "job.h"
-
+#include "packagemanagercore.h"
#include <QtCore/QPair>
#include <QtCore/QElapsedTimer>
@@ -45,18 +45,17 @@ namespace KDUpdater {
namespace QInstaller {
class MessageBoxHandler;
-class PackageManagerCore;
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; }
- void setArchivesToDownload(const QList<QPair<QString, QString> > &archives);
+ void setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives);
void setExpectedTotalSize(quint64 total);
Q_SIGNALS:
@@ -64,6 +63,9 @@ Q_SIGNALS:
void outputTextChanged(const QString &progress);
void downloadStatusChanged(const QString &status);
+ void hashDownloadReady(const QString &localPath);
+ void fileDownloadReady(const QString &localPath);
+
protected:
void doStart() override;
void doCancel() override;
@@ -91,7 +93,7 @@ private:
int m_archivesDownloaded;
int m_archivesToDownloadCount;
- QList<QPair<QString, QString> > m_archivesToDownload;
+ QList<PackageManagerCore::DownloadItem> m_archivesToDownload;
bool m_canceled;
QByteArray m_currentHash;
@@ -101,6 +103,8 @@ private:
quint64 m_totalSizeToDownload;
quint64 m_totalSizeDownloaded;
QElapsedTimer m_totalDownloadSpeedTimer;
+
+ uint m_retryCount;
};
} // namespace QInstaller
diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp
index 1b9f81ecc..a959677a9 100644
--- a/src/libs/installer/downloadfiletask.cpp
+++ b/src/libs/installer/downloadfiletask.cpp
@@ -1,7 +1,7 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "downloadfiletask_p.h"
#include "globals.h"
+#include "productkeycheck.h"
#include <QCoreApplication>
#include <QDir>
@@ -197,7 +198,7 @@ void Downloader::onReadyRead()
data.observer->addSample(read);
data.observer->addBytesTransfered(read);
- data.observer->addCheckSumData(buffer.data(), read);
+ data.observer->addCheckSumData(buffer.left(read));
int progress = m_finished * 100;
for (const auto &pair : m_downloads)
@@ -246,7 +247,7 @@ void Downloader::onFinished(QNetworkReply *reply)
if (!ba.isEmpty()) {
data.observer->addSample(ba.size());
data.observer->addBytesTransfered(ba.size());
- data.observer->addCheckSumData(ba.data(), ba.size());
+ data.observer->addCheckSumData(ba);
}
const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray();
@@ -269,7 +270,7 @@ void Downloader::onFinished(QNetworkReply *reply)
}
}
-void Downloader::onError(QNetworkReply::NetworkError error)
+void Downloader::errorOccurred(QNetworkReply::NetworkError error)
{
QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender());
@@ -286,6 +287,10 @@ void Downloader::onError(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(
@@ -395,14 +400,17 @@ QNetworkReply *Downloader::startDownload(const FileTaskItem &item)
.arg(source.toString(), source.errorString())));
return 0;
}
+ QNetworkRequest request(source);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
- QNetworkReply *reply = m_nam.get(QNetworkRequest(source));
+ QNetworkReply *reply = m_nam.get(request);
std::unique_ptr<Data> data(new Data(item));
m_downloads[reply] = std::move(data);
connect(reply, &QIODevice::readyRead, this, &Downloader::onReadyRead);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
- SLOT(onError(QNetworkReply::NetworkError)));
+ connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this,
+ SLOT(errorOccurred(QNetworkReply::NetworkError)));
#ifndef QT_NO_SSL
connect(reply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors);
#endif
diff --git a/src/libs/installer/downloadfiletask_p.h b/src/libs/installer/downloadfiletask_p.h
index 3dfce27b4..4750d5134 100644
--- a/src/libs/installer/downloadfiletask_p.h
+++ b/src/libs/installer/downloadfiletask_p.h
@@ -86,7 +86,7 @@ private slots:
void doDownload();
void onReadyRead();
void onFinished(QNetworkReply *reply);
- void onError(QNetworkReply::NetworkError error);
+ void errorOccurred(QNetworkReply::NetworkError error);
void onSslErrors(const QList<QSslError> &sslErrors);
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp
index fb1778fe0..87810211e 100644
--- a/src/libs/installer/elevatedexecuteoperation.cpp
+++ b/src/libs/installer/elevatedexecuteoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,7 +35,7 @@
#include <QtCore/QDebug>
#include <QtCore/QProcessEnvironment>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QThread>
using namespace QInstaller;
@@ -65,10 +65,12 @@ public:
private:
bool needsRerunWithReplacedVariables(QStringList &arguments, const OperationType type);
+ void setErrorMessage(const QString &message);
-
+private:
QProcessWrapper *process;
bool showStandardError;
+ QString m_customErrorMessage;
};
ElevatedExecuteOperation::ElevatedExecuteOperation(PackageManagerCore *core)
@@ -120,13 +122,12 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
args.removeAll(workingDirectoryArgument);
}
- QString customErrorMessage;
QStringList filteredCustomErrorMessage = args.filter(QLatin1String("errormessage="),
Qt::CaseInsensitive);
if (!filteredCustomErrorMessage.isEmpty()) {
QString customErrorMessageArgument = filteredCustomErrorMessage.at(0);
- customErrorMessage = customErrorMessageArgument;
- customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive);
+ m_customErrorMessage = customErrorMessageArgument;
+ m_customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive);
args.removeAll(customErrorMessageArgument);
}
@@ -137,9 +138,10 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
QList< int > allowedExitCodes;
- QRegExp re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
- if (re.exactMatch(args.first())) {
- const QStringList numbers = re.cap(1).split(QLatin1Char(','));
+ static const QRegularExpression re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
+ const QRegularExpressionMatch match = re.match(args.first());
+ if (match.hasMatch()) {
+ const QStringList numbers = match.captured(1).split(QLatin1Char(','));
for(QStringList::const_iterator it = numbers.constBegin(); it != numbers.constEnd(); ++it)
allowedExitCodes.push_back(it->toInt());
args.pop_front();
@@ -156,7 +158,8 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
const bool success = QProcessWrapper::startDetached(args.front(), args.mid(1));
if (!success) {
q->setError(UserDefinedError);
- q->setErrorString(tr("Cannot start detached: \"%1\"").arg(callstr));
+ setErrorMessage(tr("Cannot start detached: \"%1\"").arg(callstr));
+
returnValue = Error;
}
return returnValue;
@@ -201,8 +204,7 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
int returnValue = NoError;
if (!success) {
q->setError(UserDefinedError);
- //TODO: pass errorString() through the wrapper */
- q->setErrorString(tr("Cannot start: \"%1\": %2").arg(callstr,
+ setErrorMessage(tr("Cannot start: \"%1\": %2").arg(callstr,
process->errorString()));
if (!needsRerunWithReplacedVariables(arguments, type)) {
returnValue = Error;
@@ -220,30 +222,25 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
q->setValue(QLatin1String("ExitCode"), process->exitCode());
- if (process->exitStatus() == QProcessWrapper::CrashExit) {
- q->setError(UserDefinedError);
- q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr));
- returnValue = Error;
- }
+ if (success) {
+ const QByteArray standardErrorOutput = process->readAllStandardError();
+ // in error case it would be useful to see something in verbose output
+ if (!standardErrorOutput.isEmpty())
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput;
- if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) {
- if (!needsRerunWithReplacedVariables(arguments, type)) {
+ if (process->exitStatus() == QProcessWrapper::CrashExit) {
q->setError(UserDefinedError);
- if (customErrorMessage.isEmpty()) {
- q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
+ setErrorMessage(tr("Program crashed: \"%1\"").arg(callstr));
+ returnValue = Error;
+ } else if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) {
+ if (!needsRerunWithReplacedVariables(arguments, type)) {
+ q->setError(UserDefinedError);
+ setErrorMessage(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
.arg(QString::number(process->exitCode()), callstr));
+ returnValue = Error;
} else {
- q->setErrorString(customErrorMessage);
+ returnValue = NeedsRerun;
}
-
- QByteArray standardErrorOutput = process->readAllStandardError();
- // in error case it would be useful to see something in verbose output
- if (!standardErrorOutput.isEmpty())
- qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput;
-
- returnValue = Error;
- } else {
- returnValue = NeedsRerun;
}
}
Q_ASSERT(process);
@@ -276,6 +273,13 @@ bool ElevatedExecuteOperation::Private::needsRerunWithReplacedVariables(QStringL
return rerun;
}
+void ElevatedExecuteOperation::Private::setErrorMessage(const QString &message)
+{
+ if (m_customErrorMessage.isEmpty())
+ q->setErrorString(message);
+ else
+ q->setErrorString(m_customErrorMessage);
+}
/*!
Cancels the ElevatedExecuteOperation. This methods tries to terminate the process
gracefully by calling QProcessWrapper::terminate. After 10 seconds, the process gets killed.
diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp
index 2f55172e1..94cd1e36f 100644
--- a/src/libs/installer/environmentvariablesoperation.cpp
+++ b/src/libs/installer/environmentvariablesoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "qsettingswrapper.h"
#include <stdlib.h>
+#include <QDir>
#include "environment.h"
#include "globals.h"
@@ -110,6 +111,12 @@ bool handleRegExpandSz(const QString &regPath, const QString &name,
}
}
}
+#else
+ Q_UNUSED(regPath)
+ Q_UNUSED(name)
+ Q_UNUSED(value)
+ Q_UNUSED(errorString)
+ Q_UNUSED(error)
#endif
return setAsExpandSZ;
}
@@ -122,7 +129,7 @@ UpdateOperation::Error writeSetting(const QString &regPath,
QString *oldValue)
{
oldValue->clear();
- SettingsType registry(regPath, QSettingsWrapper::NativeFormat);
+ SettingsType registry(regPath, QSettings::NativeFormat);
if (!registry.isWritable()) {
*errorString = UpdateOperation::tr("Registry path %1 is not writable.").arg(regPath);
return UpdateOperation::UserDefinedError;
@@ -156,17 +163,17 @@ UpdateOperation::Error undoSetting(const QString &regPath,
{
QString actual;
{
- SettingsType registry(regPath, QSettingsWrapper::NativeFormat);
+ SettingsType registry(regPath, QSettings::NativeFormat);
actual = registry.value(name).toString();
}
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/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp
index 5ff2d9a1a..b00a67190 100644
--- a/src/libs/installer/extractarchiveoperation.cpp
+++ b/src/libs/installer/extractarchiveoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -103,7 +103,7 @@ void ExtractArchiveOperation::backup()
return;
}
- const bool hasAdminRights = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive());
+ const bool hasAdminRights = (packageManager() && packageManager()->hasAdminRights());
const bool canCreateSymLinks = QInstaller::canCreateSymbolicLinks();
bool needsAdminRights = false;
@@ -178,9 +178,13 @@ bool ExtractArchiveOperation::performOperation()
QString installDir = targetDir;
// If we have package manager in use (normal installer run) then use
// TargetDir for saving filenames, otherwise those would be saved to
- // extracted folder.
- if (packageManager())
- installDir = packageManager()->value(scTargetDir);
+ // extracted folder. Also initialize installerbasebinary which we use later
+ // to check if the extracted file in question is the maintenancetool itself.
+ QString installerBaseBinary;
+ if (PackageManagerCore *core = packageManager()) {
+ installDir = core->value(scTargetDir);
+ installerBaseBinary = core->toNativeSeparators(core->replaceVariables(core->installerBaseBinary()));
+ }
const QString resourcesPath = installDir + QLatin1Char('/') + QLatin1String("installerResources");
QString fileDirectory = resourcesPath + QLatin1Char('/') + archivePath.section(QLatin1Char('/'), 1, 1,
@@ -193,20 +197,7 @@ bool ExtractArchiveOperation::performOperation()
QFileInfo targetDirectoryInfo(fileDirectory);
- // We need to create the directory structure step-by-step to avoid a rare race condition
- // on Windows. QDir::mkpath() doesn't check if the leading directories were created elsewhere
- // (like from within another thread) after the initial check that the given path requires
- // creating also parent directories.
- //
- // On Unix patforms this case is handled by QFileSystemEngine though.
- QDir resourcesDir(resourcesPath);
- if (!resourcesDir.exists())
- resourcesDir.mkdir(resourcesPath);
-
- QDir resourceFileDir(targetDirectoryInfo.absolutePath());
- if (!resourceFileDir.exists())
- resourceFileDir.mkdir(targetDirectoryInfo.absolutePath());
-
+ QInstaller::createDirectoryWithParents(targetDirectoryInfo.absolutePath());
setDefaultFilePermissions(resourcesPath, DefaultFilePermissions::Executable);
setDefaultFilePermissions(targetDirectoryInfo.absolutePath(), DefaultFilePermissions::Executable);
@@ -215,9 +206,17 @@ bool ExtractArchiveOperation::performOperation()
setDefaultFilePermissions(file.fileName(), DefaultFilePermissions::NonExecutable);
QDataStream out (&file);
for (int i = 0; i < files.count(); ++i) {
+ if (!installerBaseBinary.isEmpty() && files[i].startsWith(installerBaseBinary)) {
+ // Do not write installerbase binary filename to extracted files. Installer binary
+ // is maintenance tool program, the binary is removed elsewhere
+ // when we do full uninstall.
+ files.clear();
+ break;
+ }
files[i] = replacePath(files.at(i), installDir, QLatin1String(scRelocatable));
}
- out << files;
+ if (!files.isEmpty())
+ out << files;
setValue(QLatin1String("files"), file.fileName());
file.close();
} else {
@@ -255,10 +254,14 @@ bool ExtractArchiveOperation::undoOperation()
if (!readDataFileContents(targetDir, &files))
return false;
}
- startUndoProcess(files);
+ if (!files.isEmpty())
+ startUndoProcess(files);
if (!useStringListType)
deleteDataFile(m_relocatedDataFileName);
+ // Remove the installerResources directory if it is empty.
+ QDir(targetDir).rmdir(QLatin1String("installerResources"));
+
return true;
}
diff --git a/src/libs/installer/fakestopprocessforupdateoperation.cpp b/src/libs/installer/fakestopprocessforupdateoperation.cpp
index 67d60a92f..bdd8625eb 100644
--- a/src/libs/installer/fakestopprocessforupdateoperation.cpp
+++ b/src/libs/installer/fakestopprocessforupdateoperation.cpp
@@ -31,6 +31,8 @@
#include "messageboxhandler.h"
#include "packagemanagercore.h"
+#include <QDir>
+
using namespace KDUpdater;
using namespace QInstaller;
@@ -79,11 +81,11 @@ bool FakeStopProcessForUpdateOperation::undoOperation()
if (processes.count() == 1) {
setError(UpdateOperation::UserDefinedError, tr("This process should be stopped before "
- "continuing: %1").arg(processes.first()));
+ "continuing: %1").arg(QDir::toNativeSeparators(processes.first())));
} else {
const QString sep = QString::fromWCharArray(L"\n \u2022 "); // Unicode bullet
setError(UpdateOperation::UserDefinedError, tr("These processes should be stopped before "
- "continuing: %1").arg(sep + processes.join(sep)));
+ "continuing: %1").arg(sep + QDir::toNativeSeparators(processes.join(sep))));
}
return false;
}
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp
index 5a89073fc..044eeb34f 100644
--- a/src/libs/installer/fileutils.cpp
+++ b/src/libs/installer/fileutils.cpp
@@ -66,54 +66,62 @@ using namespace QInstaller;
/*!
\inmodule QtInstallerFramework
- \class QInstaller::TempDirDeleter
+ \class QInstaller::TempPathDeleter
\internal
*/
// -- TempDirDeleter
-TempDirDeleter::TempDirDeleter(const QString &path)
+TempPathDeleter::TempPathDeleter(const QString &path)
{
m_paths.insert(path);
}
-TempDirDeleter::TempDirDeleter(const QStringList &paths)
+TempPathDeleter::TempPathDeleter(const QStringList &paths)
: m_paths(QSet<QString>(paths.begin(), paths.end()))
{
}
-TempDirDeleter::~TempDirDeleter()
+TempPathDeleter::~TempPathDeleter()
{
releaseAndDeleteAll();
}
-QStringList TempDirDeleter::paths() const
+QStringList TempPathDeleter::paths() const
{
- return m_paths.toList();
+ return m_paths.values();
}
-void TempDirDeleter::add(const QString &path)
+void TempPathDeleter::add(const QString &path)
{
m_paths.insert(path);
}
-void TempDirDeleter::add(const QStringList &paths)
+void TempPathDeleter::add(const QStringList &paths)
{
m_paths += QSet<QString>(paths.begin(), paths.end());
}
-void TempDirDeleter::releaseAndDeleteAll()
+void TempPathDeleter::releaseAndDeleteAll()
{
foreach (const QString &path, m_paths)
releaseAndDelete(path);
}
-void TempDirDeleter::releaseAndDelete(const QString &path)
+void TempPathDeleter::releaseAndDelete(const QString &path)
{
if (m_paths.contains(path)) {
try {
m_paths.remove(path);
- removeDirectory(path);
+ if (QFileInfo(path).isDir()) {
+ removeDirectory(path);
+ return;
+ }
+ QFile file(path);
+ if (file.exists() && !file.remove()) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot remove file \"%1\": %2").arg(file.fileName(), file.errorString()));
+ }
} catch (const Error &e) {
qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message();
} catch (...) {
@@ -363,7 +371,7 @@ bool QInstaller::setDefaultFilePermissions(QFile *file, DefaultFilePermissions p
void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir)
{
Q_ASSERT(QFileInfo(sourceDir).isDir());
- Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ Q_ASSERT(!QFileInfo::exists(targetDir) || QFileInfo(targetDir).isDir());
if (!QDir().mkpath(targetDir)) {
throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".")
.arg(QDir::toNativeSeparators(targetDir)));
@@ -394,7 +402,7 @@ void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &
void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &targetDir)
{
Q_ASSERT(QFileInfo(sourceDir).isDir());
- Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ Q_ASSERT(!QFileInfo::exists(targetDir) || QFileInfo(targetDir).isDir());
if (!QDir().mkpath(targetDir)) {
throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".")
.arg(QDir::toNativeSeparators(targetDir)));
@@ -449,6 +457,52 @@ void QInstaller::mkpath(const QString &path)
/*!
\internal
+ Creates directory \a path including all parent directories. Return \c true on
+ success, \c false otherwise.
+
+ On Windows \c QDir::mkpath() doesn't check if the leading directories were created
+ elsewhere (i.e. in another thread) after the initial check that the given path
+ requires creating also parent directories, and returns \c false.
+
+ On Unix platforms this case is handled different by QFileSystemEngine though,
+ which checks for \c EEXIST error code in case any of the recursive directories
+ could not be created.
+
+ Compared to \c QInstaller::mkpath() and \c QDir::mkpath() this function checks if
+ each parent directory to-be-created were created elsewhere.
+*/
+bool QInstaller::createDirectoryWithParents(const QString &path)
+{
+ if (path.isEmpty())
+ return false;
+
+ QFileInfo dirInfo(path);
+ if (dirInfo.exists() && dirInfo.isDir())
+ return true;
+
+ // bail out if we are at the root directory
+ if (dirInfo.isRoot())
+ return false;
+
+ QDir dir(path);
+ if (dir.exists() || dir.mkdir(path))
+ return true;
+
+ // mkdir failed, try to create the parent directory
+ if (!createDirectoryWithParents(dirInfo.absolutePath()))
+ return false;
+
+ // now try again
+ if (dir.exists() || dir.mkdir(path))
+ return true;
+
+ // directory may be have also been created elsewhere
+ return (dirInfo.exists() && dirInfo.isDir());
+}
+
+/*!
+ \internal
+
Generates and returns a temporary file name. The name can start with
a template \a templ.
*/
@@ -680,7 +734,7 @@ quint64 QInstaller::fileSize(const QFileInfo &info)
bool QInstaller::isInBundle(const QString &path, QString *bundlePath)
{
#ifdef Q_OS_MACOS
- QFileInfo fi = QFileInfo(path).absoluteFilePath();
+ QFileInfo fi(QFileInfo(path).absoluteFilePath());
while (!fi.isRoot()) {
if (fi.isBundle()) {
if (bundlePath)
@@ -810,8 +864,8 @@ void QInstaller::copyConfigChildElements(QDomDocument &dom, const QDomNodeList &
// Filename may also contain a path relative to source directory but we
// copy it strictly into target directory without extra paths
- const QString newName = domElement.text()
- .replace(QRegExp(QLatin1String("\\\\|/|\\.|:")), QLatin1String("_"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:"));
+ const QString newName = domElement.text().replace(regex, QLatin1String("_"));
const QString targetFile = targetDir + QDir::separator() + newName;
const QFileInfo elementFileInfo = QFileInfo(sourceDir, domElement.text());
diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h
index 8b79ce052..3c995937c 100644
--- a/src/libs/installer/fileutils.h
+++ b/src/libs/installer/fileutils.h
@@ -49,12 +49,12 @@ enum DefaultFilePermissions {
Executable = 0x7755
};
-class INSTALLER_EXPORT TempDirDeleter
+class INSTALLER_EXPORT TempPathDeleter
{
public:
- explicit TempDirDeleter(const QString &path);
- explicit TempDirDeleter(const QStringList &paths = QStringList());
- ~TempDirDeleter();
+ explicit TempPathDeleter(const QString &path);
+ explicit TempPathDeleter(const QStringList &paths = QStringList());
+ ~TempPathDeleter();
QStringList paths() const;
@@ -65,7 +65,7 @@ public:
void releaseAndDelete(const QString &path);
private:
- Q_DISABLE_COPY(TempDirDeleter)
+ Q_DISABLE_COPY(TempPathDeleter)
QSet<QString> m_paths;
};
@@ -89,6 +89,7 @@ private:
void INSTALLER_EXPORT mkdir(const QString &path);
void INSTALLER_EXPORT mkpath(const QString &path);
+ bool INSTALLER_EXPORT createDirectoryWithParents(const QString &path);
#ifdef Q_OS_MACOS
void INSTALLER_EXPORT mkalias(const QString &path, const QString &alias);
#endif
diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp
new file mode 100644
index 000000000..a1e31ccfe
--- /dev/null
+++ b/src/libs/installer/genericdatacache.cpp
@@ -0,0 +1,695 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "genericdatacache.h"
+
+#include "errors.h"
+#include "fileutils.h"
+#include "globals.h"
+#include "metadata.h"
+#include "updater.h"
+
+#include <QDir>
+#include <QDirIterator>
+#include <QtConcurrent>
+
+namespace QInstaller {
+
+static const QLatin1String scManifestFile("manifest.json");
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CacheableItem
+ \brief The CacheableItem is a pure virtual class that defines an interface for
+ a type suited for storage with the \l{GenericDataCache} class.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::path() const
+
+ Returns the path of this item. A subclass may override this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::setPath(const QString &path)
+
+ Sets the path of the item to \a path. A subclass may override this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::checksum() const
+
+ Returns the checksum of this item. A subclass must implement this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::isValid() const
+
+ Returns \c true if this item is valid, \c false otherwise.
+ A subclass must implement this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::isActive() const
+
+ Returns \c true if this item is an actively used cache item, \c false otherwise.
+ This information is used as a hint for filtering obsolete entries, an active item
+ can never be obsolete.
+
+ A subclass must implement this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::obsoletes(CacheableItem *other)
+
+ Returns \c true if the calling item obsoletes \a other item, \c false otherwise.
+ This method is used for filtering obsolete entries from the cache.
+
+ A subclass must implement this method.
+*/
+
+/*!
+ Virtual destructor for \c CacheableItem.
+*/
+CacheableItem::~CacheableItem()
+{
+}
+
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::GenericDataCache
+ \brief The GenericDataCache is a template class for a checksum based storage
+ of items on disk.
+
+ GenericDataCache\<T\> manages a cache storage for a set \l{path()}, which contains
+ a subdirectory for each registered item. An item of type \c T should implement
+ methods declared in the \l{CacheableItem} interface. The GenericDataCache\<T\> class can
+ still be explicitly specialized to use the derived type as a template argument, to
+ allow retrieving items as the derived type without casting.
+
+ Each cache has a manifest file in its root directory, which lists the version
+ and wrapped type of the cache, and all its items. The file is updated automatically
+ when the cache object is destructed, or it can be updated periodically by
+ calling \l{sync()}.
+*/
+
+/*!
+ \enum GenericDataCache::RegisterMode
+ This enum holds the possible values for modes of registering items to cache.
+
+ \value Copy
+ The contents of the item are copied to the cache.
+ \value Move
+ The contents of the item are move to the cache.
+*/
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::GenericDataCache()
+
+ Constructs a new empty cache. The cache is invalid until set with a
+ path and initialized.
+*/
+template <typename T>
+GenericDataCache<T>::GenericDataCache()
+ : m_version(QLatin1String("1.0.0"))
+ , m_invalidated(true)
+{
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::GenericDataCache(const QString &path, const QString &type, const QString &version)
+
+ Constructs a cache to \a path with the given \a type and \a version.
+ The cache is initialized automatically.
+*/
+template <typename T>
+GenericDataCache<T>::GenericDataCache(const QString &path, const QString &type,
+ const QString &version)
+ : m_path(path)
+ , m_type(type)
+ , m_version(version)
+ , m_invalidated(true)
+{
+ initialize();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::~GenericDataCache()
+
+ Deletes the cache object. Item contents on disk are kept.
+*/
+template <typename T>
+GenericDataCache<T>::~GenericDataCache()
+{
+ if (m_invalidated)
+ return;
+
+ toDisk();
+ invalidate();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::setType(const QString &type)
+
+ Sets the name of the wrapped type to \a type. This is used for determining if an
+ existing cache holds items of the same type. Trying to load cached items with mismatching
+ type results in discarding the old items. Optional.
+*/
+template<typename T>
+void GenericDataCache<T>::setType(const QString &type)
+{
+ QMutexLocker _(&m_mutex);
+ m_type = type;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::setVersion(const QString &version)
+
+ Sets the version of the cache to \a version. Loading from a cache with different
+ expected version discards the old items. The version property defaults to \c{1.0.0}.
+*/
+template<typename T>
+void GenericDataCache<T>::setVersion(const QString &version)
+{
+ QMutexLocker _(&m_mutex);
+ m_version = version;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::initialize()
+
+ Initializes a cache. Creates a new directory for the path configured for
+ this cache if it does not exist, and loads any previously cached items from
+ the directory. The cache directory is locked for access by this process only.
+ Returns \c true on success, \c false otherwise.
+*/
+template <typename T>
+bool GenericDataCache<T>::initialize()
+{
+ QMutexLocker _(&m_mutex);
+ Q_ASSERT(m_items.isEmpty());
+
+ if (m_path.isEmpty()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot initialize cache with empty path."));
+ return false;
+ }
+
+ QDir directory(m_path);
+ if (!directory.exists() && !directory.mkpath(m_path)) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot create directory \"%1\" for cache.").arg(m_path));
+ return false;
+ }
+
+ if (m_lock && !m_lock->unlock()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot initialize cache: %1").arg(m_lock->errorString()));
+ return false;
+ }
+
+ m_lock.reset(new KDUpdater::LockFile(m_path + QLatin1String("/cache.lock")));
+ if (!m_lock->lock()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot initialize cache: %1").arg(m_lock->errorString()));
+ return false;
+ }
+
+ if (!fromDisk())
+ return false;
+
+ m_invalidated = false;
+ return true;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::clear()
+
+ Removes all items from the cache and deletes their contents on disk. If
+ the cache directory becomes empty, it is also deleted. The cache becomes
+ invalid after this action, even in case of error while clearing. In that
+ case already deleted items will be lost. Returns \c true on success,
+ \c false otherwise.
+*/
+template <typename T>
+bool GenericDataCache<T>::clear()
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot clear invalidated cache."));
+ return false;
+ }
+
+ QFile manifestFile(m_path + QDir::separator() + scManifestFile);
+ if (manifestFile.exists() && !manifestFile.remove()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove manifest file: %1").arg(manifestFile.errorString()));
+ invalidate();
+ return false;
+ }
+
+ bool success = true;
+ for (T *item : qAsConst(m_items)) {
+ try {
+ QInstaller::removeDirectory(item->path());
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while clearing cache: %1").arg(e.message()));
+ success = false;
+ }
+ }
+
+ invalidate();
+ QDir().rmdir(m_path);
+ return success;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::sync()
+
+ Synchronizes the contents of the cache to its manifest file. Returns \c true
+ if the manifest file was updates successfully, \c false otherwise.
+*/
+template<typename T>
+bool GenericDataCache<T>::sync()
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot synchronize invalidated cache."));
+ return false;
+ }
+ return toDisk();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::isValid() const
+
+ Returns \c true if the cache is valid, \c false otherwise. A cache is considered
+ valid when it is initialized to a set path.
+*/
+template<typename T>
+bool GenericDataCache<T>::isValid() const
+{
+ QMutexLocker _(&m_mutex);
+ return !m_invalidated;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::errorString() const
+
+ Returns a string representing the last error with the cache.
+*/
+template<typename T>
+QString GenericDataCache<T>::errorString() const
+{
+ QMutexLocker _(&m_mutex);
+ return m_error;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::path() const
+
+ Returns the path of the cache on disk.
+*/
+template <typename T>
+QString GenericDataCache<T>::path() const
+{
+ QMutexLocker _(&m_mutex);
+ return m_path;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::setPath(const QString &path)
+
+ Sets a new \a path for the cache and invalidates current items. Saves the information
+ of the old cache to its manifest file.
+*/
+template <typename T>
+void GenericDataCache<T>::setPath(const QString &path)
+{
+ QMutexLocker _(&m_mutex);
+ if (!m_invalidated)
+ toDisk();
+
+ m_path = path;
+ invalidate();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::items() const
+
+ Returns a list of cached items.
+*/
+template <typename T>
+QList<T *> GenericDataCache<T>::items() const
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot retrieve items from invalidated cache."));
+ return QList<T *>();
+ }
+ return m_items.values();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::itemByChecksum(const QByteArray &checksum) const
+
+ Returns an item that matches the \a checksum or \c nullptr in case
+ no such item is cached.
+*/
+template <typename T>
+T *GenericDataCache<T>::itemByChecksum(const QByteArray &checksum) const
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot retrieve item from invalidated cache."));
+ return nullptr;
+ }
+ return m_items.value(checksum);
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::itemByPath(const QString &path) const
+
+ Returns an item from the \a path or \c nullptr in case no such item
+ is cached. Depending on the size of the cache, this can be much
+ slower than retrieving an item with \l{itemByChecksum()}.
+*/
+template <typename T>
+T *GenericDataCache<T>::itemByPath(const QString &path) const
+{
+ QMutexLocker _(&m_mutex);
+ auto it = std::find_if(m_items.constBegin(), m_items.constEnd(),
+ [&](T *item) {
+ return (QDir::fromNativeSeparators(path) == QDir::fromNativeSeparators(item->path()));
+ }
+ );
+ if (it != m_items.constEnd())
+ return it.value();
+
+ return nullptr;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode)
+
+ Registers the \a item to the cache. If \a replace is set to \c true,
+ the new \a item replaces a previous item with the same checksum.
+
+ 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 \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.
+*/
+template <typename T>
+bool GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode)
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register item to invalidated cache."));
+ return false;
+ }
+ if (!item) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register null item."));
+ return false;
+ }
+ if (!item->isValid()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register invalid item with checksum %1").arg(QLatin1String(item->checksum())));
+ return false;
+ }
+ if (m_items.contains(item->checksum())) {
+ if (replace) {// replace existing item including contents on disk
+ remove(item->checksum());
+ } else {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register item with checksum %1. An item with the same checksum "
+ "already exists in cache.").arg(QLatin1String(item->checksum())));
+ return false;
+ }
+ }
+
+ const QString newPath = m_path + QDir::separator() + QString::fromLatin1(item->checksum());
+ try {
+ // A directory is in the way but it isn't registered to the current cache, remove.
+ QDir dir;
+ if (dir.exists(newPath))
+ QInstaller::removeDirectory(newPath);
+
+ switch (mode) {
+ case Copy:
+ QInstaller::copyDirectoryContents(item->path(), newPath);
+ break;
+ case Move:
+ // First, try moving the top level directory
+ if (!dir.rename(item->path(), newPath)) {
+ qCDebug(lcDeveloperBuild) << "Failed to rename directory" << item->path()
+ << "to" << newPath << ". Trying again.";
+ // If that does not work, fallback to moving the contents one by one
+ QInstaller::moveDirectoryContents(item->path(), newPath);
+ }
+ break;
+ default:
+ throw Error(QCoreApplication::translate("GenericDataCache",
+ "Unknown register mode selected!"));
+ }
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while copying item to path \"%1\": %2").arg(newPath, e.message()));
+ return false;
+ }
+
+ item->setPath(newPath);
+ if (item->isValid()) {
+ m_items.insert(item->checksum(), item);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::removeItem(const QByteArray &checksum)
+
+ Removes the item specified by \a checksum from the cache and deletes the
+ contents of the item from disk. Returns \c true if the item
+ was removed successfully, \c false otherwise.
+*/
+template <typename T>
+bool GenericDataCache<T>::removeItem(const QByteArray &checksum)
+{
+ QMutexLocker _(&m_mutex);
+ return remove(checksum);
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::obsoleteItems() const
+
+ Returns items considered obsolete from the cache.
+*/
+template<typename T>
+QList<T *> GenericDataCache<T>::obsoleteItems() const
+{
+ QMutexLocker _(&m_mutex);
+ const QList<T *> obsoletes = QtConcurrent::blockingFiltered(m_items.values(),
+ [&](T *item1) {
+ if (item1->isActive()) // We can skip the iteration for active entries
+ return false;
+
+ for (T *item2 : qAsConst(m_items)) {
+ if (item2->obsoletes(item1))
+ return true;
+ }
+ return false;
+ }
+ );
+ return obsoletes;
+}
+
+/*!
+ \internal
+
+ Marks the cache invalid and clears all items. The contents
+ on disk are not deleted. Releases the lock file of the cache.
+*/
+template <typename T>
+void GenericDataCache<T>::invalidate()
+{
+ if (!m_items.isEmpty()) {
+ qDeleteAll(m_items);
+ m_items.clear();
+ }
+ if (m_lock && !m_lock->unlock()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while invalidating cache: %1").arg(m_lock->errorString()));
+ }
+ m_invalidated = true;
+}
+
+/*!
+ \internal
+
+ Sets the current error string to \a error and prints it as a warning to the console.
+*/
+template <typename T>
+void GenericDataCache<T>::setErrorString(const QString &error) const
+{
+ m_error = error;
+ qCWarning(QInstaller::lcInstallerInstallLog) << error;
+}
+
+/*!
+ \internal
+
+ Reads the manifest file of the cache if one exists, and populates the internal
+ hash from the file contents. Returns \c true if the manifests was read successfully
+ or if the reading was omitted. This is the case if the file does not exist yet, or
+ the type or version of the manifest does not match the current cache object. In case
+ of mismatch the old items are not restored. Returns \c false otherwise.
+*/
+template<typename T>
+bool GenericDataCache<T>::fromDisk()
+{
+ QFile manifestFile(m_path + QDir::separator() + scManifestFile);
+ if (!manifestFile.exists()) // Not yet created
+ return true;
+
+ if (!manifestFile.open(QIODevice::ReadOnly)) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot open manifest file: %1").arg(manifestFile.errorString()));
+ return false;
+ }
+
+ const QByteArray manifestData = manifestFile.readAll();
+ const QJsonDocument manifestJsonDoc(QJsonDocument::fromJson(manifestData));
+ const QJsonObject docJsonObject = manifestJsonDoc.object();
+
+ const QJsonValue type = docJsonObject.value(QLatin1String("type"));
+ if (type.toString() != m_type) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Discarding existing items from cache of type:"
+ << type.toString() << ". New type:" << m_type;
+ return true;
+ }
+
+ const QJsonValue version = docJsonObject.value(QLatin1String("version"));
+ if (KDUpdater::compareVersion(version.toString(), m_version) != 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Discarding existing items from cache with version:"
+ << version.toString() << ". New version:" << m_version;
+ return true;
+ }
+
+ const QJsonArray itemsJsonArray = docJsonObject.value(QLatin1String("items")).toArray();
+ for (const auto &itemJsonValue : itemsJsonArray) {
+ const QString checksum = itemJsonValue.toString();
+
+ std::unique_ptr<T> item(new T(m_path + QDir::separator() + checksum));
+ m_items.insert(checksum.toLatin1(), item.release());
+
+ // The cache directory may contain other entries (unrelated directories or
+ // invalid old cache items) which we don't care about, unless registering
+ // a new entry requires overwriting them.
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ Writes the manifest file with the contents of the internal item hash.
+ Returns \c true on success, \c false otherwise.
+*/
+template<typename T>
+bool GenericDataCache<T>::toDisk()
+{
+ QFile manifestFile(m_path + QDir::separator() + scManifestFile);
+ if (!manifestFile.open(QIODevice::WriteOnly)) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot open manifest file: %1").arg(manifestFile.errorString()));
+ return false;
+ }
+
+ QJsonArray itemsJsonArray;
+ const QList<QByteArray> keys = m_items.keys();
+ for (const QByteArray &key : keys)
+ itemsJsonArray.append(QJsonValue(QLatin1String(key)));
+
+ QJsonObject docJsonObject;
+ docJsonObject.insert(QLatin1String("items"), itemsJsonArray);
+ docJsonObject.insert(QLatin1String("version"), m_version);
+ if (!m_type.isEmpty())
+ docJsonObject.insert(QLatin1String("type"), m_type);
+
+ QJsonDocument manifestJsonDoc;
+ manifestJsonDoc.setObject(docJsonObject);
+ if (manifestFile.write(manifestJsonDoc.toJson()) == -1) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot write contents for manifest file: %1").arg(manifestFile.errorString()));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
+template<typename T>
+bool GenericDataCache<T>::remove(const QByteArray &checksum)
+{
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove item from invalidated cache."));
+ return false;
+ }
+ QScopedPointer<T> item(m_items.take(checksum));
+ if (!item) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove item specified by checksum %1: no such item exists.").arg(QLatin1String(checksum)));
+ return false;
+ }
+
+ try {
+ QInstaller::removeDirectory(item->path());
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while removing directory \"%1\": %2").arg(item->path(), e.message()));
+ return false;
+ }
+ return true;
+}
+
+template class GenericDataCache<Metadata>;
+
+} // namespace QInstaller
diff --git a/src/libs/installer/genericdatacache.h b/src/libs/installer/genericdatacache.h
new file mode 100644
index 000000000..94085502c
--- /dev/null
+++ b/src/libs/installer/genericdatacache.h
@@ -0,0 +1,122 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef GENERICDATACACHE_H
+#define GENERICDATACACHE_H
+
+#include "installer_global.h"
+#include "lockfile.h"
+
+#include <QHash>
+#include <QMutex>
+#include <QScopedPointer>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT CacheableItem
+{
+public:
+ CacheableItem() = default;
+ explicit CacheableItem(const QString &path)
+ : m_path(path)
+ {}
+ virtual ~CacheableItem() = 0;
+
+ virtual QString path() const { return m_path; }
+ virtual void setPath(const QString &path) { m_path = path; }
+
+ virtual QByteArray checksum() const = 0;
+ virtual bool isValid() const = 0;
+
+ virtual bool isActive() const = 0;
+ virtual bool obsoletes(CacheableItem *other) = 0;
+
+private:
+ QString m_path;
+};
+
+template <typename T>
+class INSTALLER_EXPORT GenericDataCache
+{
+public:
+ enum RegisterMode {
+ Copy = 0,
+ Move = 1
+ };
+
+ GenericDataCache();
+ explicit GenericDataCache(const QString &path, const QString &type, const QString &version);
+ virtual ~GenericDataCache();
+
+ void setType(const QString &type);
+ void setVersion(const QString &version);
+
+ bool initialize();
+ bool clear();
+ bool sync();
+
+ bool isValid() const;
+ QString errorString() const;
+
+ QString path() const;
+ void setPath(const QString &path);
+
+ QList<T *> items() const;
+ T *itemByChecksum(const QByteArray &checksum) const;
+ T *itemByPath(const QString &path) const;
+
+ bool registerItem(T *item, bool replace = false, RegisterMode mode = Copy);
+ bool removeItem(const QByteArray &checksum);
+
+ QList<T *> obsoleteItems() const;
+
+private:
+ void invalidate();
+ void setErrorString(const QString &error) const;
+
+ bool fromDisk();
+ bool toDisk();
+
+ bool remove(const QByteArray &checksum);
+
+private:
+ QScopedPointer<KDUpdater::LockFile> m_lock;
+ mutable QMutex m_mutex;
+
+ QHash<QByteArray, T *> m_items;
+ QString m_path;
+ QString m_type;
+ QString m_version;
+ mutable QString m_error;
+
+ bool m_invalidated;
+};
+
+} // namespace QInstaller
+
+#endif // GENERICDATACACHE_H
diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp
index adf1d2f6e..3fd084768 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 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";
@@ -60,6 +68,12 @@ namespace QInstaller
\internal
*/
+/*!
+ \fn inline QInstaller::splitStringWithComma(const QString &value())
+ Splits \a value into substrings wherever comma occurs, and returns the list of those strings.
+ \internal
+*/
+
Q_LOGGING_CATEGORY(lcServer, IFW_SERVER)
Q_LOGGING_CATEGORY(lcInstallerInstallLog, IFW_INSTALLER_INSTALLLOG)
Q_LOGGING_CATEGORY(lcProgressIndicator, IFW_PROGRESS_INDICATOR)
@@ -72,17 +86,19 @@ QStringList loggingCategories()
{
static QStringList categories = QStringList()
<< QLatin1String(IFW_INSTALLER_INSTALLLOG)
- << QLatin1String(IFW_SERVER);
+ << QLatin1String(IFW_SERVER)
+ << QLatin1String(IFW_DEVELOPER_BUILD)
+ << QLatin1String("js");
return categories;
}
-Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, staticCommaRegExp, (QLatin1String("(, |,)")));
+Q_GLOBAL_STATIC_WITH_ARGS(QRegularExpression, staticCommaRegExp, (QLatin1String("(, |,)")));
/*!
\internal
*/
-QRegExp commaRegExp()
+QRegularExpression commaRegExp()
{
return *staticCommaRegExp();
}
@@ -111,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 b22331e2c..2d119048b 100644
--- a/src/libs/installer/globals.h
+++ b/src/libs/installer/globals.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,7 +30,7 @@
#include "installer_global.h"
-#include <QRegExp>
+#include <QRegularExpression>
#include <QLoggingCategory>
namespace QInstaller {
@@ -43,11 +43,24 @@ INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcDeveloperBuild)
QStringList INSTALLER_EXPORT loggingCategories();
-QRegExp INSTALLER_EXPORT commaRegExp();
+QRegularExpression INSTALLER_EXPORT commaRegExp();
+inline QStringList splitStringWithComma(const QString &value) {
+ if (!value.isEmpty())
+ return value.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QStringList();
+}
QString htmlToString(const QString &html);
QString enumToString(const QMetaObject& metaObject, const char *enumerator, int key);
+template <typename T, template<typename> typename C>
+QSet<T> toQSet(const C<T> &container)
+{
+ return QSet<T>(container.begin(), container.end());
+}
+
+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 dc085f4b6..6ca50f96f 100644
--- a/src/libs/installer/globalsettingsoperation.cpp
+++ b/src/libs/installer/globalsettingsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -77,7 +77,7 @@ bool GlobalSettingsOperation::performOperation()
bool GlobalSettingsOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
const QStringList args = parsePerformOperationArguments();
@@ -128,7 +128,7 @@ QSettingsWrapper *GlobalSettingsOperation::setup(QString *key, QString *value, c
const QString &filename = arguments.at(0);
*key = arguments.at(1);
*value = arguments.at(2);
- return new QSettingsWrapper(filename, QSettingsWrapper::NativeFormat);
+ return new QSettingsWrapper(filename, QSettings::NativeFormat);
}
return nullptr;
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index 6eebed33b..ff7a0eed2 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -39,13 +39,21 @@ QT += \
widgets \
core-private \
qml-private
-win32:QT += winextras
+
+win32:lessThan(QT_MAJOR_VERSION, 6):QT += winextras
+
+greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat
HEADERS += packagemanagercore.h \
aspectratiolabel.h \
+ calculatorbase.h \
+ componentalias.h \
componentsortfilterproxymodel.h \
concurrentoperationrunner.h \
+ genericdatacache.h \
loggingutils.h \
+ metadata.h \
+ metadatacache.h \
packagemanagercore_p.h \
packagemanagergui.h \
binaryformat.h \
@@ -138,17 +146,23 @@ HEADERS += packagemanagercore.h \
abstractarchive.h \
directoryguard.h \
archivefactory.h \
- operationtracer.h
+ operationtracer.h \
+ customcombobox.h
SOURCES += packagemanagercore.cpp \
abstractarchive.cpp \
archivefactory.cpp \
aspectratiolabel.cpp \
+ calculatorbase.cpp \
+ componentalias.cpp \
concurrentoperationrunner.cpp \
directoryguard.cpp \
fileguard.cpp \
componentsortfilterproxymodel.cpp \
+ genericdatacache.cpp \
loggingutils.cpp \
+ metadata.cpp \
+ metadatacache.cpp \
operationtracer.cpp \
packagemanagercore_p.cpp \
packagemanagergui.cpp \
@@ -222,7 +236,8 @@ SOURCES += packagemanagercore.cpp \
packagesource.cpp \
repositorycategory.cpp \
componentselectionpage_p.cpp \
- commandlineparser.cpp
+ commandlineparser.cpp \
+ customcombobox.cpp
macos:SOURCES += fileutils_mac.mm
diff --git a/src/libs/installer/installer_global.h b/src/libs/installer/installer_global.h
index ea6865042..285eff910 100644
--- a/src/libs/installer/installer_global.h
+++ b/src/libs/installer/installer_global.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,4 +40,10 @@
# define INSTALLER_EXPORT
#endif
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+typedef uint hashValue;
+#else
+typedef size_t hashValue;
+#endif
+
#endif //INSTALLER_GLOBAL_H
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index 2ef5d3b74..4c53824af 100644
--- a/src/libs/installer/installercalculator.cpp
+++ b/src/libs/installer/installercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#include "installercalculator.h"
#include "component.h"
+#include "componentalias.h"
+#include "componentmodel.h"
#include "packagemanagercore.h"
#include "settings.h"
#include <globals.h>
@@ -41,156 +43,188 @@ namespace QInstaller {
\internal
*/
-InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const QList<Component *> &allComponents, const AutoDependencyHash &autoDependencyComponentHash)
- : m_core(core)
- , m_allComponents(allComponents)
+InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash)
+ : CalculatorBase(core)
, m_autoDependencyComponentHash(autoDependencyComponentHash)
{
}
-InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(const Component *c) const
+InstallerCalculator::~InstallerCalculator()
{
- return m_toInstallComponentIdReasonHash.value(c->name()).first;
}
-QString InstallerCalculator::installReason(const Component *component) const
+bool InstallerCalculator::solve()
{
- InstallerCalculator::InstallReasonType reason = installReasonType(component);
+ if (!solve(m_core->aliasesMarkedForInstallation()))
+ return false;
+
+ // Subtract components added by aliases
+ QList<Component *> components = m_core->componentsMarkedForInstallation();
+ for (auto *component : qAsConst(m_resolvedComponents))
+ components.removeAll(component);
+
+ return solve(components);
+}
+
+QString InstallerCalculator::resolutionText(Component *component) const
+{
+ const Resolution reason = resolutionType(component);
switch (reason) {
- case Automatic:
+ case Resolution::Automatic:
return QCoreApplication::translate("InstallerCalculator",
"Components added as automatic dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("InstallerCalculator", "Components added as "
- "dependency for \"%1\":").arg(installReasonReferencedComponent(component));
- case Resolved:
+ "dependency for \"%1\":").arg(referencedComponent(component));
+ case Resolution::Resolved:
return QCoreApplication::translate("InstallerCalculator",
"Components that have resolved dependencies:");
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("InstallerCalculator",
"Selected components without dependencies:");
+ case Resolution::Alias:
+ return QCoreApplication::translate("InstallerCalculator",
+ "Components selected by alias \"%1\":").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid install resolution detected!");
}
return QString();
}
-QList<Component*> InstallerCalculator::orderedComponentsToInstall() const
-{
- return m_orderedComponentsToInstall;
-}
-
-QString InstallerCalculator::componentsToInstallError() const
-{
- return m_componentsToInstallError;
-}
-
-bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components, bool modelReset, const bool revertFromInstall)
+bool InstallerCalculator::solve(const QList<Component *> &components)
{
if (components.isEmpty())
return true;
QList<Component*> notAppendedComponents; // for example components with unresolved dependencies
- foreach (Component *component, components) {
+ for (Component *component : qAsConst(components)){
if (!component)
continue;
- // When model has been reseted, there should not be components with the same name
if (m_toInstallComponentIds.contains(component->name())) {
- if (modelReset) {
- const QString errorMessage = recursionError(component);
- qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
- Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO,
- qPrintable(errorMessage));
- return false;
- }
- if (!revertFromInstall) {
- // We can end up here if component is already added as dependency and
- // user explicitly selects it to install
- continue;
- }
+ const QString errorMessage = recursionError(component);
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+ Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO,
+ qPrintable(errorMessage));
+ return false;
}
if (component->currentDependencies().isEmpty())
- realAppendToInstallComponents(component, QString(), revertFromInstall);
+ addComponentForInstall(component);
else
notAppendedComponents.append(component);
}
- foreach (Component *component, notAppendedComponents) {
- if (!appendComponentToInstall(component, QString(), revertFromInstall))
+ for (Component *component : qAsConst(notAppendedComponents)) {
+ if (!solveComponent(component))
return false;
}
// All regular dependencies are resolved. Now we are looking for auto depend on components.
- QSet<Component *> foundAutoDependOnList = autodependencyComponents(revertFromInstall);
-
+ QSet<Component *> foundAutoDependOnList = autodependencyComponents();
if (!foundAutoDependOnList.isEmpty())
- return appendComponentsToInstall(foundAutoDependOnList.values(), modelReset, revertFromInstall);
+ return solve(foundAutoDependOnList.values());
return true;
}
-bool InstallerCalculator::removeComponentsFromInstall(const QList<Component *> &components)
+bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases)
{
- return appendComponentsToInstall(components, false, true);
+ if (aliases.isEmpty())
+ return true;
+
+ QList<ComponentAlias *> notAppendedAliases; // Aliases that require other aliases
+ for (auto *alias : aliases) {
+ if (!alias)
+ continue;
+
+ if (m_toInstallComponentAliases.contains(alias->name())) {
+ const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
+ "Recursion detected, component alias \"%1\" already added.").arg(alias->name());
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+
+ Q_ASSERT_X(!m_toInstallComponentAliases.contains(alias->name()), Q_FUNC_INFO,
+ qPrintable(errorMessage));
+
+ return false;
+ }
+
+ if (alias->aliases().isEmpty()) {
+ if (!addComponentsFromAlias(alias))
+ return false;
+ } else {
+ notAppendedAliases.append(alias);
+ }
+ }
+
+ for (auto *alias : qAsConst(notAppendedAliases)) {
+ if (!solveAlias(alias))
+ return false;
+ }
+
+ return true;
}
-QString InstallerCalculator::installReasonReferencedComponent(const Component *component) const
+void InstallerCalculator::addComponentForInstall(Component *component, const QString &version)
{
- const QString componentName = component->name();
- if (m_referenceCount.contains(componentName))
- return m_referenceCount.value(componentName).first();
- return m_toInstallComponentIdReasonHash.value(componentName,
- qMakePair(InstallerCalculator::Selected, QString())).second;
+ if (!m_componentsForAutodepencencyCheck.contains(component))
+ m_componentsForAutodepencencyCheck.append(component);
+
+ if (!component->isInstalled(version) || (m_core->isUpdater() && component->isUpdateAvailable())) {
+ m_resolvedComponents.append(component);
+ m_toInstallComponentIds.insert(component->name());
+ }
}
-void InstallerCalculator::insertInstallReason(const Component *component,
- const InstallReasonType installReason, const QString &referencedComponentName, const bool revertFromInstall)
+bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias)
{
- if (revertFromInstall && m_toInstallComponentIdReasonHash.contains(component->name())) {
- m_toInstallComponentIdReasonHash.remove(component->name());
- } else if (!m_toInstallComponentIdReasonHash.contains(component->name())) {
- m_toInstallComponentIdReasonHash.insert(component->name(),
- qMakePair(installReason, referencedComponentName));
+ QList<Component *> componentsToAdd;
+ for (auto *component : alias->components()) {
+ if (m_toInstallComponentIds.contains(component->name()))
+ continue; // Already added
+
+ componentsToAdd.append(component);
+ // Updates the model, so that we also check the descendant
+ // components when calculating components to install
+ updateCheckState(component, Qt::Checked);
+ insertResolution(component, Resolution::Alias, alias->name());
}
+
+ m_toInstallComponentAliases.insert(alias->name());
+ return solve(componentsToAdd);
}
-void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall)
+QString InstallerCalculator::recursionError(Component *component) const
{
- if (!m_componentsForAutodepencencyCheck.contains(component))
- m_componentsForAutodepencencyCheck.append(component);
- if (revertFromInstall) {
- if (m_toInstallComponentIds.contains(component->name())) {
- // Component is no longer added as dependency and is not explicitly selected for install by user
- if (m_referenceCount.value(component->name()).isEmpty() && !component->isSelected()) {
- m_toInstallComponentIds.remove(component->name());
- m_orderedComponentsToInstall.removeAll(component);
- }
- }
- } else {
- if (!component->isInstalled(version)
- || (m_core->isUpdater() && component->isUpdateAvailable())) {
- m_toInstallComponentIds.insert(component->name());
- m_orderedComponentsToInstall.append(component);
- }
- }
+ return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
+ "already added with reason: \"%2\"").arg(component->name(), resolutionText(component));
+}
+
+bool InstallerCalculator::updateCheckState(Component *component, Qt::CheckState state)
+{
+ ComponentModel *currentModel = m_core->isUpdater()
+ ? m_core->updaterComponentModel()
+ : m_core->defaultComponentModel();
+
+ const QModelIndex &idx = currentModel->indexFromComponentName(component->treeName());
+ return currentModel->setData(idx, state, Qt::CheckStateRole);
}
-bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version, bool revertFromInstall)
+bool InstallerCalculator::solveComponent(Component *component, const QString &version)
{
const QStringList dependenciesList = component->currentDependencies();
- QSet<QString> allDependencies(dependenciesList.begin(), dependenciesList.end());
QString requiredDependencyVersion = version;
- foreach (const QString &dependencyComponentName, allDependencies) {
+ for (const QString &dependencyComponentName : dependenciesList) {
// PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a
// version which is not available
- Component *dependencyComponent =
- PackageManagerCore::componentByName(dependencyComponentName, m_allComponents);
+ Component *dependencyComponent = m_core->componentByName(dependencyComponentName);
if (!dependencyComponent) {
const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
"Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName,
component->name());
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
+ m_errorString.append(errorMessage);
if (component->packageManagerCore()->settings().allowUnstableComponents()) {
component->setUnstable(Component::UnstableError::MissingDependency, errorMessage);
continue;
@@ -198,8 +232,6 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
return false;
}
}
- if (revertFromInstall && dependencyComponent->forcedInstallation())
- continue;
//Check if component requires higher version than what might be already installed
bool isUpdateRequired = false;
QString requiredName;
@@ -207,87 +239,65 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
PackageManagerCore::parseNameAndVersion(dependencyComponentName, &requiredName, &requiredVersion);
if (!requiredVersion.isEmpty() &&
!dependencyComponent->value(scInstalledVersion).isEmpty()) {
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ?
- compEx.cap(2) : dependencyComponent->value(scInstalledVersion);
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ QRegularExpressionMatch match = compEx.match(dependencyComponent->value(scInstalledVersion));
+ const QString installedVersion = match.hasMatch()
+ ? match.captured(2) : dependencyComponent->value(scInstalledVersion);
- requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion;
+ match = compEx.match(requiredVersion);
+ requiredVersion = match.hasMatch() ? match.captured(2) : requiredVersion;
if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) {
isUpdateRequired = true;
requiredDependencyVersion = requiredVersion;
}
}
-
- // Component can be requested for install by several component (as a dependency).
- // Keep the reference count to a component, so we know when component needs to be
- // removed from install.
- if (!revertFromInstall && m_toInstallComponentIds.contains(dependencyComponentName)) {
- QStringList value = m_referenceCount.value(dependencyComponentName, QStringList());
- if (!value.contains(component->name()))
- value << component->name();
- m_referenceCount.insert(dependencyComponentName, value);
- }
- if (revertFromInstall) {
- if (m_toInstallComponentIds.contains(dependencyComponentName)
- && m_referenceCount.contains(dependencyComponentName)) {
- QStringList value = m_referenceCount.value(dependencyComponentName);
- if (value.contains(component->name()))
- value.removeOne(component->name());
- if (value.isEmpty())
- m_referenceCount.remove(dependencyComponentName);
- else
- m_referenceCount.insert(dependencyComponentName, value);
- }
- insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name(), true);
- if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall))
- return false;
- m_visitedComponents.remove(component);
- }
-
//Check dependencies only if
//- Dependency is not installed or update requested, nor newer version of dependency component required
//- And dependency component is not already added for install
//- And component is not already added for install, then dependencies are already resolved
- if (!revertFromInstall && ((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested())
+ if (((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested())
|| isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name())
&& !m_toInstallComponentIds.contains(component->name()))) {
if (m_visitedComponents.value(component).contains(dependencyComponent)) {
const QString errorMessage = recursionError(component);
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError = errorMessage;
+ m_errorString = errorMessage;
Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent),
Q_FUNC_INFO, qPrintable(errorMessage));
return false;
}
m_visitedComponents[component].insert(dependencyComponent);
+ // add needed dependency components to the next run
+ insertResolution(dependencyComponent, Resolution::Dependent,
+ component->name());
- QStringList value = m_referenceCount.value(dependencyComponentName, QStringList());
- if (!value.contains(component->name()))
- value << component->name();
- m_referenceCount.insert(dependencyComponentName, value);
- insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name());
- if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall))
+ if (!solveComponent(dependencyComponent, requiredDependencyVersion))
return false;
}
}
- if (!revertFromInstall && !m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall);
- insertInstallReason(component, InstallerCalculator::Resolved);
- }
- if (revertFromInstall && m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall);
+
+ if (!m_toInstallComponentIds.contains(component->name())) {
+ addComponentForInstall(component, requiredDependencyVersion);
+ insertResolution(component, Resolution::Resolved);
}
return true;
}
-QString InstallerCalculator::recursionError(const Component *component) const
+bool InstallerCalculator::solveAlias(ComponentAlias *alias)
{
- return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
- "already added with reason: \"%2\"").arg(component->name(), installReason(component));
+ for (auto *requiredAlias : alias->aliases()) {
+ if (!solveAlias(requiredAlias))
+ return false;
+ }
+
+ if (m_toInstallComponentAliases.contains(alias->name()))
+ return true;
+
+ return addComponentsFromAlias(alias);
}
-QSet<Component *> InstallerCalculator::autodependencyComponents(const bool revertFromInstall)
+QSet<Component *> InstallerCalculator::autodependencyComponents()
{
// All regular dependencies are resolved. Now we are looking for auto depend on components.
// m_componentsForAutodepencencyCheck is a list of recently calculated components to be installed
@@ -295,16 +305,16 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever
// dependency components based on that list.
QSet<Component *> foundAutoDependOnList;
for (const Component *component : qAsConst(m_componentsForAutodepencencyCheck)) {
- if (!m_autoDependencyComponentHash.contains(component->name()))
+ if (!m_autoDependencyComponentHash.contains(component->name())
+ || (m_core->isUpdater() && !component->updateRequested()))
continue;
for (const QString& autoDependency : m_autoDependencyComponentHash.value(component->name())) {
// If a components is already installed or is scheduled for installation, no need to check
// for auto depend installation.
- if ((!revertFromInstall && m_toInstallComponentIds.contains(autoDependency))
- || (revertFromInstall && !m_toInstallComponentIds.contains(autoDependency))) {
+ if (m_toInstallComponentIds.contains(autoDependency)) {
continue;
}
- Component *autoDependComponent = PackageManagerCore::componentByName(autoDependency, m_allComponents);
+ Component *autoDependComponent = m_core->componentByName(autoDependency);
if (!autoDependComponent)
continue;
if ((!autoDependComponent->isInstalled()
@@ -314,10 +324,8 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever
// are other autodependencies as well
if (autoDependComponent->isAutoDependOn(m_toInstallComponentIds)) {
foundAutoDependOnList.insert(autoDependComponent);
- insertInstallReason(autoDependComponent, InstallerCalculator::Automatic);
+ insertResolution(autoDependComponent, Resolution::Automatic);
}
- } else if (revertFromInstall && m_toInstallComponentIds.contains(autoDependComponent->name())) {
- foundAutoDependOnList.insert(autoDependComponent);
}
}
}
diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h
index 21b3775eb..e542dc664 100644
--- a/src/libs/installer/installercalculator.h
+++ b/src/libs/installer/installercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "installer_global.h"
#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -39,58 +40,41 @@
namespace QInstaller {
class Component;
+class ComponentAlias;
+class PackageManagerCore;
-class INSTALLER_EXPORT InstallerCalculator
+class INSTALLER_EXPORT InstallerCalculator : public CalculatorBase
{
public:
- InstallerCalculator(PackageManagerCore *core, const QList<Component *> &allComponents, const AutoDependencyHash &autoDependencyComponentHash);
+ InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash);
+ ~InstallerCalculator();
- enum InstallReasonType
- {
- Selected, // "Selected Component(s) without Dependencies"
- Automatic, // "Component(s) added as automatic dependencies"
- Dependent, // "Added as dependency for %1."
- Resolved // "Component(s) that have resolved Dependencies"
- };
+ bool solve();
+ bool solve(const QList<Component *> &components) override;
+ bool solve(const QList<ComponentAlias *> &aliases);
- InstallReasonType installReasonType(const Component *c) const;
- QString installReason(const Component *component) const;
- QList<Component*> orderedComponentsToInstall() const;
- QString componentsToInstallError() const;
-
- bool appendComponentsToInstall(const QList<Component*> &components, bool modelReset = false, const bool revertFromInstall = false);
- bool removeComponentsFromInstall(const QList<Component*> &components);
+ QString resolutionText(Component *component) const override;
private:
- QString installReasonReferencedComponent(const Component *component) const;
- void insertInstallReason(const Component *component,
- const InstallReasonType installReason,
- const QString &referencedComponentName = QString(),
- const bool revertFromInstall = false);
- void realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall);
- bool appendComponentToInstall(Component *component, const QString &version, const bool revertFromInstall);
- QString recursionError(const Component *component) const;
- QSet<Component *> autodependencyComponents(const bool revertFromInstall);
+ bool solveComponent(Component *component, const QString &version = QString()) override;
+ bool solveAlias(ComponentAlias *alias);
+
+ void addComponentForInstall(Component *component, const QString &version = QString());
+ bool addComponentsFromAlias(ComponentAlias *alias);
+ QSet<Component *> autodependencyComponents();
+ QString recursionError(Component *component) const;
+
+ bool updateCheckState(Component *component, Qt::CheckState state);
private:
- PackageManagerCore *m_core;
- QList<Component*> m_allComponents;
QHash<Component*, QSet<Component*> > m_visitedComponents;
QList<const Component*> m_componentsForAutodepencencyCheck;
- //for faster lookups.
- QSet<QString> m_toInstallComponentIds;
- QHash<QString, QStringList> m_referenceCount;
- QString m_componentsToInstallError;
- //calculate installation order variables
- QList<Component*> m_orderedComponentsToInstall;
- //we can't use this reason hash as component id hash, because some reasons are ready before
- //the component is added
- QHash<QString, QPair<InstallReasonType, QString> > m_toInstallComponentIdReasonHash;
+ QSet<QString> m_toInstallComponentIds; //for faster lookups
+ QSet<QString> m_toInstallComponentAliases;
//Helper hash for quicker search for autodependency components
AutoDependencyHash m_autoDependencyComponentHash;
};
-}
-
+} // namespace QInstaller
#endif // INSTALLERCALCULATOR_H
diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp
index 5927ebf74..b21634cd7 100644
--- a/src/libs/installer/installiconsoperation.cpp
+++ b/src/libs/installer/installiconsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -57,7 +57,7 @@ QString InstallIconsOperation::targetDirectory()
Qt::SkipEmptyParts);
XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path
- if (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive())
+ if (packageManager() && packageManager()->hasAdminRights())
XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path
QString directory;
diff --git a/src/libs/installer/lib7z_create.h b/src/libs/installer/lib7z_create.h
index 9e920aa55..b908b028f 100644
--- a/src/libs/installer/lib7z_create.h
+++ b/src/libs/installer/lib7z_create.h
@@ -35,9 +35,10 @@
#include <Common/MyCom.h>
#include <7zip/UI/Common/Update.h>
+#include <QStringList>
+
QT_BEGIN_NAMESPACE
class QFileDevice;
-class QStringList;
QT_END_NAMESPACE
namespace Lib7z
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
index bd08c68c5..e0d8a53e4 100644
--- a/src/libs/installer/lib7z_facade.cpp
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -363,7 +363,7 @@ static quint32 getUInt32Property(IInArchive *archive, int index, int propId, qui
static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions)
{
quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0);
- QFile::Permissions permissions = nullptr;
+ QFile::Permissions permissions = QFile::Permissions();
if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
if (hasPermissions != nullptr)
*hasPermissions = true;
diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp
index 4573da2b1..233a4c28b 100644
--- a/src/libs/installer/libarchivearchive.cpp
+++ b/src/libs/installer/libarchivearchive.cpp
@@ -276,7 +276,7 @@ void ExtractWorker::extract(const QString &dirPath, const quint64 totalFiles)
LibArchiveArchive::configureReader(reader.get());
LibArchiveArchive::configureDiskWriter(writer.get());
- DirectoryGuard targetDir(QFileInfo(dirPath).absolutePath());
+ DirectoryGuard targetDir(QFileInfo(dirPath).absoluteFilePath());
try {
const QStringList createdDirs = targetDir.tryCreate();
// Make sure that all leading directories created get removed as well
@@ -622,7 +622,7 @@ bool LibArchiveArchive::extract(const QString &dirPath, const quint64 totalFiles
configureReader(reader.get());
configureDiskWriter(writer.get());
- DirectoryGuard targetDir(QFileInfo(dirPath).absolutePath());
+ DirectoryGuard targetDir(QFileInfo(dirPath).absoluteFilePath());
try {
const QStringList createdDirs = targetDir.tryCreate();
// Make sure that all leading directories created get removed as well
@@ -739,7 +739,7 @@ bool LibArchiveArchive::create(const QStringList &data)
dataEntry_w.get()[dataEntry.length()] = '\0';
if ((status = archive_read_disk_open_w(reader.get(), dataEntry_w.get()))) {
- throw Error(tr("Cannot open file \"%1\"for reading: %2")
+ throw Error(tr("Cannot open file \"%1\" for reading: %2")
.arg(dataEntry, errorStringWithCode(reader.get())));
}
#else
@@ -832,7 +832,7 @@ QVector<ArchiveEntry> LibArchiveArchive::list()
ArchiveEntry archiveEntry;
archiveEntry.path = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry);
- archiveEntry.utcTime = QDateTime::fromTime_t(archive_entry_mtime(entry));
+ archiveEntry.utcTime = QDateTime::fromSecsSinceEpoch(archive_entry_mtime(entry));
archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR);
archiveEntry.isSymbolicLink = (archive_entry_filetype(entry) == AE_IFLNK);
archiveEntry.uncompressedSize = archive_entry_size(entry);
diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp
index e5c1e8598..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.
@@ -105,7 +107,7 @@ void LibArchiveWrapperPrivate::setFilename(const QString &filename)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilename), filename, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilename), filename);
m_lock.unlock();
}
m_archive.setFilename(filename);
@@ -145,7 +147,7 @@ bool LibArchiveWrapperPrivate::extract(const QString &dirPath, const quint64 tot
timer.start();
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total);
m_lock.unlock();
{
QEventLoop loop;
@@ -203,7 +205,7 @@ void LibArchiveWrapperPrivate::setCompressionLevel(const AbstractArchive::Compre
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level);
m_lock.unlock();
return;
}
@@ -219,7 +221,7 @@ void LibArchiveWrapperPrivate::cancel()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveCancel));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveCancel));
m_lock.unlock();
return;
}
@@ -354,7 +356,7 @@ void LibArchiveWrapperPrivate::addDataBlock(const QByteArray &buffer)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer);
m_lock.unlock();
}
}
@@ -367,7 +369,7 @@ void LibArchiveWrapperPrivate::setClientDataAtEnd()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
m_lock.unlock();
}
}
@@ -379,7 +381,7 @@ void LibArchiveWrapperPrivate::setClientFilePosition(qint64 pos)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos);
m_lock.unlock();
}
}
diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp
index a5a61c71f..0d30ab514 100644
--- a/src/libs/installer/licenseoperation.cpp
+++ b/src/libs/installer/licenseoperation.cpp
@@ -31,6 +31,7 @@
#include "packagemanagercore.h"
#include "settings.h"
#include "fileutils.h"
+#include "globals.h"
#include <QtCore/QDir>
#include <QtCore/QFile>
@@ -56,7 +57,7 @@ void LicenseOperation::backup()
bool LicenseOperation::performOperation()
{
- QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ QVariantMap licenses = value(scLicensesValue).toMap();
if (licenses.isEmpty()) {
setError(UserDefinedError);
setErrorString(tr("No license files found to copy."));
@@ -80,14 +81,17 @@ bool LicenseOperation::performOperation()
for (QVariantMap::const_iterator it = licenses.constBegin(); it != licenses.constEnd(); ++it) {
QFile file(targetDir + QLatin1Char('/') + it.key());
- if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
setError(UserDefinedError);
setErrorString(tr("Can not write license file \"%1\".").arg(QDir::toNativeSeparators(file.fileName())));
return false;
}
- QTextStream stream(&file);
+ QString outString;
+ QTextStream stream(&outString);
stream << it.value().toString();
+
+ file.write(outString.toUtf8());
}
return true;
@@ -95,11 +99,10 @@ bool LicenseOperation::performOperation()
bool LicenseOperation::undoOperation()
{
- const QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ const QVariantMap licenses = value(scLicensesValue).toMap();
if (licenses.isEmpty()) {
- setError(UserDefinedError);
- setErrorString(tr("No license files found to delete."));
- return false;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "No license files found to delete.";
+ return true;
}
QString targetDir = arguments().value(0);
diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp
index ff9f49696..027dac232 100644
--- a/src/libs/installer/link.cpp
+++ b/src/libs/installer/link.cpp
@@ -263,7 +263,7 @@ QString Link::targetPath() const
#ifdef Q_OS_WIN
return readWindowsSymLink(m_path);
#else
- return QFileInfo(m_path).readLink();
+ return QFileInfo(m_path).symLinkTarget();
#endif
}
diff --git a/src/libs/installer/loggingutils.cpp b/src/libs/installer/loggingutils.cpp
index 8c542a1d1..9a36720dd 100644
--- a/src/libs/installer/loggingutils.cpp
+++ b/src/libs/installer/loggingutils.cpp
@@ -29,8 +29,10 @@
#include "loggingutils.h"
#include "component.h"
+#include "componentalias.h"
#include "globals.h"
#include "fileutils.h"
+#include "packagemanagercore.h"
#include "remoteclient.h"
#include "remotefileengine.h"
@@ -110,6 +112,7 @@ public:
*/
LoggingHandler::LoggingHandler()
: m_verbLevel(VerbosityLevel::Silent)
+ , m_outputRedirected(false)
{
#if defined(Q_OS_UNIX)
m_outputRedirected = !isatty(fileno(stdout));
@@ -251,7 +254,7 @@ bool LoggingHandler::outputRedirected() const
/*!
Prints update information from \a components.
*/
-void LoggingHandler::printUpdateInformation(const QList<Component *> components) const
+void LoggingHandler::printUpdateInformation(const QList<Component *> &components) const
{
QString output;
QXmlStreamWriter stream(&output);
@@ -365,6 +368,44 @@ void LoggingHandler::printPackageInformation(const PackagesList &matchedPackages
}
/*!
+ Prints basic or more detailed information about component \a aliases,
+ depending on the current verbosity level.
+*/
+void LoggingHandler::printAliasInformation(const QList<ComponentAlias *> &aliases)
+{
+ QList<ComponentAlias *> sortedAliases = aliases;
+ std::sort(sortedAliases.begin(), sortedAliases.end(),
+ [](const ComponentAlias *lhs, const ComponentAlias *rhs) {
+ return lhs->name() < rhs->name();
+ }
+ );
+
+ QString output;
+ QTextStream stream(&output);
+
+ stream << Qt::endl;
+ for (auto *alias : qAsConst(sortedAliases)) {
+ stream << "Name: " << alias->name() << Qt::endl;
+ stream << "Display name: " << alias->displayName() << Qt::endl;
+ stream << "Description: " << alias->description() << Qt::endl;
+ stream << "Version: " << alias->version() << Qt::endl;
+ if (verboseLevel() == VerbosityLevel::Detailed)
+ stream << "Virtual: " << alias->value(scVirtual) << Qt::endl;
+
+ stream << "Components: " << alias->value(scRequiredComponents) << Qt::endl;
+ stream << "Required aliases: " << alias->value(scRequiredAliases) << Qt::endl;
+
+ stream << "Optional components: " << alias->value(scOptionalComponents) << Qt::endl;
+ stream << "Optional aliases: " << alias->value(scOptionalAliases) << Qt::endl;
+
+ if (sortedAliases.indexOf(alias) != (sortedAliases.count() - 1))
+ stream << "========================================" << Qt::endl;
+ }
+
+ std::cout << qPrintable(output);
+}
+
+/*!
\internal
*/
VerboseWriter::VerboseWriter()
@@ -441,7 +482,7 @@ VerboseWriter *VerboseWriter::instance()
*/
void VerboseWriter::appendLine(const QString &msg)
{
- m_stream << msg << endl;
+ m_stream << msg << Qt::endl;
}
/*!
diff --git a/src/libs/installer/loggingutils.h b/src/libs/installer/loggingutils.h
index f41a18663..18ff2d2c5 100644
--- a/src/libs/installer/loggingutils.h
+++ b/src/libs/installer/loggingutils.h
@@ -41,6 +41,7 @@
namespace QInstaller {
class Component;
+class ComponentAlias;
class INSTALLER_EXPORT LoggingHandler
{
@@ -64,9 +65,10 @@ public:
VerbosityLevel verboseLevel() const;
bool outputRedirected() const;
- void printUpdateInformation(const QList<Component *> components) const;
+ void printUpdateInformation(const QList<Component *> &components) const;
void printLocalPackageInformation(const QList<KDUpdater::LocalPackage> &packages) const;
void printPackageInformation(const PackagesList &matchedPackages, const LocalPackagesMap &installedPackages) const;
+ void printAliasInformation(const QList<ComponentAlias *> &aliases);
friend VerbosityLevel &operator++(VerbosityLevel &level, int);
friend VerbosityLevel &operator--(VerbosityLevel &level, int);
diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp
index e2d461ba8..052709e51 100644
--- a/src/libs/installer/messageboxhandler.cpp
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -368,6 +368,9 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton defaultButton)
{
QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
+ 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
new file mode 100644
index 000000000..2eccb020e
--- /dev/null
+++ b/src/libs/installer/metadata.cpp
@@ -0,0 +1,410 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "metadata.h"
+
+#include "constants.h"
+#include "globals.h"
+#include "metadatajob.h"
+
+#include <QCryptographicHash>
+#include <QDir>
+#include <QDomDocument>
+#include <QFile>
+#include <QByteArrayMatcher>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Metadata
+ \brief The Metadata class represents fetched metadata from a repository.
+*/
+
+
+/*!
+ \internal
+*/
+static bool verifyFileIntegrityFromElement(const QDomElement &element, const QString &childNodeName,
+ const QString &attribute, const QString &metaDirectory, bool testChecksum)
+{
+ const QDomNodeList nodes = element.childNodes();
+ for (int i = 0; i < nodes.count(); ++i) {
+ const QDomNode node = nodes.at(i);
+ if (node.nodeName() != childNodeName)
+ continue;
+
+ const QDir dir(metaDirectory);
+ const QString filename = attribute.isEmpty()
+ ? node.toElement().text()
+ : node.toElement().attribute(attribute);
+
+ if (filename.isEmpty())
+ continue;
+
+ QFile file(dir.absolutePath() + QDir::separator() + filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+
+ if (!testChecksum)
+ continue;
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray checksum = hash.result().toHex();
+ if (!QFileInfo::exists(dir.absolutePath() + QDir::separator()
+ + QString::fromLatin1(checksum) + QLatin1String(".sha1"))) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unexpected checksum for file" << file.fileName();
+ return false;
+ }
+ }
+ return true;
+}
+
+/*!
+ Constructs a new metadata object.
+*/
+Metadata::Metadata()
+ : CacheableItem()
+ , m_fromDefaultRepository(false)
+{
+}
+
+/*!
+ Constructs a new metadata object with a \a path.
+*/
+Metadata::Metadata(const QString &path)
+ : CacheableItem(path)
+ , m_fromDefaultRepository(false)
+{
+}
+
+/*!
+ Returns the checksum of this metadata which is the checksum of the Updates.xml file.
+ The checksum value is stored to memory after first read, so a single object should
+ not be reused for referring other metadata.
+*/
+QByteArray Metadata::checksum() const
+{
+ if (!m_checksum.isEmpty())
+ return m_checksum;
+
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly))
+ return QByteArray();
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&updateFile);
+ m_checksum = hash.result().toHex();
+
+ return m_checksum;
+}
+
+/*!
+ Sets the checksum of this metadata to \a checksum. Calling this function
+ will omit calculating the checksum from the update file when retrieving
+ the checksum with \l{checksum()} for the first time.
+*/
+void Metadata::setChecksum(const QByteArray &checksum)
+{
+ m_checksum = checksum;
+}
+
+/*!
+ Returns the root of the document tree representing the \c Updates.xml
+ document of this metadata. Returns an empty \c QDomDocument in case
+ of failure to reading the file.
+*/
+QDomDocument Metadata::updatesDocument() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return QDomDocument();
+ }
+
+ QDomDocument doc;
+ QString errorString;
+ if (!doc.setContent(&updateFile, &errorString)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot set document content:" << errorString;
+ return QDomDocument();
+ }
+
+ return doc;
+}
+
+/*!
+ Returns \c true if the \c Updates.xml document of this metadata exists, and that all
+ meta files referenced in the document exist. If the \c Updates.xml contains a \c Checksum
+ element with a value of \c true, the integrity of the files is also verified.
+
+ Returns \c false otherwise.
+*/
+bool Metadata::isValid() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return false;
+ }
+
+ return verifyMetaFiles(&updateFile);
+}
+
+/*!
+ Returns \c true if this metadata is active, \c false otherwise. Metadata is
+ considered active if it is currently associated with a valid repository.
+*/
+bool Metadata::isActive() const
+{
+ return m_repository.isValid();
+}
+
+/*!
+ Checks whether this metadata object obsoletes the \a other metadata. The other metadata
+ is considered obsolete if it is not currently associated with any repository, and the
+ URL of the calling metadata matches the last known URL of the other metadata. Returns
+ \c true if the current metadata obsoletes the other, \c false otherwise.
+*/
+bool Metadata::obsoletes(CacheableItem *other)
+{
+ if (Metadata *meta = dynamic_cast<Metadata *>(other)) {
+ // If the current metadata is not in use it should not replace anything.
+ if (!isActive())
+ return false;
+
+ // If the other metadata is in use it is not obsolete.
+ if (meta->isActive())
+ return false;
+
+ // Current metadata has the same persistent url as other metadata, other is obsolete.
+ if (persistentRepositoryPath() == meta->persistentRepositoryPath())
+ return true;
+
+ // The refreshed url of the current metadata matches the persistent url of other
+ // metadata, other is obsolete.
+ if (m_repository.url().path(QUrl::FullyEncoded) == meta->persistentRepositoryPath())
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the repository object set for this metadata. This is the repository
+ the metadata is currently associated with, which may be different from the
+ repository where it was originally fetched.
+*/
+Repository Metadata::repository() const
+{
+ return m_repository;
+}
+
+/*!
+ Sets the \a repository object of this metadata. If a repository
+ is already set, the new one will override the previous one. The metadata becomes
+ associated with the set repository even if it was fetched from another one.
+*/
+void Metadata::setRepository(const Repository &repository)
+{
+ m_repository = repository;
+}
+
+/*!
+ Returns \c true if this metadata is available from a default repository,
+ which means a repository without category, \c false otherwise.
+*/
+bool Metadata::isAvailableFromDefaultRepository() const
+{
+ return m_fromDefaultRepository;
+}
+
+/*!
+ Sets the metadata available from a default repository based on the value
+ of \a defaultRepository. This is not mutually exclusive from a metadata
+ that has repository categories set.
+*/
+void Metadata::setAvailableFromDefaultRepository(bool defaultRepository)
+{
+ m_fromDefaultRepository = defaultRepository;
+}
+
+/*!
+ Sets the repository path of this metadata from \a url, without the protocol or hostname.
+ Unlike \l{setRepository()} this value is saved to disk, which allows retrieving the
+ repository path of the metadata on later runs.
+*/
+void Metadata::setPersistentRepositoryPath(const QUrl &url)
+{
+ const QString newPath = url.path(QUrl::FullyEncoded).trimmed();
+ if (m_persistentRepositoryPath == newPath)
+ return;
+
+ QFile file(path() + QLatin1String("/repository.txt"));
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName() << "for writing:" << file.errorString();
+ return;
+ }
+ QTextStream out(&file);
+ out << newPath;
+
+ m_persistentRepositoryPath = newPath;
+}
+
+/*!
+ Returns the persistent repository path of the metadata.
+*/
+QString Metadata::persistentRepositoryPath()
+{
+ if (!m_persistentRepositoryPath.isEmpty())
+ return m_persistentRepositoryPath;
+
+ QFile file(path() + QLatin1String("/repository.txt"));
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName() << "for reading:" << file.errorString();
+ return QString();
+ }
+ m_persistentRepositoryPath = QString::fromLatin1(file.readAll()).trimmed();
+ return m_persistentRepositoryPath;
+}
+
+/*!
+ Returns true if the updates document of this metadata contains the repository
+ update element, which can include actions to \c add, \c remove, and \c replace
+ repositories.
+
+ \note This function does not check that the repository updates are actually
+ valid, only that the updates document contains the \c RepositoryUpdate element.
+*/
+bool Metadata::containsRepositoryUpdates() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return false;
+ }
+
+ static const auto matcher = qMakeStaticByteArrayMatcher("<RepositoryUpdate>");
+ while (!updateFile.atEnd()) {
+ const QByteArray line = updateFile.readLine().simplified();
+ if (matcher.indexIn(line) != -1)
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Verifies that the files referenced in \a updateFile document exist
+ on disk. If the document contains a \c Checksum element with a value
+ of \c true, the integrity of the files is also verified.
+
+ Returns \c true if the meta files are valid, \c false otherwise.
+*/
+bool Metadata::verifyMetaFiles(QFile *updateFile) const
+{
+ QDomDocument doc;
+ QString errorString;
+ if (!doc.setContent(updateFile, &errorString)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot set document content:" << errorString;
+ return false;
+ }
+
+ const QDomElement rootElement = doc.documentElement();
+ const QDomNodeList childNodes = rootElement.childNodes();
+
+ bool testChecksum = true;
+ const QDomElement checksumElement = rootElement.firstChildElement(QLatin1String("Checksum"));
+ if (!checksumElement.isNull())
+ testChecksum = (checksumElement.text().toLower() == scTrue);
+
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomElement element = childNodes.at(i).toElement();
+ if (element.isNull() || element.tagName() != QLatin1String("PackageUpdate"))
+ continue;
+
+ const QDomNodeList c2 = element.childNodes();
+ QString packageName;
+ QString unused1;
+ QString unused2;
+
+ // Only need the package name, so values for "online" and "testCheckSum" do not matter
+ if (!MetadataJob::parsePackageUpdate(c2, packageName, unused1, unused2, true, true))
+ continue; // nothing to check for this package
+
+ const QString packagePath = QString::fromLatin1("%1/%2/").arg(path(), packageName);
+ for (auto &metaTagName : scMetaElements) {
+ const QDomElement metaElement = element.firstChildElement(metaTagName);
+ if (metaElement.isNull())
+ continue;
+
+ if (metaElement.tagName() == QLatin1String("Licenses")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("License"),
+ QLatin1String("file"), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("UserInterfaces")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("UserInterface"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Translations")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("Translation"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Script")) {
+ if (!verifyFileIntegrityFromElement(metaElement.parentNode().toElement(),
+ QLatin1String("Script"), QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown meta element.");
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/metadata.h b/src/libs/installer/metadata.h
new file mode 100644
index 000000000..c7e4e857c
--- /dev/null
+++ b/src/libs/installer/metadata.h
@@ -0,0 +1,81 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef METADATA_H
+#define METADATA_H
+
+#include "installer_global.h"
+#include "genericdatacache.h"
+#include "repository.h"
+
+#include <QDomDocument>
+
+class QFile;
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT Metadata : public CacheableItem
+{
+public:
+ Metadata();
+ explicit Metadata(const QString &path);
+ ~Metadata() {}
+
+ QByteArray checksum() const override;
+ void setChecksum(const QByteArray &checksum);
+ QDomDocument updatesDocument() const;
+
+ bool isValid() const override;
+ bool isActive() const override;
+ bool obsoletes(CacheableItem *other) override;
+
+ Repository repository() const;
+ void setRepository(const Repository &repository);
+
+ bool isAvailableFromDefaultRepository() const;
+ void setAvailableFromDefaultRepository(bool defaultRepository);
+
+ void setPersistentRepositoryPath(const QUrl &url);
+ QString persistentRepositoryPath();
+
+ bool containsRepositoryUpdates() const;
+
+private:
+ bool verifyMetaFiles(QFile *updateFile) const;
+
+private:
+ Repository m_repository;
+ QString m_persistentRepositoryPath;
+ mutable QByteArray m_checksum;
+
+ bool m_fromDefaultRepository;
+};
+
+} // namespace QInstaller
+
+#endif // METADATA_H
diff --git a/src/libs/installer/metadatacache.cpp b/src/libs/installer/metadatacache.cpp
new file mode 100644
index 000000000..744e455f4
--- /dev/null
+++ b/src/libs/installer/metadatacache.cpp
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "metadatacache.h"
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::MetadataCache
+ \brief The MetadataCache is a class for a checksum based storage of \c Metadata objects on disk.
+
+ MetadataCache manages a cache storage for a set \l{path()}, which contains
+ a subdirectory for each registered \c Metadata item. The cache has a manifest file in
+ its root directory, which lists the version and type of the cache, and all its items.
+ The file is updated automatically when the metadata cache object is destructed, or
+ it can be updated periodically by calling \l{sync()}.
+*/
+
+/*!
+ Constructs a new empty cache. The cache is invalid until set with a
+ path and initialized.
+*/
+MetadataCache::MetadataCache()
+ : GenericDataCache<Metadata>()
+{
+ setType(QLatin1String("Metadata"));
+ setVersion(QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)));
+}
+
+/*!
+ Constructs a cache to \a path. The cache is initialized automatically.
+*/
+MetadataCache::MetadataCache(const QString &path)
+ : GenericDataCache(path, QLatin1String("Metadata"), QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)))
+{
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/metadatacache.h b/src/libs/installer/metadatacache.h
new file mode 100644
index 000000000..804d1b6db
--- /dev/null
+++ b/src/libs/installer/metadatacache.h
@@ -0,0 +1,46 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef METADATACACHE_H
+#define METADATACACHE_H
+
+#include "genericdatacache.h"
+#include "metadata.h"
+
+namespace QInstaller {
+
+class MetadataCache : public GenericDataCache<Metadata>
+{
+public:
+ MetadataCache();
+ explicit MetadataCache(const QString &path);
+};
+
+} // namespace QInstaller
+
+#endif // METADATACACHE_H
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index 50fd723ab..1bed304c6 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.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.
@@ -38,31 +38,18 @@
#include "globals.h"
#include <QTemporaryDir>
+#include <QtConcurrent>
#include <QtMath>
#include <QRandomGenerator>
-
-const QStringList metaElements = {QLatin1String("Script"), QLatin1String("Licenses"), QLatin1String("UserInterfaces"), QLatin1String("Translations")};
+#include <QApplication>
namespace QInstaller {
/*!
- \inmodule QtInstallerFramework
- \class QInstaller::Metadata
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
- \class QInstaller::ArchiveMetadata
- \internal
-*/
-
-/*!
\enum QInstaller::DownloadType
\value All
\value CompressedPackage
- \value UpdatesXML
*/
/*!
@@ -97,6 +84,7 @@ MetadataJob::MetadataJob(QObject *parent)
, m_downloadType(DownloadType::All)
, m_downloadableChunkSize(1000)
, m_taskNumber(0)
+ , m_defaultRepositoriesFetched(false)
{
QByteArray downloadableChunkSize = qgetenv("IFW_METADATA_SIZE");
if (!downloadableChunkSize.isEmpty()) {
@@ -109,12 +97,19 @@ MetadataJob::MetadataJob(QObject *parent)
connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished);
connect(&m_metadataTask, &QFutureWatcherBase::finished, this, &MetadataJob::metadataTaskFinished);
connect(&m_metadataTask, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged);
+ connect(&m_updateCacheTask, &QFutureWatcherBase::finished, this, &MetadataJob::updateCacheTaskFinished);
}
MetadataJob::~MetadataJob()
{
resetCompressedFetch();
reset();
+
+ if (!m_core)
+ return;
+
+ if (m_metaFromCache.isValid() && !m_core->settings().persistentLocalCache())
+ m_metaFromCache.clear();
}
/*
@@ -123,26 +118,101 @@ MetadataJob::~MetadataJob()
* repositories which might not be currently selected.
*/
-QList<Metadata> MetadataJob::metadata() const
+QList<Metadata *> MetadataJob::metadata() const
{
- QList<Metadata> metadata = m_metaFromDefaultRepositories.values();
- foreach (RepositoryCategory repositoryCategory, m_core->settings().repositoryCategories()) {
- if (m_core->isUpdater() || (repositoryCategory.isEnabled() && m_fetchedArchive.contains(repositoryCategory.displayname()))) {
- QList<ArchiveMetadata> archiveMetaList = m_fetchedArchive.values(repositoryCategory.displayname());
- foreach (ArchiveMetadata archiveMeta, archiveMetaList) {
- metadata.append(archiveMeta.metaData);
- }
+ const QSet<RepositoryCategory> categories = m_core->settings().repositoryCategories();
+ QHash<RepositoryCategory, QSet<Repository>> repositoryHash;
+ // Create hash of categorized repositories to avoid constructing
+ // excess temp objects when filtering below.
+ for (const RepositoryCategory &category : categories)
+ repositoryHash.insert(category, category.repositories());
+
+ QList<Metadata *> metadata = m_metaFromCache.items();
+ // Filter cache items not associated with current repositories and categories
+ QtConcurrent::blockingFilter(metadata, [&](const Metadata *item) {
+ if (!item->isActive())
+ return false;
+
+ // No need to check if the repository is enabled here. Changing the network
+ // settings resets the cache and we don't fetch the disabled repositories,
+ // so the cached items stay inactive.
+
+ if (item->isAvailableFromDefaultRepository())
+ return true;
+
+ QHash<RepositoryCategory, QSet<Repository>>::const_iterator it;
+ for (it = repositoryHash.constBegin(); it != repositoryHash.constEnd(); ++it) {
+ if (!it.key().isEnabled())
+ continue; // Let's try the next one
+
+ if (it->contains(item->repository()))
+ return true;
}
- }
+ return false;
+ });
+
return metadata;
}
-Repository MetadataJob::repositoryForDirectory(const QString &directory) const
+/*
+ Returns a repository object from the cache item matching \a directory. If the
+ \a directory does not belong to the cache, an empty repository is returned.
+*/
+Repository MetadataJob::repositoryForCacheDirectory(const QString &directory) const
{
- if (m_metaFromDefaultRepositories.contains(directory))
- return m_metaFromDefaultRepositories.value(directory).repository;
- else
- return m_metaFromArchive.value(directory).repository;
+ QDir dir(directory);
+ if (!QDir::fromNativeSeparators(dir.path())
+ .startsWith(QDir::fromNativeSeparators(m_metaFromCache.path()))) {
+ return Repository();
+ }
+ const QString dirName = dir.dirName();
+ Metadata *cachedMeta = m_metaFromCache.itemByChecksum(dirName.toUtf8());
+ if (cachedMeta)
+ return cachedMeta->repository();
+
+ return Repository();
+}
+
+bool MetadataJob::resetCache(bool init)
+{
+ // Need the path from current settings
+ if (!m_core) {
+ qCWarning(lcInstallerInstallLog) << "Cannot reset metadata cache: "
+ "missing package manager core engine.";
+ return false;
+ }
+
+ if (m_metaFromCache.isValid() && !m_core->settings().persistentLocalCache())
+ m_metaFromCache.clear();
+
+ m_metaFromCache.setPath(m_core->settings().localCachePath());
+
+ if (!init)
+ return true;
+
+ const bool success = m_metaFromCache.initialize();
+ if (success) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Using metadata cache from"
+ << m_metaFromCache.path();
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Found"
+ << m_metaFromCache.items().count() << "cached items.";
+ }
+ return success;
+}
+
+bool MetadataJob::clearCache()
+{
+ if (m_metaFromCache.clear())
+ return true;
+
+ setError(JobError::CacheError);
+ setErrorString(m_metaFromCache.errorString());
+ return false;
+}
+
+bool MetadataJob::isValidCache() const
+{
+ return m_metaFromCache.isValid();
}
// -- private slots
@@ -151,39 +221,91 @@ void MetadataJob::doStart()
{
setError(Job::NoError);
setErrorString(QString());
+ m_metadataResult.clear();
+ setProgressTotalAmount(100);
+
if (!m_core) {
emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine."));
return; // We can't do anything here without core, so avoid tons of !m_core checks.
}
+ if (!m_metaFromCache.isValid() && !resetCache(true)) {
+ emitFinishedWithError(JobError::CacheError, m_metaFromCache.errorString());
+ return;
+ }
+
const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance();
- if (m_downloadType == DownloadType::All || m_downloadType == DownloadType::UpdatesXML) {
- emit infoMessage(this, tr("Preparing meta information download..."));
+ if (m_downloadType != DownloadType::CompressedPackage) {
+ emit infoMessage(this, tr("Fetching latest update information..."));
const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly();
- if (onlineInstaller || m_core->isMaintainer()) {
- QList<FileTaskItem> items;
- QSet<Repository> repositories = getRepositories();
+ const QSet<Repository> repositories = getRepositories();
+
+ if (onlineInstaller || m_core->isMaintainer()
+ || (m_core->settings().allowRepositoriesForOfflineInstaller() && !repositories.isEmpty())) {
+ static const QString updateFilePath(QLatin1Char('/') + scUpdatesXML + QLatin1Char('?'));
+ static const QString randomQueryString = QString::number(QRandomGenerator::global()->generate());
+
+ quint64 cachedCount = 0;
+ setProgressTotalAmount(0); // Show only busy indicator during this loop as we have no progress to measure
foreach (const Repository &repo, repositories) {
+ // For not blocking the UI
+ qApp->processEvents();
+
if (repo.isEnabled() &&
productKeyCheck->isValidRepository(repo)) {
QAuthenticator authenticator;
authenticator.setUser(repo.username());
authenticator.setPassword(repo.password());
- if (!repo.isCompressed()) {
- QString url = repo.url().toString() + QLatin1String("/Updates.xml?");
- if (!m_core->value(scUrlQueryString).isEmpty())
- url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ if (repo.isCompressed())
+ continue;
+
+ QString url;
+ url = repo.url().toString() + updateFilePath;
+ if (!m_core->value(scUrlQueryString).isEmpty())
+ url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ // also append a random string to avoid proxy caches
+ url.append(randomQueryString);
+
+ // Check if we can skip downloading already cached repositories
+ const Status foundStatus = findCachedUpdatesFile(repo, url);
+ if (foundStatus == XmlDownloadSuccess) {
+ // Found existing Updates.xml
+ ++cachedCount;
+ continue;
+ } else if (foundStatus == XmlDownloadRetry) {
+ // Repositories changed, restart with the new repositories
+ QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
+ return;
+ }
- // also append a random string to avoid proxy caches
- FileTaskItem item(url.append(QString::number(QRandomGenerator::global()->generate())));
- item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
- items.append(item);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
}
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+ item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
+ item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
+ m_updatesXmlItems.append(item);
}
}
- if (items.count() > 0) {
- startXMLTask(items);
+ setProgressTotalAmount(100);
+ const quint64 totalCount = repositories.count();
+ if (cachedCount > 0) {
+ qCDebug(lcInstallerInstallLog).nospace() << "Loaded from cache "
+ << cachedCount << "/" << totalCount << ". Downloading remaining "
+ << m_updatesXmlItems.count() << "/" << totalCount <<".";
+ } else {
+ qCDebug(lcInstallerInstallLog).nospace() <<"Downloading " << m_updatesXmlItems.count()
+ << " items to cache.";
+ }
+ if (m_updatesXmlItems.count() > 0) {
+ double taskCount = m_updatesXmlItems.length()/static_cast<double>(m_downloadableChunkSize);
+ m_totalTaskCount = qCeil(taskCount);
+ m_taskNumber = 0;
+ startXMLTask();
} else {
emitFinished();
}
@@ -222,18 +344,28 @@ void MetadataJob::doStart()
}
}
-void MetadataJob::startXMLTask(const QList<FileTaskItem> &items)
+bool MetadataJob::startXMLTask()
{
- DownloadFileTask *const xmlTask = new DownloadFileTask(items);
- xmlTask->setProxyFactory(m_core->proxyFactory());
- connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this,
- &MetadataJob::progressChanged);
- m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
+ int chunkSize = qMin(m_updatesXmlItems.length(), m_downloadableChunkSize);
+ QList<FileTaskItem> tempPackages = m_updatesXmlItems.mid(0, chunkSize);
+ m_updatesXmlItems = m_updatesXmlItems.mid(chunkSize, m_updatesXmlItems.length());
+ if (tempPackages.length() > 0) {
+ DownloadFileTask *const xmlTask = new DownloadFileTask(tempPackages);
+ xmlTask->setProxyFactory(m_core->proxyFactory());
+ connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this,
+ &MetadataJob::progressChanged);
+ m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
+
+ setInfoMessage(tr("Retrieving information from remote repositories..."));
+ return true;
+ }
+ return false;
}
void MetadataJob::doCancel()
{
reset();
+ resetCache();
emitFinishedWithError(Job::Canceled, tr("Metadata download canceled."));
}
@@ -257,6 +389,27 @@ void MetadataJob::startUnzipRepositoryTask(const Repository &repo)
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
+void MetadataJob::startUpdateCacheTask()
+{
+ const int toRegisterCount = m_fetchedMetadata.count();
+ if (toRegisterCount > 0)
+ emit infoMessage(this, tr("Updating local cache with %n new items...",
+ nullptr, toRegisterCount));
+
+ UpdateCacheTask *task = new UpdateCacheTask(m_metaFromCache, m_fetchedMetadata);
+ m_updateCacheTask.setFuture(QtConcurrent::run(&UpdateCacheTask::doTask, task));
+}
+
+/*
+ Resets the repository information from all cache items, which
+ makes them inactive until associated with new repositories.
+*/
+void MetadataJob::resetCacheRepositories()
+{
+ for (auto *metaToReset : m_metaFromCache.items())
+ metaToReset->setRepository(Repository());
+}
+
void MetadataJob::unzipRepositoryTaskFinished()
{
QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender());
@@ -283,9 +436,17 @@ void MetadataJob::unzipRepositoryTaskFinished()
error = testJob.error();
errorString = testJob.errorString();
if (error == Job::NoError) {
- FileTaskItem item(url);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
+ }
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+
item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- m_unzipRepositoryitems.append(item);
+ m_updatesXmlItems.append(item);
} else {
//Repository is not valid, remove it
Settings &s = m_core->settings();
@@ -305,8 +466,8 @@ void MetadataJob::unzipRepositoryTaskFinished()
//One can specify many zipped repository items at once. As the repositories are
//unzipped one by one, we collect here all items before parsing xml files from those.
- if (m_unzipRepositoryitems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) {
- startXMLTask(m_unzipRepositoryitems);
+ if (m_updatesXmlItems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) {
+ startXMLTask();
} else {
if (error != Job::NoError) {
emitFinishedWithError(QInstaller::DownloadError, errorString);
@@ -330,19 +491,34 @@ void MetadataJob::xmlTaskFinished()
Status status = XmlDownloadFailure;
try {
m_xmlTask.waitForFinished();
- status = parseUpdatesXml(m_xmlTask.future().results());
+ m_updatesXmlResult.append(m_xmlTask.future().results());
+ if (!startXMLTask()) {
+ status = parseUpdatesXml(m_updatesXmlResult);
+ m_updatesXmlResult.clear();
+ } else {
+ return;
+ }
} 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 {
@@ -351,13 +527,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();
@@ -366,7 +554,7 @@ void MetadataJob::xmlTaskFinished()
temporaries.insert(replacement);
s.addTemporaryRepositories(temporaries, true);
} else {
- QHash<QString, QPair<Repository, Repository> > update;
+ QMultiHash<QString, QPair<Repository, Repository> > update;
update.insert(QLatin1String("replace"), qMakePair(original, replacement));
if (s.updateRepositoryCategories(update) == Settings::UpdatesApplied)
@@ -407,13 +595,13 @@ void MetadataJob::xmlTaskFinished()
return;
if (status == XmlDownloadSuccess) {
- if (m_downloadType != DownloadType::UpdatesXML) {
- if (!fetchMetaDataPackages())
- emitFinished();
- } else {
- emitFinished();
+ if (!fetchMetaDataPackages()) {
+ // 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();
@@ -441,10 +629,8 @@ void MetadataJob::unzipTaskFinished()
m_unzipTasks.remove(watcher);
delete watcher;
- if (m_unzipTasks.isEmpty()) {
- setProcessedAmount(100);
- emitFinished();
- }
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
}
void MetadataJob::progressChanged(int progress)
@@ -476,17 +662,29 @@ void MetadataJob::metadataTaskFinished()
} else {
throw QInstaller::TaskException(mismatchMessage);
}
+ QFileInfo fi(result.target());
+ QString targetPath = fi.absolutePath();
+ if (m_fetchedMetadata.contains(targetPath)) {
+ delete m_fetchedMetadata.value(targetPath);
+ m_fetchedMetadata.remove(targetPath);
+ }
+ continue;
}
UnzipArchiveTask *task = new UnzipArchiveTask(result.target(),
item.value(TaskRole::UserRole).toString());
+ task->setRemoveArchive(true);
+ task->setStoreChecksums(true);
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task));
connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished);
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
+
} else {
- emitFinished();
+ startUpdateCacheTask();
}
}
} catch (const TaskException &e) {
@@ -501,6 +699,25 @@ void MetadataJob::metadataTaskFinished()
}
}
+void MetadataJob::updateCacheTaskFinished()
+{
+ try {
+ m_updateCacheTask.waitForFinished();
+ } catch (const CacheTaskException &e) {
+ emitFinishedWithError(QInstaller::CacheError, e.message());
+ } catch (const QUnhandledException &e) {
+ emitFinishedWithError(QInstaller::CacheError, QLatin1String(e.what()));
+ } catch (...) {
+ emitFinishedWithError(QInstaller::CacheError, tr("Unknown exception during updating cache."));
+ }
+
+ if (error() != Job::NoError)
+ return;
+
+ setProcessedAmount(100);
+ emitFinished();
+}
+
// -- private
@@ -511,18 +728,11 @@ bool MetadataJob::fetchMetaDataPackages()
QList<FileTaskItem> tempPackages = m_packages.mid(0, chunkSize);
m_packages = m_packages.mid(chunkSize, m_packages.length());
if (tempPackages.length() > 0) {
- m_taskNumber++;
setProcessedAmount(0);
DownloadFileTask *const metadataTask = new DownloadFileTask(tempPackages);
metadataTask->setProxyFactory(m_core->proxyFactory());
m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask));
- setProgressTotalAmount(100);
- QString metaInformation;
- if (m_totalTaskCount > 1)
- metaInformation = tr("Retrieving meta information from remote repository... %1/%2 ").arg(m_taskNumber).arg(m_totalTaskCount);
- else
- metaInformation = tr("Retrieving meta information from remote repository... ");
- emit infoMessage(this, metaInformation);
+ setInfoMessage(tr("Retrieving meta information from remote repository..."));
return true;
}
return false;
@@ -531,9 +741,12 @@ bool MetadataJob::fetchMetaDataPackages()
void MetadataJob::reset()
{
m_packages.clear();
- m_metaFromDefaultRepositories.clear();
- m_metaFromArchive.clear();
- m_fetchedArchive.clear();
+ m_updatesXmlItems.clear();
+ m_defaultRepositoriesFetched = false;
+ m_fetchedCategorizedRepositories.clear();
+
+ qDeleteAll(m_fetchedMetadata);
+ m_fetchedMetadata.clear();
setError(Job::NoError);
setErrorString(QString());
@@ -547,6 +760,7 @@ void MetadataJob::reset()
} catch (...) {}
m_tempDirDeleter.releaseAndDeleteAll();
m_metadataResult.clear();
+ m_updatesXmlResult.clear();
m_taskNumber = 0;
}
@@ -554,7 +768,6 @@ void MetadataJob::resetCompressedFetch()
{
setError(Job::NoError);
setErrorString(QString());
- m_unzipRepositoryitems.clear();
try {
foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) {
@@ -586,43 +799,54 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
if (result.target().isEmpty()) {
continue;
}
- Metadata metadata;
- QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
- if (!tmp.isValid()) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
- return XmlDownloadFailure;
- }
-
- tmp.setAutoRemove(false);
- metadata.directory = tmp.path();
- m_tempDirDeleter.add(metadata.directory);
+ QFileInfo fileInfo(result.target());
+ std::unique_ptr<Metadata> metadata(new Metadata(fileInfo.absolutePath()));
QFile file(result.target());
- if (!file.rename(metadata.directory + QLatin1String("/Updates.xml"))) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot rename target to Updates.xml:"
- << file.errorString();
- return XmlDownloadFailure;
- }
-
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open Updates.xml for reading:"
<< file.errorString();
return XmlDownloadFailure;
}
+ const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
+ const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+ const QByteArray updatesChecksum = hash.result().toHex();
+
+ if (!repository.xmlChecksum().isEmpty() && updatesChecksum != repository.xmlChecksum()) {
+ qCWarning(lcDeveloperBuild).noquote().nospace() << "The checksum for Updates.xml "
+ "file downloaded from repository:\n" << repository.url().toString() << "\ndoes not "
+ "match the expected value:\n\tActual SHA1: " << updatesChecksum << "\n\tExpected SHA1: "
+ << repository.xmlChecksum() << Qt::endl;
+ }
+
+ bool refreshed;
+ // Check if we have cached the metadata for this repository already
+ Status status = refreshCacheItem(result, updatesChecksum, &refreshed);
+ if (status != XmlDownloadSuccess)
+ return status;
+
+ if (refreshed) // Found existing metadata
+ continue;
+
+ metadata->setChecksum(updatesChecksum);
+
+ file.seek(0);
QString error;
QDomDocument doc;
if (!doc.setContent(&file, &error)) {
qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Cannot fetch a valid version of Updates.xml from repository "
- << metadata.repository.displayname() << ": " << error;
+ << metadata->repository().displayname() << ": " << error;
//If there are other repositories, try to use those
continue;
}
file.close();
- const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
- metadata.repository = item.value(TaskRole::UserRole).value<Repository>();
- const bool online = !(metadata.repository.url().scheme()).isEmpty();
+ metadata->setRepository(repository);
+ const bool online = !(metadata->repository().url().scheme()).isEmpty();
bool testCheckSum = true;
const QDomElement root = doc.documentElement();
@@ -634,14 +858,14 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
// all metadata inside one repository to a single 7z file. Fetch that
// instead of component specific meta 7z files.
const QDomNode sha1 = root.firstChildElement(scSHA1);
- QDomElement metadataNameElement = root.firstChildElement(QLatin1String("MetadataName"));
+ QDomElement metadataNameElement = root.firstChildElement(scMetadataName);
QDomNodeList children = root.childNodes();
if (!sha1.isNull() && !metadataNameElement.isNull()) {
- const QString repoUrl = metadata.repository.url().toString();
- const QString metadataName = metadataNameElement.toElement().text();
- addFileTaskItem(QString::fromLatin1("%1/%2").arg(repoUrl, metadataName),
- metadata.directory + QString::fromLatin1("/%1").arg(metadataName),
- metadata, sha1.toElement().text(), QString());
+ const QString repoUrl = metadata->repository().url().toString();
+ const QString metadataName = metadataNameElement.toElement().text();
+ addFileTaskItem(QString::fromLatin1("%1/%2").arg(repoUrl, metadataName),
+ metadata->path() + QString::fromLatin1("/%1").arg(metadataName),
+ metadata.get(), sha1.toElement().text(), QString());
} else {
bool metaFound = false;
for (int i = 0; i < children.count(); ++i) {
@@ -653,16 +877,13 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
online, testCheckSum);
// If meta element (script, licenses, etc.) is not found, no need to fetch metadata.
- // The offline-generator instance is an exception to this - if the Updates.xml contains
- // checksum element for the meta-archive, we will fetch it, so that the temporary
- // location contents match the remote repository.
- if (metaFound || (m_core->isOfflineGenerator() && !packageHash.isEmpty())) {
- const QString repoUrl = metadata.repository.url().toString();
+ if (metaFound) {
+ const QString repoUrl = metadata->repository().url().toString();
addFileTaskItem(QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, packageName, packageVersion),
- metadata.directory + QString::fromLatin1("/%1-%2-meta.7z").arg(packageName, packageVersion),
- metadata, packageHash, packageName);
+ metadata->path() + QString::fromLatin1("/%1-%2-meta.7z").arg(packageName, packageVersion),
+ metadata.get(), packageHash, packageName);
} else {
- QString fileName = metadata.directory + QLatin1Char('/') + packageName;
+ QString fileName = metadata->path() + QLatin1Char('/') + packageName;
QDir directory(fileName);
if (!directory.exists()) {
directory.mkdir(fileName);
@@ -672,40 +893,24 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
}
}
- if (metadata.repository.categoryname().isEmpty()) {
- m_metaFromDefaultRepositories.insert(metadata.directory, metadata);
- } else {
- //Hash metadata to help checking if meta for repository is already fetched
- ArchiveMetadata archiveMetadata;
- archiveMetadata.metaData = metadata;
- m_fetchedArchive.insert(metadata.repository.categoryname(), archiveMetadata);
-
- //Check if other categories have the same url (contains same metadata)
- //so we can speed up other category fetches
- foreach (RepositoryCategory category, m_core->settings().repositoryCategories()) {
- if (category.displayname() != metadata.repository.categoryname()) {
- foreach (Repository repository, category.repositories()) {
- if (repository.url() == metadata.repository.url()) {
- m_fetchedArchive.insert(category.displayname(), archiveMetadata);
- }
- }
- }
- }
- // Hash for faster lookups
- m_metaFromArchive.insert(metadata.directory, metadata);
- }
+ // Remember the fetched metadata
+ Metadata *const metadataPtr = metadata.get();
+ const QString categoryName = metadata->repository().categoryname();
+ if (categoryName.isEmpty())
+ metadata->setAvailableFromDefaultRepository(true);
+ else
+ m_fetchedCategorizedRepositories.insert(metadataPtr->repository()); // For faster lookups
+ const QString metadataPath = metadata->path();
+ m_fetchedMetadata.insert(metadataPath, metadata.release());
// search for additional repositories that we might need to check
- const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
- if (!repositoryUpdate.isNull()) {
- QHash<QString, QPair<Repository, Repository> > repositoryUpdates =
- searchAdditionalRepositories(repositoryUpdate, result, metadata);
- if (!repositoryUpdates.isEmpty()) {
- MetadataJob::Status status = setAdditionalRepositories(repositoryUpdates, result, metadata);
- if (status == XmlDownloadRetry)
- return status;
- }
+ status = parseRepositoryUpdates(root, result, metadataPtr);
+ if (status == XmlDownloadRetry) {
+ // The repository update may have removed or replaced current repositories,
+ // clear repository information from cached items and refresh on next fetch run.
+ resetCacheRepositories();
+ return status;
}
}
double taskCount = m_packages.length()/static_cast<double>(m_downloadableChunkSize);
@@ -715,46 +920,128 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
return XmlDownloadSuccess;
}
+MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result,
+ const QByteArray &checksum, bool *refreshed)
+{
+ Q_ASSERT(refreshed);
+ *refreshed = false;
+
+ Metadata *cachedMetadata = m_metaFromCache.itemByChecksum(checksum);
+ if (!cachedMetadata)
+ return XmlDownloadSuccess;
+
+ const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
+ const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
+
+ if (cachedMetadata->isValid() && !repository.isCompressed()) {
+ // Refresh repository information to cache. Same repository may appear in multiple
+ // categories and the metadata may be available from default repositories simultaneously.
+ cachedMetadata->setRepository(repository);
+ if (!repository.categoryname().isEmpty())
+ m_fetchedCategorizedRepositories.insert(repository); // For faster lookups
+ else
+ cachedMetadata->setAvailableFromDefaultRepository(true);
+
+ // Refresh also persistent information, the url of the repository may have changed
+ // from the last fetch.
+ cachedMetadata->setPersistentRepositoryPath(repository.url());
+
+ // search for additional repositories that we might need to check
+ if (cachedMetadata->containsRepositoryUpdates()) {
+ QDomDocument doc = cachedMetadata->updatesDocument();
+ const Status status = parseRepositoryUpdates(doc.documentElement(), result, cachedMetadata);
+ if (status == XmlDownloadRetry) {
+ // The repository update may have removed or replaced current repositories,
+ // clear repository information from cached items and refresh on next fetch run.
+ resetCacheRepositories();
+ return status;
+ }
+ }
+ *refreshed = true;
+ return XmlDownloadSuccess;
+ }
+ // Missing or corrupted files, or compressed repository which takes priority
+ // over remote repository. We will re-download and uncompress
+ // the metadata. Remove broken item from the cache.
+ if (!m_metaFromCache.removeItem(checksum)) {
+ qCWarning(lcInstallerInstallLog) << m_metaFromCache.errorString();
+ return XmlDownloadFailure;
+ }
+ return XmlDownloadSuccess;
+}
+
+MetadataJob::Status MetadataJob::findCachedUpdatesFile(const Repository &repository, const QString &fileUrl)
+{
+ if (repository.xmlChecksum().isEmpty())
+ return XmlDownloadFailure;
+
+ Metadata *metadata = m_metaFromCache.itemByChecksum(repository.xmlChecksum());
+ if (!metadata)
+ return XmlDownloadFailure;
+
+ const QString targetPath = metadata->path() + QLatin1Char('/') + scUpdatesXML;
+
+ FileTaskItem cachedMetaTaskItem(fileUrl, targetPath);
+ cachedMetaTaskItem.insert(TaskRole::UserRole, QVariant::fromValue(repository));
+ const FileTaskResult cachedMetaTaskResult(targetPath, repository.xmlChecksum(), cachedMetaTaskItem, false);
+
+ bool isCached = false;
+ const Status status = refreshCacheItem(cachedMetaTaskResult, repository.xmlChecksum(), &isCached);
+ if (isCached)
+ return XmlDownloadSuccess;
+ else if (status == XmlDownloadRetry)
+ return XmlDownloadRetry;
+ else
+ return XmlDownloadFailure;
+}
+
+MetadataJob::Status MetadataJob::parseRepositoryUpdates(const QDomElement &root,
+ const FileTaskResult &result, Metadata *metadata)
+{
+ MetadataJob::Status status = XmlDownloadSuccess;
+ const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
+ if (!repositoryUpdate.isNull()) {
+ const QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates
+ = searchAdditionalRepositories(repositoryUpdate, result, *metadata);
+ if (!repositoryUpdates.isEmpty())
+ status = setAdditionalRepositories(repositoryUpdates, result, *metadata);
+ }
+ return status;
+}
+
QSet<Repository> MetadataJob::getRepositories()
{
QSet<Repository> repositories;
- //In the first run, m_metadata is empty. Get always the default repositories
- if (m_metaFromDefaultRepositories.isEmpty()) {
+ //In the first run, get always the default repositories
+ if (!m_defaultRepositoriesFetched) {
repositories = m_core->settings().repositories();
+ m_defaultRepositoriesFetched = true;
}
// Fetch repositories under archive which are selected in UI.
// If repository is already fetched, do not fetch it again.
- // In updater mode, fetch always all archive repositories to get updates
- foreach (RepositoryCategory repositoryCategory, m_core->settings().repositoryCategories()) {
- if (m_core->isUpdater() || (repositoryCategory.isEnabled())) {
- foreach (Repository repository, repositoryCategory.repositories()) {
- QHashIterator<QString, ArchiveMetadata> i(m_fetchedArchive);
- bool fetch = true;
- while (i.hasNext()) {
- i.next();
- ArchiveMetadata metaData = i.value();
- if (repository.url() == metaData.metaData.repository.url())
- fetch = false;
- }
- if (fetch)
- repositories.insert(repository);
- }
- }
+ for (const RepositoryCategory &repositoryCategory : m_core->settings().repositoryCategories()) {
+ if (!repositoryCategory.isEnabled())
+ continue;
+
+ for (const Repository &repository : repositoryCategory.repositories()) {
+ if (!m_fetchedCategorizedRepositories.contains(repository))
+ repositories.insert(repository);
+ }
}
return repositories;
}
-void MetadataJob::addFileTaskItem(const QString &source, const QString &target, const Metadata &metadata,
+void MetadataJob::addFileTaskItem(const QString &source, const QString &target, Metadata *metadata,
const QString &sha1, const QString &packageName)
{
FileTaskItem item(source, target);
QAuthenticator authenticator;
- authenticator.setUser(metadata.repository.username());
- authenticator.setPassword(metadata.repository.password());
+ authenticator.setUser(metadata->repository().username());
+ authenticator.setPassword(metadata->repository().password());
- item.insert(TaskRole::UserRole, metadata.directory);
+ item.insert(TaskRole::UserRole, metadata->path());
item.insert(TaskRole::Checksum, sha1.toLatin1());
item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
item.insert(TaskRole::Name, packageName);
@@ -775,7 +1062,7 @@ bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageNam
else if ((element.tagName() == QLatin1String("SHA1")) && testCheckSum)
packageHash = element.text();
else {
- foreach (QString meta, metaElements) {
+ foreach (QString meta, scMetaElements) {
if (element.tagName() == meta) {
metaFound = true;
break;
@@ -786,7 +1073,7 @@ bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageNam
return metaFound;
}
-QHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
+QMultiHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
(const QDomNode &repositoryUpdate, const FileTaskResult &result, const Metadata &metadata)
{
QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates;
@@ -828,21 +1115,21 @@ QHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepo
}
} else {
qDebug() << "Invalid additional repositories action set in Updates.xml fetched "
- "from" << metadata.repository.displayname() << "line:" << el.lineNumber();
+ "from" << metadata.repository().displayname() << "line:" << el.lineNumber();
}
}
}
return repositoryUpdates;
}
-MetadataJob::Status MetadataJob::setAdditionalRepositories(QHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+MetadataJob::Status MetadataJob::setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
const FileTaskResult &result, const Metadata& metadata)
{
MetadataJob::Status status = XmlDownloadSuccess;
Settings &s = m_core->settings();
const QSet<Repository> temporaries = s.temporaryRepositories();
// in case the temp repository introduced something new, we only want that temporary
- if (temporaries.contains(metadata.repository)) {
+ if (temporaries.contains(metadata.repository())) {
QSet<Repository> tmpRepositories;
typedef QPair<Repository, Repository> RepositoryPair;
@@ -858,7 +1145,9 @@ MetadataJob::Status MetadataJob::setAdditionalRepositories(QHash<QString, QPair<
if (tmpRepositories.count() > 0) {
s.addTemporaryRepositories(tmpRepositories, true);
QFile::remove(result.target());
- m_metaFromDefaultRepositories.clear();
+ m_defaultRepositoriesFetched = false;
+ qDeleteAll(m_fetchedMetadata);
+ m_fetchedMetadata.clear();
status = XmlDownloadRetry;
}
} else if (s.updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) {
@@ -872,10 +1161,22 @@ MetadataJob::Status MetadataJob::setAdditionalRepositories(QHash<QString, QPair<
if (gainedAdminRights)
m_core->dropAdminRights();
}
- m_metaFromDefaultRepositories.clear();
+ m_defaultRepositoriesFetched = false;
+ qDeleteAll(m_fetchedMetadata);
+ m_fetchedMetadata.clear();
QFile::remove(result.target());
status = XmlDownloadRetry;
}
return status;
}
+
+void MetadataJob::setInfoMessage(const QString &message)
+{
+ m_taskNumber++;
+ QString metaInformation = message;
+ if (m_totalTaskCount > 1)
+ metaInformation = QLatin1String(" %1 %2/%3 ").arg(message).arg(m_taskNumber).arg(m_totalTaskCount);
+ emit infoMessage(this, metaInformation);
+
+}
} // namespace QInstaller
diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h
index d3b404c58..13ad3ea8c 100644
--- a/src/libs/installer/metadatajob.h
+++ b/src/libs/installer/metadatajob.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.
@@ -32,6 +32,8 @@
#include "downloadfiletask.h"
#include "fileutils.h"
#include "job.h"
+#include "metadata.h"
+#include "metadatacache.h"
#include "repository.h"
#include <QFutureWatcher>
@@ -43,23 +45,10 @@ namespace QInstaller {
class PackageManagerCore;
-struct Metadata
-{
- QString directory;
- Repository repository;
-};
-
-struct ArchiveMetadata
-{
- QString archive;
- Metadata metaData;
-};
-
enum DownloadType
{
All,
- CompressedPackage,
- UpdatesXML
+ CompressedPackage
};
class INSTALLER_EXPORT MetadataJob : public Job
@@ -77,12 +66,16 @@ public:
explicit MetadataJob(QObject *parent = 0);
~MetadataJob();
- QList<Metadata> metadata() const;
- Repository repositoryForDirectory(const QString &directory) const;
+ QList<Metadata *> metadata() const;
+ Repository repositoryForCacheDirectory(const QString &directory) const;
void setPackageManagerCore(PackageManagerCore *core) { m_core = core; }
void addDownloadType(DownloadType downloadType) { m_downloadType = downloadType;}
QStringList shaMismatchPackages() const { return m_shaMissmatchPackages; }
+ bool resetCache(bool init = false);
+ bool clearCache();
+ bool isValidCache() const;
+
private slots:
void doStart() override;
void doCancel() override;
@@ -90,46 +83,60 @@ private slots:
void xmlTaskFinished();
void unzipTaskFinished();
void metadataTaskFinished();
+ void updateCacheTaskFinished();
void progressChanged(int progress);
void setProgressTotalAmount(int maximum);
void unzipRepositoryTaskFinished();
- void startXMLTask(const QList<FileTaskItem> &items);
+ bool startXMLTask();
private:
bool fetchMetaDataPackages();
void startUnzipRepositoryTask(const Repository &repo);
+ void startUpdateCacheTask();
+ void resetCacheRepositories();
void reset();
void resetCompressedFetch();
Status parseUpdatesXml(const QList<FileTaskResult> &results);
+ Status refreshCacheItem(const FileTaskResult &result, const QByteArray &checksum, bool *refreshed);
+ Status findCachedUpdatesFile(const Repository &repository, const QString &fileUrl);
+ Status parseRepositoryUpdates(const QDomElement &root, const FileTaskResult &result, Metadata *metadata);
QSet<Repository> getRepositories();
- void addFileTaskItem(const QString &source, const QString &target, const Metadata &metadata,
+ void addFileTaskItem(const QString &source, const QString &target, Metadata *metadata,
const QString &sha1, const QString &packageName);
- bool parsePackageUpdate(const QDomNodeList &c2, QString &packageName, QString &packageVersion,
+ static bool parsePackageUpdate(const QDomNodeList &c2, QString &packageName, QString &packageVersion,
QString &packageHash, bool online, bool testCheckSum);
- QHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
+ QMultiHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
const FileTaskResult &result, const Metadata &metadata);
- MetadataJob::Status setAdditionalRepositories(QHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+ MetadataJob::Status setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
const FileTaskResult &result, const Metadata& metadata);
+ void setInfoMessage(const QString &message);
+
+private:
+ friend class Metadata;
private:
PackageManagerCore *m_core;
QList<FileTaskItem> m_packages;
- TempDirDeleter m_tempDirDeleter;
+ QList<FileTaskItem> m_updatesXmlItems;
+ TempPathDeleter m_tempDirDeleter;
QFutureWatcher<FileTaskResult> m_xmlTask;
QFutureWatcher<FileTaskResult> m_metadataTask;
+ QFutureWatcher<void> m_updateCacheTask;
QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks;
QHash<QFutureWatcher<void> *, QObject*> m_unzipRepositoryTasks;
DownloadType m_downloadType;
- QList<FileTaskItem> m_unzipRepositoryitems;
QList<FileTaskResult> m_metadataResult;
+ QList<FileTaskResult> m_updatesXmlResult;
int m_downloadableChunkSize;
int m_taskNumber;
int m_totalTaskCount;
QStringList m_shaMissmatchPackages;
- QMultiHash<QString, ArchiveMetadata> m_fetchedArchive;
- QHash<QString, Metadata> m_metaFromDefaultRepositories;
- QHash<QString, Metadata> m_metaFromArchive; //for faster lookups.
+ bool m_defaultRepositoriesFetched;
+
+ QSet<Repository> m_fetchedCategorizedRepositories;
+ QHash<QString, Metadata *> m_fetchedMetadata;
+ MetadataCache m_metaFromCache;
};
} // namespace QInstaller
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index 5fd44e6f9..837a7e9ae 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -32,6 +32,7 @@
#include "archivefactory.h"
#include "metadatajob.h"
+#include <QCryptographicHash>
#include <QDir>
#include <QFile>
@@ -61,10 +62,17 @@ class UnzipArchiveTask : public AbstractTask<void>
public:
UnzipArchiveTask(const QString &arcive, const QString &target)
- : m_archive(arcive), m_targetDir(target)
+ : m_archive(arcive)
+ , m_targetDir(target)
+ , m_removeArchive(false)
+ , m_storeChecksums(false)
{}
+
QString target() { return m_targetDir; }
QString archive() { return m_archive; }
+ void setRemoveArchive(bool remove) { m_removeArchive = remove; }
+ void setStoreChecksums(bool store) { m_storeChecksums = store; }
+
void doTask(QFutureInterface<void> &fi) override
{
fi.reportStarted();
@@ -79,20 +87,132 @@ public:
if (!archive) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Unsupported archive \"%1\": no handler "
"registered for file suffix \"%2\".").arg(m_archive, QFileInfo(m_archive).suffix())));
+ return;
} else if (!archive->open(QIODevice::ReadOnly)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
"reading: %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
} else if (!archive->extract(m_targetDir)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting "
"archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
+ }
+
+ if (m_storeChecksums) {
+ // Calculate and store checksums of extracted files for later use
+ const QVector<ArchiveEntry> entries = archive->list();
+ for (auto &entry : entries) {
+ if (entry.isDirectory)
+ continue;
+
+ QFile file(m_targetDir + QDir::separator() + entry.path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open extracted file \"%1\" for "
+ "reading: %2").arg(QDir::toNativeSeparators(file.fileName()), file.errorString())));
+ break;
+ }
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray hexChecksum = hash.result().toHex();
+ 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;
+ }
+ }
}
+ archive->close();
+ if (m_removeArchive)
+ QFile::remove(m_archive);
+
fi.reportFinished();
}
private:
QString m_archive;
QString m_targetDir;
+ bool m_removeArchive;
+ bool m_storeChecksums;
+};
+
+class CacheTaskException : public QException
+{
+public:
+ CacheTaskException() {}
+ explicit CacheTaskException(const QString &message)
+ : m_message(message)
+ {}
+ ~CacheTaskException() {}
+
+ void raise() const override { throw *this; }
+ QString message() const { return m_message; }
+ CacheTaskException *clone() const override { return new CacheTaskException(*this); }
+
+private:
+ QString m_message;
+};
+
+class UpdateCacheTask : public AbstractTask<void>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(UpdateCacheTask)
+
+public:
+ UpdateCacheTask(MetadataCache &cache, QHash<QString, Metadata *> &updates)
+ : m_cache(&cache)
+ , m_updates(&updates)
+ {}
+
+ void doTask(QFutureInterface<void> &fi) override
+ {
+ fi.reportStarted();
+ fi.setExpectedResultCount(1);
+
+ // Register items from current run to cache
+ QStringList registeredKeys;
+ bool success = true;
+ for (auto *meta : qAsConst(*m_updates)) {
+ if (!m_cache->registerItem(meta, true, MetadataCache::Move)) {
+ success = false;
+ break;
+ }
+ meta->setPersistentRepositoryPath(meta->repository().url());
+ registeredKeys.append(m_updates->key(meta));
+ }
+ // Remove items whose ownership was transferred to cache
+ for (auto &key : qAsConst(registeredKeys))
+ m_updates->remove(key);
+
+ // Bail out if there was error while registering items
+ if (!success) {
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
+ m_cache->sync();
+ fi.reportFinished();
+ return;
+ }
+
+ // ...and clean up obsolete cached items
+ const QList<Metadata *> obsolete = m_cache->obsoleteItems();
+ for (auto *meta : obsolete)
+ m_cache->removeItem(meta->checksum());
+
+ if (!m_cache->sync()) {
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
+ }
+
+ fi.reportFinished();
+ }
+
+private:
+ MetadataCache *const m_cache;
+ QHash<QString, Metadata *> *const m_updates;
};
} // namespace QInstaller
diff --git a/src/libs/installer/observer.cpp b/src/libs/installer/observer.cpp
index 30afce719..57b67d8e1 100644
--- a/src/libs/installer/observer.cpp
+++ b/src/libs/installer/observer.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -121,9 +121,9 @@ QByteArray FileTaskObserver::checkSum() const
return m_hash.result();
}
-void FileTaskObserver::addCheckSumData(const char *data, int length)
+void FileTaskObserver::addCheckSumData(const QByteArray &data)
{
- m_hash.addData(data, length);
+ m_hash.addData(data);
}
void FileTaskObserver::addSample(qint64 sample)
diff --git a/src/libs/installer/observer.h b/src/libs/installer/observer.h
index 198a0f89c..d638d8ee4 100644
--- a/src/libs/installer/observer.h
+++ b/src/libs/installer/observer.h
@@ -60,7 +60,7 @@ public:
QString progressText() const override;
QByteArray checkSum() const;
- void addCheckSumData(const char *data, int length);
+ void addCheckSumData(const QByteArray &data);
void addSample(qint64 sample);
void timerEvent(QTimerEvent *event) override;
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 9110d0c91..5c6f481f9 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.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.
@@ -31,6 +31,7 @@
#include "adminauthorization.h"
#include "binarycontent.h"
#include "component.h"
+#include "componentalias.h"
#include "componentmodel.h"
#include "downloadarchivesjob.h"
#include "errors.h"
@@ -46,6 +47,7 @@
#include "installercalculator.h"
#include "uninstallercalculator.h"
#include "loggingutils.h"
+#include "componentsortfilterproxymodel.h"
#include <productkeycheck.h>
@@ -54,12 +56,17 @@
#include <QtConcurrentRun>
#include <QtCore/QMutex>
-#include <QtCore/QRegExp>
#include <QtCore/QSettings>
#include <QtCore/QTemporaryFile>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore5Compat/QTextCodec>
+#include <QtCore5Compat/QTextDecoder>
+#include <QtCore5Compat/QTextEncoder>
+#else
#include <QtCore/QTextCodec>
#include <QtCore/QTextDecoder>
#include <QtCore/QTextEncoder>
+#endif
#include <QtCore/QTextStream>
#include <QDesktopServices>
@@ -176,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()
*/
/*!
@@ -214,7 +202,7 @@ using namespace QInstaller;
*/
/*!
- \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale::Language lang)
+ \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale lang)
Emitted when the language \a lang has changed.
@@ -532,7 +520,6 @@ void PackageManagerCore::reset()
d->m_status = PackageManagerCore::Unfinished;
d->m_installerBaseBinaryUnreplaced.clear();
d->m_coreCheckedHash.clear();
- d->m_componentsToInstallCalculated = false;
}
/*!
@@ -574,85 +561,113 @@ void PackageManagerCore::cancelMetaInfoJob()
}
/*!
- \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
- */
-void PackageManagerCore::componentsToInstallNeedsRecalculation()
+ Resets the cache used to store downloaded metadata, if one was previously
+ initialized. If \a init is set to \c true, the cache is reinitialized for
+ the path configured in installer's settings.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool PackageManagerCore::resetLocalCache(bool init)
{
- d->clearInstallerCalculator();
+ return d->m_metadataJob.resetCache(init);
+}
- QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
+/*!
+ Clears the contents of the cache used to store downloaded metadata.
+ Returns \c true on success, \c false otherwise. An error string can
+ be retrieved with \a error.
+*/
+bool PackageManagerCore::clearLocalCache(QString *error)
+{
+ if (d->m_metadataJob.clearCache())
+ return true;
- d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall, true);
+ if (error)
+ *error = d->m_metadataJob.errorString();
- d->calculateUninstallComponents();
- d->updateComponentCheckedState();
+ return false;
+}
- // update all nodes uncompressed size
- foreach (Component *const component, components(ComponentType::Root))
- component->updateUncompressedSize(); // this is a recursive call
+/*!
+ Returns \c true if the metadata cache is initialized and valid, \c false otherwise.
+*/
+bool PackageManagerCore::isValidCache() const
+{
+ return d->m_metadataJob.isValidCache();
}
/*!
- Calculates components to install based on user selection. \a indexes
- contains list of model indexes user has selected for install, dependencies
- and autodependencies are resolved later.
+ \internal
*/
-void PackageManagerCore::calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes)
-{
- QList<Component*> componentsToInstall;
- QList<Component*> componentsToUnInstall;
- ComponentModel *model = isUpdater() ? updaterComponentModel() : defaultComponentModel();
- for (QModelIndex index : indexes) {
- Component *installComponent = model->componentFromIndex(index);
- // 1. Component is selected for install
- if (installComponent->isSelected() && !installComponent->isInstalled()) {
- componentsToInstall.append(installComponent);
- // Check if component has replacements that needs to be removed
- const QList<Component*> replacedComponents = d->replacedComponentsByName(installComponent->name());
- for (Component *replacedComponent : replacedComponents) {
- componentsToUnInstall.append(replacedComponent);
- d->uninstallerCalculator()->insertUninstallReason(replacedComponent,
- UninstallerCalculator::UninstallReasonType::Replaced);
- }
- }
- // 2. Component is reseleted for install (tapping checkbox off/on)
- else if (installComponent->isSelected() && installComponent->isInstalled()
- && !d->installerCalculator()->orderedComponentsToInstall().contains(installComponent)) {
- componentsToInstall.append(installComponent);
- }
- // 3. Component is selected for uninstall
- else if (!isUpdater() && !installComponent->isSelected() && installComponent->isInstalled()) {
- componentsToUnInstall.append(installComponent);
- }
- // 4. Component is reselected for uninstall (tapping checkbox on/off)
- else if (!installComponent->isSelected()
- && d->installerCalculator()->orderedComponentsToInstall().contains(installComponent)) {
- componentsToUnInstall.append(installComponent);
- // Check if component has replacements that needs to be readded
- componentsToInstall.append(d->replacedComponentsByName(installComponent->name()));
- }
- }
+template <typename T>
+bool PackageManagerCore::loadComponentScripts(const T &components, const bool postScript)
+{
+ return d->loadComponentScripts(components, postScript);
+}
- d->installerCalculator()->removeComponentsFromInstall(componentsToUnInstall);
- d->m_componentsToInstallCalculated
- = d->installerCalculator()->appendComponentsToInstall(componentsToInstall, false);
- if (!isUpdater()) {
- d->uninstallerCalculator()->appendComponentsToUninstall(componentsToUnInstall, false);
- }
- d->uninstallerCalculator()->removeComponentsFromUnInstall(componentsToInstall);
+template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
- d->updateComponentCheckedState();
+/*!
+ 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}
+ */
+void PackageManagerCore::componentsToInstallNeedsRecalculation()
+{
+ recalculateAllComponents();
+}
/*!
- Forces a recalculation of components to install.
+ \fn QInstaller::PackageManagerCore::clearComponentsToInstallCalculated()
+
+ \deprecated [4.5] Installer framework recalculates components each time the calculation
+ of components to install is requested, so there is no need to call this anymore, and the
+ method does nothing. On previous versions calling this forced a recalculation of
+ components to install.
+
\sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated}
*/
-void PackageManagerCore::clearComponentsToInstallCalculated()
+
+/*!
+ Recalculates all components to install and uninstall. Returns \c true
+ on success, \c false otherwise. Detailed error messages can be retrieved
+ with {installer::componentsToInstallError} and {installer::componentsToUninstallError}.
+ */
+bool PackageManagerCore::recalculateAllComponents()
{
- d->m_componentsToInstallCalculated = false;
+ // Clear previous results first, as the check states are updated
+ // at the end of both calculate methods, which refer to the results
+ // from both calculators. Needed to keep the state correct.
+ d->clearInstallerCalculator();
+ d->clearUninstallerCalculator();
+
+ if (!calculateComponentsToInstall())
+ return false;
+ if (!isInstaller() && !calculateComponentsToUninstall())
+ return false;
+
+ // update all nodes uncompressed size
+ foreach (Component *const component, components(ComponentType::Root))
+ component->updateUncompressedSize(); // this is a recursive call
+
+ return true;
}
/*!
@@ -796,12 +811,13 @@ quint64 PackageManagerCore::requiredDiskSpace() const
*/
quint64 PackageManagerCore::requiredTemporaryDiskSpace() const
{
- if (isOfflineOnly())
- return 0;
-
quint64 result = 0;
- foreach (QInstaller::Component *component, orderedComponentsToInstall())
+ foreach (QInstaller::Component *component, orderedComponentsToInstall()) {
+ if (!component->isFromOnlineRepository())
+ continue;
+
result += size(component, scCompressedSize);
+ }
return result;
}
@@ -814,16 +830,19 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
{
Q_ASSERT(partProgressSize >= 0 && partProgressSize <= 1);
- QList<QPair<QString, QString> > archivesToDownload;
+ QList<DownloadItem> archivesToDownload;
quint64 archivesToDownloadTotalSize = 0;
QList<Component*> neededComponents = orderedComponentsToInstall();
foreach (Component *component, neededComponents) {
// collect all archives to be downloaded
const QStringList toDownload = component->downloadableArchives();
+ bool checkSha1CheckSum = (component->value(scCheckSha1CheckSum).toLower() == scTrue);
foreach (const QString &versionFreeString, toDownload) {
- archivesToDownload.push_back(qMakePair(QString::fromLatin1("installer://%1/%2")
- .arg(component->name(), versionFreeString), QString::fromLatin1("%1/%2/%3")
- .arg(component->repositoryUrl().toString(), component->name(), versionFreeString)));
+ DownloadItem item;
+ item.checkSha1CheckSum = checkSha1CheckSum;
+ item.fileName = scInstallerPrefixWithTwoArgs.arg(component->name(), versionFreeString);
+ item.sourceUrl = scThreeArgs.arg(component->repositoryUrl().toString(), component->name(), versionFreeString);
+ archivesToDownload.push_back(item);
}
archivesToDownloadTotalSize += component->value(scCompressedSize).toULongLong();
}
@@ -831,9 +850,10 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
if (archivesToDownload.isEmpty())
return 0;
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nDownloading packages..."));
+ 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);
@@ -843,6 +863,11 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
connect(&archivesJob, &DownloadArchivesJob::downloadStatusChanged,
ProgressCoordinator::instance(), &ProgressCoordinator::additionalProgressStatusChanged);
+ connect(&archivesJob, &DownloadArchivesJob::fileDownloadReady,
+ d, &PackageManagerCorePrivate::addPathForDeletion);
+ connect(&archivesJob, &DownloadArchivesJob::hashDownloadReady,
+ d, &PackageManagerCorePrivate::addPathForDeletion);
+
ProgressCoordinator::instance()->registerPartProgress(&archivesJob,
SIGNAL(progressChanged(double)), partProgressSize);
@@ -981,7 +1006,7 @@ void PackageManagerCore::rollBackInstallation()
*/
bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) const
{
- QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettingsWrapper::NativeFormat);
+ QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettings::NativeFormat);
return settings.value(QString::fromLatin1(".%1/Default").arg(extension)).isValid();
}
@@ -996,7 +1021,7 @@ bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) con
*/
bool PackageManagerCore::fileExists(const QString &filePath) const
{
- return QFileInfo(filePath).exists();
+ return QFileInfo::exists(filePath);
}
/*!
@@ -1021,8 +1046,7 @@ QString PackageManagerCore::readFile(const QString &filePath, const QString &cod
return QString();
QTextStream stream(&f);
- stream.setCodec(codec);
- return stream.readAll();
+ return QString::fromUtf8(codec->fromUnicode(stream.readAll()));
}
/*!
@@ -1263,11 +1287,16 @@ PackageManagerCore::PackageManagerCore()
Creates and initializes a remote client. Requests administrator's rights for
QFile, QSettings, and QProcess operations. Calls \c init() with \a socketName, \a key,
and \a mode to set the server side authorization key.
+
+ The \a datFileName contains the corresponding .dat file name for the running
+ \c maintenance tool binary. \a datFileName can be empty if \c maintenance tool
+ fails to find it or if \c installer is run instead of \c maintenance tool.
*/
PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &operations,
+ const QString &datFileName,
const QString &socketName, const QString &key, Protocol::Mode mode,
const QHash<QString, QString> &params, const bool commandLineInstance)
- : d(new PackageManagerCorePrivate(this, magicmaker, operations))
+ : d(new PackageManagerCorePrivate(this, magicmaker, operations, datFileName))
{
setCommandLineInstance(commandLineInstance);
Repository::registerMetaType(); // register, cause we stream the type as QVariant
@@ -1298,8 +1327,8 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB
QSet<QString> packagesWithoutOperation = installedPackages - operationPackages;
QSet<QString> orphanedOperations = operationPackages - installedPackages;
if (!packagesWithoutOperation.isEmpty() || !orphanedOperations.isEmpty()) {
- qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.toList();
- qCritical() << "Orphaned operations" << orphanedOperations.toList();
+ qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.values();
+ qCritical() << "Orphaned operations" << orphanedOperations.values();
qCritical() << "Your installation seems to be corrupted. Please consider re-installing from scratch, "
"remove the packages from components.xml which operations are missing, "
"or reinstall the packages.";
@@ -1506,7 +1535,7 @@ bool PackageManagerCore::fetchLocalPackagesTree()
continue;
}
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
component->loadDataFromPackage(package);
QString name = component->treeName();
if (components.contains(name)) {
@@ -1532,7 +1561,7 @@ bool PackageManagerCore::fetchLocalPackagesTree()
if (!treeName.isEmpty())
treeNameComponents.insert(component->name(), treeName);
- components.insert(name, component.take());
+ components.insert(name, component.release());
}
// Second pass with leftover packages
if (firstRun)
@@ -1577,10 +1606,11 @@ void PackageManagerCore::networkSettingsChanged()
cancelMetaInfoJob();
d->m_updates = false;
+ d->m_aliases = false;
d->m_repoFetched = false;
d->m_updateSourcesAdded = false;
- if (isMaintainer() ) {
+ if (!isInstaller()) {
bool gainedAdminRights = false;
if (!directoryWritable(d->targetDir())) {
gainAdminRights();
@@ -1642,7 +1672,7 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true, true)) {
+ if (!d->addUpdateResourcesFromRepositories(true)) {
return false;
}
@@ -1653,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 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);
@@ -1681,12 +1742,18 @@ bool PackageManagerCore::fetchRemotePackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true))
+ if (!d->addUpdateResourcesFromRepositories())
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);
}
@@ -1935,24 +2002,82 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie
settings().setTemporaryRepositories(repositorySet, replace);
}
-/*!
- Checks whether the downloader should try to download SHA-1 checksums for
- archives and returns the checksums.
-*/
-bool PackageManagerCore::testChecksum() const
+bool PackageManagerCore::addQBspRepositories(const QStringList &repositories)
{
- return d->m_testChecksum;
+ 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;
}
-/*!
- The \a test argument determines whether the downloader should try to
- download SHA-1 checksums for archives.
-*/
-void PackageManagerCore::setTestChecksum(bool test)
+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()
{
- d->m_testChecksum = test;
+ 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.
@@ -1981,6 +2106,10 @@ ScriptEngine *PackageManagerCore::controlScriptEngine() const
*/
void PackageManagerCore::appendRootComponent(Component *component)
{
+ // For normal installer runs components aren't appended after model reset
+ if (Q_UNLIKELY(!d->m_componentByNameHash.isEmpty()))
+ d->m_componentByNameHash.clear();
+
d->m_rootComponents.append(component);
emit componentAdded(component);
}
@@ -2046,7 +2175,7 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask, const QSt
QRegularExpression re(regexp);
QList<Component*>::iterator iter = components.begin();
while (iter != components.end()) {
- if (!re.match(iter.i->t()->name()).hasMatch())
+ if (!re.match((*iter)->name()).hasMatch())
iter = components.erase(iter);
else
iter++;
@@ -2062,6 +2191,10 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask, const QSt
*/
void PackageManagerCore::appendUpdaterComponent(Component *component)
{
+ // For normal installer runs components aren't appended after model reset
+ if (Q_UNLIKELY(!d->m_componentByNameHash.isEmpty()))
+ d->m_componentByNameHash.clear();
+
component->setUpdateAvailable(true);
d->m_updaterComponents.append(component);
emit componentAdded(component);
@@ -2075,7 +2208,39 @@ void PackageManagerCore::appendUpdaterComponent(Component *component)
*/
Component *PackageManagerCore::componentByName(const QString &name) const
{
- return componentByName(name, components(ComponentType::AllNoReplacements));
+ if (name.isEmpty())
+ return nullptr;
+
+ if (d->m_componentByNameHash.isEmpty()) {
+ // We can avoid the linear lookups from the component list by creating
+ // a <name,component> hash once, and reusing it on subsequent calls.
+ const QList<Component *> componentsList = components(ComponentType::AllNoReplacements);
+ for (Component *component : componentsList)
+ d->m_componentByNameHash.insert(component->name(), component);
+ }
+
+ QString fixedVersion;
+ QString fixedName;
+
+ parseNameAndVersion(name, &fixedName, &fixedVersion);
+
+ Component *component = d->m_componentByNameHash.value(fixedName);
+ if (!component)
+ return nullptr;
+
+ if (componentMatches(component, fixedName, fixedVersion))
+ return component;
+
+ return nullptr;
+}
+
+/*!
+ Searches for a component alias matching \a name and returns it.
+ If no alias matches the name, \c nullptr is returned.
+*/
+ComponentAlias *PackageManagerCore::aliasByName(const QString &name) const
+{
+ return d->m_componentAliases.value(name);
}
/*!
@@ -2104,6 +2269,18 @@ Component *PackageManagerCore::componentByName(const QString &name, const QList<
}
/*!
+ Returns an array of all components currently available. If the repository
+ metadata have not been fetched yet, the array will be empty. Optionally, a
+ \a regexp expression can be used to further filter the listed packages.
+
+ \sa {installer::components}{installer.components}
+ */
+QList<Component *> PackageManagerCore::components(const QString &regexp) const
+{
+ return components(PackageManagerCore::ComponentType::All, regexp);
+}
+
+/*!
Returns \c true if directory specified by \a path is writable by
the current user.
*/
@@ -2141,8 +2318,27 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const
}
/*!
- Determines which components to install based on the current run mode and returns an
- ordered list of components to install. Also auto installed dependencies are resolved.
+ Returns a list of component aliases that are marked for installation.
+ The list can be empty.
+*/
+QList<ComponentAlias *> PackageManagerCore::aliasesMarkedForInstallation() const
+{
+ if (isUpdater()) // Aliases not supported on update at the moment
+ return QList<ComponentAlias *>();
+
+ QList<ComponentAlias *> markedForInstallation;
+ for (auto *alias : qAsConst(d->m_componentAliases)) {
+ if (alias && alias->isSelected())
+ markedForInstallation.append(alias);
+ }
+
+ return markedForInstallation;
+}
+
+/*!
+ Determines which components to install based on the current run mode, including component aliases,
+ dependencies and automatic dependencies. Returns \c true on success, \c false otherwise.
+
The aboutCalculateComponentsToInstall() signal is emitted
before the calculation starts, the finishedCalculateComponentsToInstall()
signal once all calculations are done.
@@ -2153,16 +2349,15 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const
bool PackageManagerCore::calculateComponentsToInstall() const
{
emit aboutCalculateComponentsToInstall();
- if (!d->m_componentsToInstallCalculated) {
- d->clearInstallerCalculator();
- QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
- d->storeCheckState();
- d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall);
- }
+ d->clearInstallerCalculator();
+
+ const bool calculated = d->installerCalculator()->solve();
+
+ d->updateComponentInstallActions();
+
emit finishedCalculateComponentsToInstall();
- return d->m_componentsToInstallCalculated;
+ return calculated;
}
/*!
@@ -2170,29 +2365,34 @@ bool PackageManagerCore::calculateComponentsToInstall() const
*/
QList<Component*> PackageManagerCore::orderedComponentsToInstall() const
{
- return d->installerCalculator()->orderedComponentsToInstall();
+ return d->installerCalculator()->resolvedComponents();
}
/*!
- Calculates components to install and uninstall. In case of an error, returns \c false
- and and sets the \a displayString for error detail.
+ Returns a HTML-formatted description of the reasons each component is about
+ to be installed or uninstalled, or a description of the error occurred while
+ calculating components to install and uninstall.
*/
-
-bool PackageManagerCore::calculateComponents(QString *displayString)
+QString PackageManagerCore::componentResolveReasons() const
{
QString htmlOutput;
- if (!calculateComponentsToInstall()) {
+ if (!componentsToInstallError().isEmpty()) {
htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>")
- .arg(tr("Cannot resolve all dependencies.")));
+ .arg(tr("Cannot resolve all dependencies.")));
//if we have a missing dependency or a recursion we can display it
- if (!componentsToInstallError().isEmpty()) {
- htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
- componentsToInstallError()));
- }
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
+ componentsToInstallError()));
htmlOutput.append(QLatin1String("</ul>"));
- if (displayString)
- *displayString = htmlOutput;
- return false;
+ return htmlOutput;
+ }
+
+ if (!componentsToUninstallError().isEmpty()) {
+ htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>")
+ .arg(tr("Cannot resolve components to uninstall.")));
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
+ componentsToUninstallError()));
+ htmlOutput.append(QLatin1String("</ul>"));
+ return htmlOutput;
}
QList<Component*> componentsToRemove = componentsToUninstall();
@@ -2225,28 +2425,46 @@ bool PackageManagerCore::calculateComponents(QString *displayString)
}
htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name()));
}
- if (displayString)
- *displayString = htmlOutput;
- return true;
+ return htmlOutput;
}
/*!
- Calculates a list of components to uninstall based on the current run mode.
+ Calculates a list of components to uninstall.
+
The aboutCalculateComponentsToUninstall() signal is emitted
before the calculation starts, the finishedCalculateComponentsToUninstall() signal once all
- calculations are done. Always returns \c true.
+ calculations are done. Returns \c true on success, \c false otherwise.
\sa {installer::calculateComponentsToUninstall}{installer.calculateComponentsToUninstall}
*/
bool PackageManagerCore::calculateComponentsToUninstall() const
{
emit aboutCalculateComponentsToUninstall();
- if (!isUpdater()) {
- d->calculateUninstallComponents();
- d->storeCheckState();
+
+ d->clearUninstallerCalculator();
+ const QList<Component *> componentsToInstallList = d->installerCalculator()->resolvedComponents();
+
+ QList<Component *> selectedComponentsToUninstall;
+ foreach (Component* component, components(PackageManagerCore::ComponentType::Replacements)) {
+ // Uninstall the component if replacement is selected for install or update
+ QPair<Component*, Component*> comp = d->componentsToReplace().value(component->name());
+ if (comp.first && d->m_installerCalculator->resolvedComponents().contains(comp.first)) {
+ d->uninstallerCalculator()->insertResolution(component,
+ CalculatorBase::Resolution::Replaced, comp.first->name());
+ selectedComponentsToUninstall.append(comp.second);
+ }
+ }
+ foreach (Component *component, components(PackageManagerCore::ComponentType::AllNoReplacements)) {
+ if (component->uninstallationRequested() && !componentsToInstallList.contains(component))
+ selectedComponentsToUninstall.append(component);
}
+ const bool componentsToUninstallCalculated =
+ d->uninstallerCalculator()->solve(selectedComponentsToUninstall);
+
+ d->updateComponentInstallActions();
+
emit finishedCalculateComponentsToUninstall();
- return true;
+ return componentsToUninstallCalculated;
}
/*!
@@ -2258,7 +2476,7 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
*/
QList<Component *> PackageManagerCore::componentsToUninstall() const
{
- return d->uninstallerCalculator()->componentsToUninstall().toList();
+ return d->uninstallerCalculator()->resolvedComponents();
}
/*!
@@ -2266,7 +2484,15 @@ QList<Component *> PackageManagerCore::componentsToUninstall() const
*/
QString PackageManagerCore::componentsToInstallError() const
{
- return d->installerCalculator()->componentsToInstallError();
+ return d->installerCalculator()->error();
+}
+
+/*!
+ Returns errors found in the components that are marked for uninstallation.
+*/
+QString PackageManagerCore::componentsToUninstallError() const
+{
+ return d->uninstallerCalculator()->error();
}
/*!
@@ -2280,7 +2506,7 @@ QString PackageManagerCore::componentsToInstallError() const
*/
QString PackageManagerCore::installReason(Component *component) const
{
- return d->installerCalculator()->installReason(component);
+ return d->installerCalculator()->resolutionText(component);
}
/*!
@@ -2296,7 +2522,7 @@ QString PackageManagerCore::installReason(Component *component) const
*/
QString PackageManagerCore::uninstallReason(Component *component) const
{
- return d->uninstallerCalculator()->uninstallReason(component);
+ return d->uninstallerCalculator()->resolutionText(component);
}
/*!
@@ -2329,44 +2555,65 @@ QList<Component*> PackageManagerCore::dependees(const Component *_component) con
}
/*!
- Returns a list of components that depend on \a component. The list can be
- empty. Dependendants are calculated from components which are about to be updated,
- if no update is requested then the dependant is calculated from installed packages.
-
- \note Automatic dependencies are not resolved.
+ Returns true if components which are about to be installed or updated
+ are dependent on \a component.
*/
-QList<Component*> PackageManagerCore::installDependants(const Component *component) const
+bool PackageManagerCore::isDependencyForRequestedComponent(const Component *component) const
{
if (!component)
- return QList<Component *>();
+ return false;
const QList<QInstaller::Component *> availableComponents = components(ComponentType::All);
if (availableComponents.isEmpty())
- return QList<Component *>();
+ return false;
- QList<Component *> dependants;
QString name;
QString version;
- foreach (Component *availableComponent, availableComponents) {
- if (isUpdater() && availableComponent->updateRequested()) {
+ for (Component *availableComponent : availableComponents) {
+ if (!availableComponent) {
+ continue;
+ }
+ // 1. In updater mode, component to be updated might have new dependencies
+ // Check if the dependency is still needed
+ // 2. If component is selected and not installed, check if the dependency is needed
+ if (availableComponent->isSelected()
+ && ((isUpdater() && availableComponent->isInstalled())
+ || (isPackageManager() && !availableComponent->isInstalled()))) {
const QStringList &dependencies = availableComponent->dependencies();
foreach (const QString &dependency, dependencies) {
parseNameAndVersion(dependency, &name, &version);
if (componentMatches(component, name, version)) {
- dependants.append(availableComponent);
+ return true;
}
}
- } else {
- KDUpdater::LocalPackage localPackage = d->m_localPackageHub->packageInfo(availableComponent->name());
- foreach (const QString &dependency, localPackage.dependencies) {
- parseNameAndVersion(dependency, &name, &version);
- if (componentMatches(component, name, version)) {
- dependants.append(availableComponent);
- }
+ }
+ }
+ return false;
+}
+
+
+/*!
+ Returns a list of local components which are dependent on \a component.
+*/
+QStringList PackageManagerCore::localDependenciesToComponent(const Component *component) const
+{
+ if (!component)
+ return QStringList();
+
+ QStringList dependents;
+ QString name;
+ QString version;
+
+ QMap<QString, LocalPackage> localPackages = d->m_localPackageHub->localPackages();
+ for (const KDUpdater::LocalPackage &localPackage : qAsConst(localPackages)) {
+ for (const QString &dependency : localPackage.dependencies) {
+ parseNameAndVersion(dependency, &name, &version);
+ if (componentMatches(component, name, version)) {
+ dependents.append(localPackage.name);
}
}
}
- return dependants;
+ return dependents;
}
/*!
@@ -2378,12 +2625,13 @@ ComponentModel *PackageManagerCore::defaultComponentModel() const
if (!d->m_defaultModel) {
d->m_defaultModel = componentModel(const_cast<PackageManagerCore*> (this),
QLatin1String("AllComponentsModel"));
+
+ connect(this, &PackageManagerCore::startAllComponentsReset, [&] {
+ d->m_defaultModel->reset();
+ });
+ connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel,
+ &ComponentModel::reset);
}
- connect(this, &PackageManagerCore::startAllComponentsReset, [&] {
- d->m_defaultModel->reset();
- });
- connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel,
- &ComponentModel::reset);
return d->m_defaultModel;
}
@@ -2396,44 +2644,70 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const
if (!d->m_updaterModel) {
d->m_updaterModel = componentModel(const_cast<PackageManagerCore*> (this),
QLatin1String("UpdaterComponentsModel"));
+
+ connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] {
+ d->m_updaterModel->reset();
+ });
+ connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel,
+ &ComponentModel::reset);
}
- connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] {
- d->m_updaterModel->reset();
- });
- connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel,
- &ComponentModel::reset);
return d->m_updaterModel;
}
/*!
+ 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
can be used to further filter listed packages.
+ Returns \c true if matching packages were found, \c false otherwise.
+
\sa setVirtualComponentsVisible()
*/
-void PackageManagerCore::listAvailablePackages(const QString &regexp, const QHash<QString, QString> &filters)
+bool PackageManagerCore::listAvailablePackages(const QString &regexp, const QHash<QString, QString> &filters)
{
setPackageViewer();
+ d->enableAllCategories();
qCDebug(QInstaller::lcInstallerInstallLog)
<< "Searching packages with regular expression:" << regexp;
ComponentModel *model = defaultComponentModel();
- d->fetchMetaInformationFromRepositories(DownloadType::UpdatesXML);
+ PackagesList packages;
+
+ if (!d->m_updates) {
+ d->fetchMetaInformationFromRepositories();
+ d->addUpdateResourcesFromRepositories();
+
+ packages = d->remotePackages();
+ if (!fetchAllPackages(packages, LocalPackagesMap())) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "There was a problem with loading the package data.";
+ return false;
+ }
+ } else {
+ // No need to fetch metadata again
+ packages = d->remotePackages();
+ }
- d->addUpdateResourcesFromRepositories(true);
QRegularExpression re(regexp);
re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
- const PackagesList &packages = d->remotePackages();
- if (!fetchAllPackages(packages, LocalPackagesMap())) {
- qCWarning(QInstaller::lcInstallerInstallLog)
- << "There was a problem with loading the package data.";
- return;
- }
PackagesList matchedPackages;
- foreach (Package *package, packages) {
+ foreach (Package *package, qAsConst(packages)) {
const QString name = package->data(scName).toString();
Component *component = componentByName(name);
if (!component)
@@ -2455,10 +2729,68 @@ void PackageManagerCore::listAvailablePackages(const QString &regexp, const QHas
matchedPackages.append(package);
}
}
- if (matchedPackages.count() == 0)
+ if (matchedPackages.count() == 0) {
qCDebug(QInstaller::lcInstallerInstallLog) << "No matching packages found.";
- else
- LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages());
+ return false;
+ }
+
+ LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages());
+ return true;
+}
+
+/*!
+ Lists available component aliases filtered with \a regexp without GUI. Virtual
+ aliases are not listed unless set visible.
+
+ Returns \c true if matching package aliases were found, \c false otherwise.
+
+ \sa setVirtualComponentsVisible()
+*/
+bool PackageManagerCore::listAvailableAliases(const QString &regexp)
+{
+ setPackageViewer();
+ d->enableAllCategories();
+ qCDebug(QInstaller::lcInstallerInstallLog)
+ << "Searching aliases with regular expression:" << regexp;
+
+ ComponentModel *model = defaultComponentModel();
+ Q_UNUSED(model);
+
+ if (!d->m_updates) {
+ d->fetchMetaInformationFromRepositories();
+ d->addUpdateResourcesFromRepositories();
+
+ const PackagesList &packages = d->remotePackages();
+ if (!fetchAllPackages(packages, LocalPackagesMap())) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "There was a problem with loading the package data.";
+ return false;
+ }
+ }
+
+ QRegularExpression re(regexp);
+ re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+
+ QList<ComponentAlias *> matchedAliases;
+ for (auto *alias : qAsConst(d->m_componentAliases)) {
+ if (!alias)
+ continue;
+
+ if (re.match(alias->name()).hasMatch() && !alias->isUnstable()) {
+ if (alias->isVirtual() && !virtualComponentsVisible())
+ continue;
+
+ matchedAliases.append(alias);
+ }
+ }
+
+ if (matchedAliases.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No matching package aliases found.";
+ return false;
+ }
+
+ LoggingHandler::instance().printAliasInformation(matchedAliases);
+ return true;
}
bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &componentName)
@@ -2472,7 +2804,7 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co
}
ComponentModel *model = defaultComponentModel();
const QModelIndex &idx = model->indexFromComponentName(component->treeName());
- if (model->data(idx, Qt::CheckStateRole) == QVariant::Invalid) {
+ if (model->data(idx, Qt::CheckStateRole) == QVariant()) {
// Component cannot be unselected, check why
if (component->forcedInstallation()) {
qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace()
@@ -2496,38 +2828,58 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co
/*!
\internal
- Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given \a components in the
- default component model. Returns \c true if \a components contains at least one component
+ Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given component \a names in the
+ default component model, and select given aliases in the \c names list.
+
+ Returns \c true if \a names contains at least one component or component alias
eligible for installation, otherwise returns \c false. An error message can be retrieved
with \a errorMessage.
*/
-bool PackageManagerCore::checkComponentsForInstallation(const QStringList &components, QString &errorMessage)
+bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound)
{
bool installComponentsFound = false;
ComponentModel *model = defaultComponentModel();
- foreach (const QString &name, components) {
+ foreach (const QString &name, names) {
Component *component = componentByName(name);
if (!component) {
- errorMessage.append(tr("Cannot install %1. Component not found.\n").arg(name));
+ // No such component, check if we have an alias by the name
+ if (ComponentAlias *alias = aliasByName(name)) {
+ 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 "
+ "be selected manually.").arg(name) + QLatin1Char('\n'));
+ continue;
+ }
+
+ alias->setSelected(true);
+ installComponentsFound = true;
+ } else {
+ errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n'));
+ }
+
continue;
}
const QModelIndex &idx = model->indexFromComponentName(component->treeName());
if (idx.isValid()) {
- if ((model->data(idx, Qt::CheckStateRole) == QVariant::Invalid) && !component->forcedInstallation()) {
+ if ((model->data(idx, Qt::CheckStateRole) == QVariant()) && !component->forcedInstallation()) {
// User cannot select the component, check why
if (component->autoDependencies().count() > 0) {
errorMessage.append(tr("Cannot install component %1. Component is installed only as automatic "
- "dependency to %2.\n").arg(name, component->autoDependencies().join(QLatin1Char(','))));
+ "dependency to %2.").arg(name, component->autoDependencies().join(QLatin1Char(','))) + QLatin1Char('\n'));
} else if (!component->isCheckable()) {
errorMessage.append(tr("Cannot install component %1. Component is not checkable, meaning you "
- "have to select one of the subcomponents.\n").arg(name));
+ "have to select one of the subcomponents.").arg(name) + QLatin1Char('\n'));
} else if (component->isUnstable()) {
errorMessage.append(tr("Cannot install component %1. There was a problem loading this component, "
- "so it is marked unstable and cannot be selected.\n").arg(name));
+ "so it is marked unstable and cannot be selected.").arg(name) + QLatin1Char('\n'));
}
} else if (component->isInstalled()) {
- errorMessage.append(tr("Component %1 already installed\n").arg(name));
+ errorMessage.append(tr("Component %1 already installed").arg(name) + QLatin1Char('\n'));
} else {
model->setData(idx, Qt::Checked, Qt::CheckStateRole);
installComponentsFound = true;
@@ -2542,16 +2894,16 @@ bool PackageManagerCore::checkComponentsForInstallation(const QStringList &compo
return false;
} else if (trace->isVirtual()) {
errorMessage.append(tr("Cannot install %1. Component is a descendant "
- "of a virtual component %2.\n").arg(name, trace->name()));
+ "of a virtual component %2.").arg(name, trace->name()) + QLatin1Char('\n'));
return true;
}
}
};
// idx is invalid and component valid when we have invisible virtual component
if (component->isVirtual())
- errorMessage.append(tr("Cannot install %1. Component is virtual.\n").arg(name));
+ errorMessage.append(tr("Cannot install %1. Component is virtual.").arg(name) + QLatin1Char('\n'));
else if (!isDescendantOfVirtual())
- errorMessage.append(tr("Cannot install %1. Component not found.\n").arg(name));
+ errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n'));
}
}
if (!installComponentsFound)
@@ -2584,6 +2936,25 @@ void PackageManagerCore::listInstalledPackages(const QString &regexp)
LoggingHandler::instance().printLocalPackageInformation(packages);
}
+PackageManagerCore::Status PackageManagerCore::searchAvailableUpdates()
+{
+ setUpdater();
+ d->enableAllCategories();
+ if (!fetchRemotePackagesTree()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error();
+ return status();
+ }
+
+ const QList<QInstaller::Component *> availableUpdates =
+ components(QInstaller::PackageManagerCore::ComponentType::Root);
+ if (availableUpdates.isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "There are currently no updates available.";
+ return status();
+ }
+ QInstaller::LoggingHandler::instance().printUpdateInformation(availableUpdates);
+ return status();
+}
+
/*!
Updates the selected components \a componentsToUpdate without GUI.
If essential components are found, then only those will be updated.
@@ -2591,13 +2962,25 @@ void PackageManagerCore::listInstalledPackages(const QString &regexp)
*/
PackageManagerCore::Status PackageManagerCore::updateComponentsSilently(const QStringList &componentsToUpdate)
{
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
setUpdater();
ComponentModel *model = updaterComponentModel();
- fetchRemotePackagesTree();
+ if (componentsToUpdate.isEmpty()) {
+ d->enableAllCategories();
+ fetchRemotePackagesTree();
+ } else {
+ 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();
@@ -2703,6 +3086,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.
@@ -2727,9 +3124,6 @@ void PackageManagerCore::createAutoDependencyHash(const QString &component, cons
*/
PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const QStringList& components)
{
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
-
if (components.isEmpty()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "No components selected for uninstallation.";
return PackageManagerCore::Canceled;
@@ -2768,8 +3162,6 @@ PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const
PackageManagerCore::Status PackageManagerCore::removeInstallationSilently()
{
setCompleteUninstallation(true);
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen.";
if (!(d->m_autoConfirmCommand || d->askUserConfirmCommand())) {
@@ -2791,23 +3183,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);
}
/*!
@@ -2819,9 +3195,6 @@ PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStr
PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently(const QStringList& components)
{
if (!isInstaller()) {
- // Check if there are processes running in the install if maintenancetool is used.
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
setPackageManager();
//Check that packages are not already installed
@@ -2834,24 +3207,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);
}
/*!
@@ -2917,6 +3273,21 @@ void PackageManagerCore::dropAdminRights()
}
/*!
+ Returns \c true if the installer has admin rights. For example, if the installer
+ was started with a root account, or the internal admin rights elevation is active.
+
+ Returns \c false otherwise.
+
+ \sa {installer::hasAdminRights}{installer.hasAdminRights}
+ \sa gainAdminRights()
+ \sa dropAdminRights()
+*/
+bool PackageManagerCore::hasAdminRights() const
+{
+ return AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive();
+}
+
+/*!
Sets checkAvailableSpace based on value of \a check.
*/
void PackageManagerCore::setCheckAvailableSpace(bool check)
@@ -2925,12 +3296,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());
@@ -2958,10 +3339,10 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
<< humanReadableSize(repositorySize);
if (d->m_checkAvailableSpace) {
- const VolumeInfo tempVolume = VolumeInfo::fromPath(QDir::tempPath());
+ const VolumeInfo cacheVolume = VolumeInfo::fromPath(settings().localCachePath());
const VolumeInfo targetVolume = VolumeInfo::fromPath(value(scTargetDir));
- const quint64 tempVolumeAvailableSize = tempVolume.availableSize();
+ const quint64 cacheVolumeAvailableSize = cacheVolume.availableSize();
const quint64 installVolumeAvailableSize = targetVolume.availableSize();
// at the moment there is no better way to check this
@@ -2972,58 +3353,62 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
return true;
}
- const bool tempOnSameVolume = (targetVolume == tempVolume);
- if (tempOnSameVolume) {
- qDebug() << "Tmp and install directories are on the same volume. Volume mount point:"
+ const bool cacheOnSameVolume = (targetVolume == cacheVolume);
+ if (cacheOnSameVolume) {
+ qDebug() << "Cache and install directories are on the same volume. Volume mount point:"
<< targetVolume.mountPath() << "Free space available:"
<< humanReadableSize(installVolumeAvailableSize);
} else {
- qDebug() << "Tmp is on a different volume than the installation directory. Tmp volume mount point:"
- << tempVolume.mountPath() << "Free space available:"
- << humanReadableSize(tempVolumeAvailableSize) << "Install volume mount point:"
+ qDebug() << "Cache is on a different volume than the installation directory. Cache volume mount point:"
+ << cacheVolume.mountPath() << "Free space available:"
+ << humanReadableSize(cacheVolumeAvailableSize) << "Install volume mount point:"
<< targetVolume.mountPath() << "Free space available:"
<< humanReadableSize(installVolumeAvailableSize);
}
- if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
- message = tr("Not enough disk space to store temporary files and the "
+ if (cacheOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
+ 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 (tempVolumeAvailableSize < tempRequired) {
- message = tr("Not enough disk space to store temporary files! %1 are available, "
- "while the minimum required is %2.").arg(humanReadableSize(tempVolumeAvailableSize),
- humanReadableSize(tempRequired));
+ if (cacheVolumeAvailableSize < tempRequired) {
+ 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));
return false;
}
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, tr("Installation will use %1 of disk space.")
+ 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."))
.arg(humanReadableSize(requiredDiskSpace()))).simplified();
return true;
@@ -3082,6 +3467,10 @@ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const
}
/*!
+ \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To manually check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
Sets additional \a processes that can run when
updating with the maintenance tool.
@@ -3093,6 +3482,10 @@ void PackageManagerCore::setAllowedRunningProcesses(const QStringList &processes
}
/*!
+ \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To manually check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
Returns processes that are allowed to run when
updating with the maintenance tool.
@@ -3300,9 +3693,10 @@ bool PackageManagerCore::performOperation(const QString &name, const QStringList
*/
bool PackageManagerCore::versionMatches(const QString &version, const QString &requirement)
{
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString comparator = compEx.exactMatch(requirement) ? compEx.cap(1) : QLatin1String("=");
- const QString ver = compEx.exactMatch(requirement) ? compEx.cap(2) : requirement;
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ const QRegularExpressionMatch match = compEx.match(requirement);
+ const QString comparator = match.hasMatch() ? match.captured(1) : QLatin1String("=");
+ const QString ver = match.hasMatch() ? match.captured(2) : requirement;
const bool allowEqual = comparator.contains(QLatin1Char('='));
const bool allowLess = comparator.contains(QLatin1Char('<'));
@@ -3398,6 +3792,17 @@ void PackageManagerCore::setInstallerBaseBinary(const QString &path)
}
/*!
+ Returns the value of \c installerbase binary which is used when writing
+ the maintenance tool. Value can be empty.
+
+ \sa setInstallerBaseBinary()
+*/
+QString PackageManagerCore::installerBaseBinary() const
+{
+ return d->m_installerBaseBinaryUnreplaced;
+}
+
+/*!
Sets the \c installerbase binary located at \a path to use when writing the
offline installer. Setting this makes it possible to run the offline generator
in cases where we are not running a real installer, i.e. when executing autotests.
@@ -3420,14 +3825,18 @@ void PackageManagerCore::addResourcesForOfflineGeneration(const QString &rcPath)
/*!
Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is
- returned. Additionally, on Windows, \a key can be a registry key.
+ returned. Additionally, on Windows, \a key can be a registry key. Optionally, you can specify the
+ \a format of the registry key. By default the \a format is QSettings::NativeFormat.
+ For accessing the 32-bit system registry from a 64-bit application running on 64-bit Windows,
+ use QSettings::Registry32Format. For accessing the 64-bit system registry from a 32-bit
+ application running on 64-bit Windows, use QSettings::Registry64Format.
\sa {installer::value}{installer.value}
\sa setValue(), containsValue(), valueChanged()
*/
-QString PackageManagerCore::value(const QString &key, const QString &defaultValue) const
+QString PackageManagerCore::value(const QString &key, const QString &defaultValue, const int &format) const
{
- return d->m_data.value(key, defaultValue).toString();
+ return d->m_data.value(key, defaultValue, static_cast<QSettings::Format>(format)).toString();
}
/*!
@@ -3592,6 +4001,14 @@ QString PackageManagerCore::offlineBinaryName() const
}
/*!
+ Add new \a source for looking component aliases.
+*/
+void PackageManagerCore::addAliasSource(const AliasSource &source)
+{
+ d->m_aliasSources.insert(source);
+}
+
+/*!
\sa {installer::setInstaller}{installer.setInstaller}
\sa isInstaller(), setUpdater(), setPackageManager()
*/
@@ -3650,6 +4067,7 @@ bool PackageManagerCore::isUninstaller() const
void PackageManagerCore::setUpdater()
{
d->m_magicBinaryMarker = BinaryContent::MagicUpdaterMarker;
+ d->m_componentByNameHash.clear();
emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -3670,6 +4088,7 @@ bool PackageManagerCore::isUpdater() const
void PackageManagerCore::setPackageManager()
{
d->m_magicBinaryMarker = BinaryContent::MagicPackageManagerMarker;
+ d->m_componentByNameHash.clear();
emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -3691,6 +4110,7 @@ bool PackageManagerCore::isPackageManager() const
void PackageManagerCore::setOfflineGenerator()
{
d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3709,6 +4129,7 @@ bool PackageManagerCore::isOfflineGenerator() const
void PackageManagerCore::setPackageViewer()
{
d->m_magicMarkerSupplement = BinaryContent::PackageViewer;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3722,6 +4143,17 @@ bool PackageManagerCore::isPackageViewer() const
}
/*!
+ Resets the binary marker supplement of the installer to \c Default.
+ The supplement enables or disables additional features on top of the binary
+ marker state (\c Installer, \c Updater, \c PackageManager, \c Uninstaller).
+*/
+void PackageManagerCore::resetBinaryMarkerSupplement()
+{
+ d->m_magicMarkerSupplement = BinaryContent::Default;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
Sets the installer magic binary marker based on \a magicMarker and
userSetBinaryMarker to \c true.
*/
@@ -3885,9 +4317,9 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
if (settings().allowUnstableComponents()) {
// Check if there are sha checksum mismatch. Component will still show in install tree
// but is unselectable.
- foreach (const QString packageName, d->m_metadataJob.shaMismatchPackages()) {
- if (packageName == component->name()) {
- const QString errorString = QLatin1String("SHA mismatch detected for component ") + packageName;
+ foreach (const QString pkgName, d->m_metadataJob.shaMismatchPackages()) {
+ if (pkgName == component->name()) {
+ const QString errorString = QLatin1String("SHA mismatch detected for component ") + pkgName;
d->m_pendingUnstableComponents.insert(component->name(),
QPair<Component::UnstableError, QString>(Component::ShaMismatch, errorString));
}
@@ -3896,33 +4328,18 @@ 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.repositoryForDirectory(localPath);
+ const Repository repo = d->m_metadataJob.repositoryForCacheDirectory(localPath);
if (repo.isValid()) {
component->setRepositoryUrl(repo.url());
component->setValue(QLatin1String("username"), repo.username());
component->setValue(QLatin1String("password"), repo.password());
}
- // add downloadable archive from xml
- const QStringList downloadableArchives = data.package->data(scDownloadableArchives).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
-
- if (component->isFromOnlineRepository()) {
- foreach (const QString downloadableArchive, downloadableArchives)
- component->addDownloadableArchive(downloadableArchive);
- }
-
- const QStringList componentsToReplace = data.package->data(scReplaces).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ if (component->isFromOnlineRepository())
+ component->addDownloadableArchives(data.package->data(scDownloadableArchives).toString());
+ const QStringList componentsToReplace = QInstaller::splitStringWithComma(data.package->data(scReplaces).toString());
if (!componentsToReplace.isEmpty()) {
// Store the component (this is a component that replaces others) and all components that
// this one will replace.
@@ -4015,134 +4432,148 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
{
emit startAllComponentsReset();
- d->clearAllComponentLists();
- QHash<QString, QInstaller::Component*> allComponents;
+ try {
+ d->clearAllComponentLists();
+ QHash<QString, QInstaller::Component*> allComponents;
- Data data;
- data.components = &allComponents;
- data.installedPackages = &locals;
+ Data data;
+ data.components = &allComponents;
+ data.installedPackages = &locals;
- QMap<QString, QString> remoteTreeNameComponents;
- QMap<QString, QString> allTreeNameComponents;
+ QMap<QString, QString> remoteTreeNameComponents;
+ QMap<QString, QString> allTreeNameComponents;
- std::function<bool(PackagesList *, bool)> loadRemotePackages;
- loadRemotePackages = [&](PackagesList *treeNamePackages, bool firstRun) -> bool {
- foreach (Package *const package, (firstRun ? remotes : *treeNamePackages)) {
- if (d->statusCanceledOrFailed())
- return false;
+ std::function<bool(PackagesList *, bool)> loadRemotePackages;
+ loadRemotePackages = [&](PackagesList *treeNamePackages, bool firstRun) -> bool {
+ foreach (Package *const package, (firstRun ? remotes : *treeNamePackages)) {
+ if (d->statusCanceledOrFailed())
+ return false;
- if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString()))
- continue;
+ if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString()))
+ continue;
- if (firstRun && !package->data(scTreeName)
- .value<QPair<QString, bool>>().first.isEmpty()) {
- // Package has a tree name, leave for later
- treeNamePackages->append(package);
- continue;
- }
+ if (firstRun && !package->data(scTreeName)
+ .value<QPair<QString, bool>>().first.isEmpty()) {
+ // Package has a tree name, leave for later
+ treeNamePackages->append(package);
+ continue;
+ }
- QScopedPointer<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
- data.package = package;
- remoteComponent->loadDataFromPackage(*package);
- if (updateComponentData(data, remoteComponent.data())) {
- // Create a list where is name and treename. Repo can contain a package with
- // a different treename of component which is already installed. We don't want
- // to move already installed local packages.
- const QString treeName = remoteComponent->value(scTreeName);
- if (!treeName.isEmpty())
- remoteTreeNameComponents.insert(remoteComponent->name(), treeName);
- const QString name = remoteComponent->treeName();
- allComponents.insert(name, remoteComponent.take());
+ std::unique_ptr<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
+ data.package = package;
+ remoteComponent->loadDataFromPackage(*package);
+ if (updateComponentData(data, remoteComponent.get())) {
+ // Create a list where is name and treename. Repo can contain a package with
+ // a different treename of component which is already installed. We don't want
+ // to move already installed local packages.
+ const QString treeName = remoteComponent->value(scTreeName);
+ if (!treeName.isEmpty())
+ remoteTreeNameComponents.insert(remoteComponent->name(), treeName);
+ const QString name = remoteComponent->treeName();
+ allComponents.insert(name, remoteComponent.release());
+ }
}
+ // Second pass with leftover packages
+ return firstRun ? loadRemotePackages(treeNamePackages, false) : true;
+ };
+
+ {
+ // Loading remote package data is performed in two steps: first, components without
+ // - and then components with tree names. This is to ensure components with tree
+ // names do not replace other components when registering fails to a name conflict.
+ PackagesList treeNamePackagesTmp;
+ if (!loadRemotePackages(&treeNamePackagesTmp, true))
+ return false;
}
- // Second pass with leftover packages
- return firstRun ? loadRemotePackages(treeNamePackages, false) : true;
- };
+ allTreeNameComponents = remoteTreeNameComponents;
- {
- // Loading remote package data is performed in two steps: first, components without
- // - and then components with tree names. This is to ensure components with tree
- // names do not replace other components when registering fails to a name conflict.
- PackagesList treeNamePackagesTmp;
- if (!loadRemotePackages(&treeNamePackagesTmp, true))
- return false;
- }
- allTreeNameComponents = remoteTreeNameComponents;
+ foreach (auto &package, locals) {
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!d->m_localVirtualComponents.contains(package.name))
+ d->m_localVirtualComponents.append(package.name);
+ }
- foreach (auto &package, locals) {
- if (package.virtualComp && package.autoDependencies.isEmpty()) {
- if (!d->m_localVirtualComponents.contains(package.name))
- d->m_localVirtualComponents.append(package.name);
- }
+ std::unique_ptr<QInstaller::Component> localComponent(new QInstaller::Component(this));
+ localComponent->loadDataFromPackage(package);
+ const QString name = localComponent->treeName();
+
+ // 1. Component has a treename in local but not in remote, add with local treename
+ if (!remoteTreeNameComponents.contains(localComponent->name()) && !localComponent->value(scTreeName).isEmpty()) {
+ delete allComponents.take(localComponent->name());
+ // 2. Component has different treename in local and remote, add with local treename
+ } else if (remoteTreeNameComponents.contains(localComponent->name())) {
+ const QString remoteTreeName = remoteTreeNameComponents.value(localComponent->name());
+ const QString localTreeName = localComponent->value(scTreeName);
+ if (remoteTreeName != localTreeName) {
+ delete allComponents.take(remoteTreeNameComponents.value(localComponent->name()));
+ } else {
+ // 3. Component has same treename in local and remote, don't add the component again.
+ continue;
+ }
+ // 4. Component does not have treename in local or remote, don't add the component again.
+ } else if (allComponents.contains(localComponent->name())) {
+ Component *const component = allComponents.value(localComponent->name());
+ if (component->value(scTreeName).isEmpty() && localComponent->value(scTreeName).isEmpty())
+ continue;
+ }
+ // 5. Remote has treename for a different component that is already reserved
+ // by this local component, Or, remote adds component without treename
+ // but it conflicts with a local treename.
+ if (allComponents.contains(name)) {
+ const QString key = remoteTreeNameComponents.key(name);
+ qCritical() << "Cannot register component" << (key.isEmpty() ? name : key)
+ << "with name" << name << "! Component with identifier" << name
+ << "already exists.";
+
+ if (!key.isEmpty())
+ allTreeNameComponents.remove(key);
+
+ // Try to re-add the remote component as unstable
+ if (!key.isEmpty() && !allComponents.contains(key) && settings().allowUnstableComponents()) {
+ qCDebug(lcInstallerInstallLog)
+ << "Registering component with the original indetifier:" << key;
+
+ Component *component = allComponents.take(name);
+ component->removeValue(scTreeName);
+ const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
- QScopedPointer<QInstaller::Component> localComponent(new QInstaller::Component(this));
- localComponent->loadDataFromPackage(package);
- const QString name = localComponent->treeName();
-
- // 1. Component has a treename in local but not in remote, add with local treename
- if (!remoteTreeNameComponents.contains(localComponent->name()) && !localComponent->value(scTreeName).isEmpty()) {
- delete allComponents.take(localComponent->name());
- // 2. Component has different treename in local and remote, add with local treename
- } else if (remoteTreeNameComponents.contains(localComponent->name())) {
- const QString remoteTreeName = remoteTreeNameComponents.value(localComponent->name());
- const QString localTreeName = localComponent->value(scTreeName);
- if (remoteTreeName != localTreeName) {
- delete allComponents.take(remoteTreeNameComponents.value(localComponent->name()));
- } else {
- // 3. Component has same treename in local and remote, don't add the component again.
- continue;
+ allComponents.insert(key, component);
+ } else {
+ delete allComponents.take(name);
+ }
}
- // 4. Component does not have treename in local or remote, don't add the component again.
- } else if (allComponents.contains(localComponent->name())) {
- Component *const component = allComponents.value(localComponent->name());
- if (component->value(scTreeName).isEmpty() && localComponent->value(scTreeName).isEmpty())
- continue;
+
+ const QString treeName = localComponent->value(scTreeName);
+ if (!treeName.isEmpty())
+ allTreeNameComponents.insert(localComponent->name(), treeName);
+ allComponents.insert(name, localComponent.release());
}
- // 5. Remote has treename for a different component that is already reserved
- // by this local component, Or, remote adds component without treename
- // but it conflicts with a local treename.
- if (allComponents.contains(name)) {
- const QString key = remoteTreeNameComponents.key(name);
- qCritical() << "Cannot register component" << (key.isEmpty() ? name : key)
- << "with name" << name << "! Component with identifier" << name
- << "already exists.";
-
- if (!key.isEmpty())
- allTreeNameComponents.remove(key);
-
- // Try to re-add the remote component as unstable
- if (!key.isEmpty() && !allComponents.contains(key) && settings().allowUnstableComponents()) {
- qCDebug(lcInstallerInstallLog)
- << "Registering component with the original indetifier:" << key;
- Component *component = allComponents.take(name);
- component->removeValue(scTreeName);
- const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
- d->m_pendingUnstableComponents.insert(component->name(),
- QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
+ // store all components that got a replacement
+ storeReplacedComponents(allComponents, data, &allTreeNameComponents);
- allComponents.insert(key, component);
- } else {
- delete allComponents.take(name);
- }
- }
+ // Move children of treename components
+ createAutoTreeNames(allComponents, allTreeNameComponents);
- const QString treeName = localComponent->value(scTreeName);
- if (!treeName.isEmpty())
- allTreeNameComponents.insert(localComponent->name(), treeName);
- allComponents.insert(name, localComponent.take());
- }
+ if (!d->buildComponentTree(allComponents, true))
+ return false;
- // store all components that got a replacement
- storeReplacedComponents(allComponents, data, &allTreeNameComponents);
+ d->commitPendingUnstableComponents();
- // Move children of treename components
- createAutoTreeNames(allComponents, allTreeNameComponents);
+ if (!d->buildComponentAliases())
+ return false;
- if (!d->buildComponentTree(allComponents, true))
- return false;
+ } catch (const Error &error) {
+ d->clearAllComponentLists();
+ d->setStatus(PackageManagerCore::Failure, error.message());
- d->commitPendingUnstableComponents();
+ // TODO: make sure we remove all message boxes inside the library at some point.
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
emit finishAllComponentsReset(d->m_rootComponents);
return true;
@@ -4152,104 +4583,105 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
{
emit startUpdaterComponentsReset();
- d->clearUpdaterComponentLists();
- QHash<QString, QInstaller::Component *> components;
+ try {
+ d->clearUpdaterComponentLists();
+ QHash<QString, QInstaller::Component *> components;
- Data data;
- data.components = &components;
- data.installedPackages = &locals;
+ Data data;
+ data.components = &components;
+ data.installedPackages = &locals;
- setFoundEssentialUpdate(false);
- LocalPackagesMap installedPackages = locals;
- QStringList replaceMes;
+ setFoundEssentialUpdate(false);
+ LocalPackagesMap installedPackages = locals;
+ QStringList replaceMes;
- foreach (Package *const update, remotes) {
- if (d->statusCanceledOrFailed())
- return false;
+ foreach (Package *const update, remotes) {
+ if (d->statusCanceledOrFailed())
+ return false;
- if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
- continue;
+ if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
+ continue;
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
- data.package = update;
- component->loadDataFromPackage(*update);
- if (updateComponentData(data, component.data())) {
- // Keep a reference so we can resolve dependencies during update.
- d->m_updaterComponentsDeps.append(component.take());
-
-// const QString isNew = update->data(scNewComponent).toString();
-// if (isNew.toLower() != scTrue)
-// continue;
-
- const QString &name = d->m_updaterComponentsDeps.last()->name();
- const QString replaces = data.package->data(scReplaces).toString();
- installedPackages.take(name); // remove from local installed packages
-
- bool isValidUpdate = locals.contains(name);
- if (!replaces.isEmpty()) {
- const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
- Qt::SkipEmptyParts);
- foreach (const QString &possibleName, possibleNames) {
- if (locals.contains(possibleName)) {
- isValidUpdate = true;
- replaceMes << possibleName;
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
+ data.package = update;
+ component->loadDataFromPackage(*update);
+ if (updateComponentData(data, component.get())) {
+ // Keep a reference so we can resolve dependencies during update.
+ d->m_updaterComponentsDeps.append(component.release());
+
+ // const QString isNew = update->data(scNewComponent).toString();
+ // if (isNew.toLower() != scTrue)
+ // continue;
+
+ const QString &name = d->m_updaterComponentsDeps.last()->name();
+ const QString replaces = data.package->data(scReplaces).toString();
+ installedPackages.take(name); // remove from local installed packages
+
+ bool isValidUpdate = locals.contains(name);
+ if (!replaces.isEmpty()) {
+ const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
+ Qt::SkipEmptyParts);
+ foreach (const QString &possibleName, possibleNames) {
+ if (locals.contains(possibleName)) {
+ isValidUpdate = true;
+ replaceMes << possibleName;
+ }
}
}
- }
- // break if the update is not valid and if it's not the maintenance tool (we might get an update
- // for the maintenance tool even if it's not currently installed - possible offline installation)
- if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse))
- continue; // Update for not installed package found, skip it.
+ // break if the update is not valid and if it's not the maintenance tool (we might get an update
+ // for the maintenance tool even if it's not currently installed - possible offline installation)
+ if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse))
+ continue; // Update for not installed package found, skip it.
- const LocalPackage &localPackage = locals.value(name);
- if (!d->packageNeedsUpdate(localPackage, update))
- continue;
- // It is quite possible that we may have already installed the update. Lets check the last
- // update date of the package and the release date of the update. This way we can compare and
- // figure out if the update has been installed or not.
- const QDate updateDate = update->data(scReleaseDate).toDate();
- if (localPackage.lastUpdateDate > updateDate)
- continue;
+ const LocalPackage &localPackage = locals.value(name);
+ if (!d->packageNeedsUpdate(localPackage, update))
+ continue;
+ // It is quite possible that we may have already installed the update. Lets check the last
+ // update date of the package and the release date of the update. This way we can compare and
+ // figure out if the update has been installed or not.
+ const QDate updateDate = update->data(scReleaseDate).toDate();
+ if (localPackage.lastUpdateDate > updateDate)
+ continue;
- if (update->data(scEssential, scFalse).toString().toLower() == scTrue ||
- update->data(scForcedUpdate, scFalse).toString().toLower() == scTrue) {
- setFoundEssentialUpdate(true);
- }
+ if (update->data(scEssential, scFalse).toString().toLower() == scTrue ||
+ update->data(scForcedUpdate, scFalse).toString().toLower() == scTrue) {
+ setFoundEssentialUpdate(true);
+ }
- // this is not a dependency, it is a real update
- components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ // this is not a dependency, it is a real update
+ components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ }
}
- }
- QHash<QString, QInstaller::Component *> localReplaceMes;
- foreach (const QString &key, installedPackages.keys()) {
- QInstaller::Component *component = new QInstaller::Component(this);
- component->loadDataFromPackage(installedPackages.value(key));
- d->m_updaterComponentsDeps.append(component);
- }
-
- foreach (const QString &key, locals.keys()) {
- LocalPackage package = locals.value(key);
- if (package.virtualComp && package.autoDependencies.isEmpty()) {
- if (!d->m_localVirtualComponents.contains(package.name))
- d->m_localVirtualComponents.append(package.name);
- }
- // Keep a list of local components that should be replaced
- // Remove from components list - we don't want to update the component
- // as it is replaced by other component
- if (replaceMes.contains(key)) {
+ QHash<QString, QInstaller::Component *> localReplaceMes;
+ foreach (const QString &key, installedPackages.keys()) {
QInstaller::Component *component = new QInstaller::Component(this);
- component->loadDataFromPackage(locals.value(key));
- localReplaceMes.insert(component->name(), component);
- delete components.take(component->name());
+ component->loadDataFromPackage(installedPackages.value(key));
+ d->m_updaterComponentsDeps.append(component);
+ }
+
+ foreach (const QString &key, locals.keys()) {
+ LocalPackage package = locals.value(key);
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!d->m_localVirtualComponents.contains(package.name))
+ d->m_localVirtualComponents.append(package.name);
+ }
+ // Keep a list of local components that should be replaced
+ // Remove from components list - we don't want to update the component
+ // as it is replaced by other component
+ if (replaceMes.contains(key)) {
+ QInstaller::Component *component = new QInstaller::Component(this);
+ component->loadDataFromPackage(locals.value(key));
+ localReplaceMes.insert(component->name(), component);
+ delete components.take(component->name());
+ }
}
- }
- // store all components that got a replacement, but do not modify the components list
- storeReplacedComponents(localReplaceMes.unite(components), data);
+ // store all components that got a replacement, but do not modify the components list
+ localReplaceMes.insert(components);
+ storeReplacedComponents(localReplaceMes, data);
- try {
if (!components.isEmpty()) {
// append all components w/o parent to the direct list
foreach (QInstaller::Component *component, components) {
@@ -4257,22 +4689,26 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
}
// after everything is set up, load the scripts
+ if (!d->loadComponentScripts(components))
+ return false;
+
foreach (QInstaller::Component *component, components) {
if (d->statusCanceledOrFailed())
return false;
- component->loadComponentScript();
- if (!component->isUnstable() && component->autoDependencies().isEmpty())
+ if (!component->isUnstable())
component->setCheckState(Qt::Checked);
}
+ // even for possible dependencies we need to load the scripts for example to get archives
+ if (!d->loadComponentScripts(d->m_updaterComponentsDeps))
+ return false;
+
// after everything is set up, check installed components
foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) {
if (d->statusCanceledOrFailed())
return false;
- // even for possible dependency we need to load the script for example to get archives
- component->loadComponentScript();
- if (component->isInstalled() && !component->autoDependencies().isEmpty()) {
+ if (component->isInstalled()) {
// since we do not put them into the model, which would force a update of e.g. tri state
// components, we have to check all installed components ourselves
if (!component->isUnstable())
@@ -4333,14 +4769,19 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
if (treeNameComponents.isEmpty())
return;
- QHash<QString, Component *> componentsTemp = components;
- for (auto *component : qAsConst(components)) {
+ QHash<QString, Component *> componentsTemp;
+ QMutableHashIterator<QString, Component* > i(components);
+ while (i.hasNext()) {
+ i.next();
+ Component *component = i.value();
if (component->treeName() != component->name()) // already handled
continue;
QString newName;
// Check treename candidates, keep the name closest to a leaf component
- for (auto &name : treeNameComponents.keys()) {
+ QMap<QString, QString>::const_iterator j;
+ for (j = treeNameComponents.begin(); j != treeNameComponents.end(); ++j) {
+ const QString name = j.key();
if (!component->name().startsWith(name))
continue;
@@ -4348,8 +4789,8 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
if (!(parent && parent->treeNameMoveChildren()))
continue; // TreeName only applied to parent
- if (newName.split(QLatin1Char('.'), QString::SkipEmptyParts).count()
- > name.split(QLatin1Char('.'), QString::SkipEmptyParts).count()) {
+ if (newName.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()
+ > name.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()) {
continue;
}
newName = name;
@@ -4375,16 +4816,16 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
d->m_pendingUnstableComponents.insert(component->name(),
QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
} else {
- componentsTemp.remove(componentsTemp.key(component));
+ i.remove();
}
continue;
}
component->setValue(scAutoTreeName, treeName);
- componentsTemp.remove(componentsTemp.key(component));
+ i.remove();
componentsTemp.insert(treeName, component);
}
- components = componentsTemp;
+ components.insert(componentsTemp);
}
void PackageManagerCore::restoreCheckState()
@@ -4451,10 +4892,7 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con
ComponentModel::tr("Release Date"));
model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal,
ComponentModel::tr("Size"));
- connect(model, &ComponentModel::modelCheckStateChanged,
- this, &PackageManagerCore::componentsToInstallNeedsRecalculation);
- connect(model, &ComponentModel::componentsCheckStateChanged,
- this, &PackageManagerCore::calculateUserSelectedComponentsToInstall);
+
return model;
}
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index 1cc62cac5..170ddf557 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.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.
@@ -29,6 +29,8 @@
#define PACKAGEMANAGERCORE_H
#include "binaryformat.h"
+#include "binarycontent.h"
+#include "component.h"
#include "protocol.h"
#include "repository.h"
#include "qinstallerglobal.h"
@@ -38,16 +40,20 @@
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QStringList>
-#include <QtCore/QVector>
+#include <QtCore/QList>
+#include <QSettings>
+#include <QModelIndex>
namespace QInstaller {
-class Component;
+struct AliasSource;
class ComponentModel;
+class ComponentAlias;
class ScriptEngine;
class PackageManagerCorePrivate;
class PackageManagerProxyFactory;
class Settings;
+class ComponentSortFilterProxyModel;
// -- PackageManagerCore
@@ -62,6 +68,7 @@ class INSTALLER_EXPORT PackageManagerCore : public QObject
public:
PackageManagerCore();
PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &ops,
+ const QString &datFilename = QString(),
const QString &socketName = QString(),
const QString &key = QLatin1String(Protocol::DefaultAuthorizationKey),
Protocol::Mode mode = Protocol::Mode::Production,
@@ -77,7 +84,8 @@ public:
Canceled = 3,
Unfinished = 4,
ForceUpdate = 5,
- EssentialUpdated = 6
+ EssentialUpdated = 6,
+ NoPackagesFound = 7
};
Status status() const;
QString error() const;
@@ -102,6 +110,14 @@ public:
AllNoReplacements = (Root | Descendants | Dependencies),
All = (Root | Descendants | Dependencies | Replacements)
};
+
+ struct DownloadItem
+ {
+ QString fileName;
+ QString sourceUrl;
+ bool checkSha1CheckSum;
+ };
+
Q_DECLARE_FLAGS(ComponentTypes, ComponentType)
static QFont virtualComponentsFont();
@@ -134,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();
@@ -164,6 +181,7 @@ public:
Q_INVOKABLE static QString findPath(const QString &name, const QStringList &paths = QStringList());
Q_INVOKABLE void setInstallerBaseBinary(const QString &path);
+ QString installerBaseBinary() const;
void setOfflineBaseBinary(const QString &path);
void addResourcesForOfflineGeneration(const QString &rcPath);
@@ -171,7 +189,7 @@ public:
// parameter handling
Q_INVOKABLE bool containsValue(const QString &key) const;
Q_INVOKABLE void setValue(const QString &key, const QString &value);
- Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const;
+ Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString(), const int &format = QSettings::NativeFormat) const;
Q_INVOKABLE QStringList values(const QString &key, const QStringList &defaultValue = QStringList()) const;
Q_INVOKABLE QString key(const QString &value) const;
@@ -190,12 +208,20 @@ public:
void setOfflineBinaryName(const QString &name);
QString offlineBinaryName() const;
- bool testChecksum() const;
- void setTestChecksum(bool test);
+ void addAliasSource(const AliasSource &source);
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);
@@ -232,27 +258,42 @@ public:
void appendUpdaterComponent(Component *components);
QList<Component *> components(ComponentTypes mask, const QString &regexp = QString()) const;
- Component *componentByName(const QString &identifier) const;
+ Q_INVOKABLE QInstaller::Component *componentByName(const QString &identifier) const;
+ Q_INVOKABLE QList<QInstaller::Component *> components(const QString &regexp = QString()) const;
+
+ ComponentAlias *aliasByName(const QString &name) const;
Q_INVOKABLE bool calculateComponentsToInstall() const;
QList<Component*> orderedComponentsToInstall() const;
- bool calculateComponents(QString *displayString);
+
+ Q_INVOKABLE bool recalculateAllComponents();
+ QString componentResolveReasons() const;
Q_INVOKABLE bool calculateComponentsToUninstall() const;
QList<Component*> componentsToUninstall() const;
+ QList<Component *> componentsMarkedForInstallation() const;
+ QList<ComponentAlias *> aliasesMarkedForInstallation() const;
+
QString componentsToInstallError() const;
+ QString componentsToUninstallError() const;
QString installReason(Component *component) const;
QString uninstallReason(Component *component) const;
QList<Component*> dependees(const Component *component) const;
- QList<Component*> installDependants(const Component *component) const;
+ bool isDependencyForRequestedComponent(const Component *component) const;
+ QStringList localDependenciesToComponent(const Component *component) const;
ComponentModel *defaultComponentModel() const;
ComponentModel *updaterComponentModel() const;
+ ComponentSortFilterProxyModel *componentSortFilterProxyModel();
+
void listInstalledPackages(const QString &regexp = QString());
- void listAvailablePackages(const QString &regexp = QString(),
+ bool listAvailablePackages(const QString &regexp = QString(),
const QHash<QString, QString> &filters = QHash<QString, QString>());
+ bool listAvailableAliases(const QString &regexp = QString());
+
+ PackageManagerCore::Status searchAvailableUpdates();
PackageManagerCore::Status updateComponentsSilently(const QStringList &componentsToUpdate);
PackageManagerCore::Status installSelectedComponentsSilently(const QStringList& components);
PackageManagerCore::Status installDefaultComponentsSilently();
@@ -280,6 +321,8 @@ public:
void setPackageViewer();
Q_INVOKABLE bool isPackageViewer() const;
+ void resetBinaryMarkerSupplement();
+
void setUserSetBinaryMarker(qint64 magicMarker);
Q_INVOKABLE bool isUserSetBinaryMarker() const;
@@ -294,9 +337,11 @@ public:
Q_INVOKABLE bool gainAdminRights();
Q_INVOKABLE void dropAdminRights();
+ 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;
@@ -340,9 +385,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();
@@ -353,9 +409,8 @@ public Q_SLOTS:
void languageChanged();
void setCompleteUninstallation(bool complete);
void cancelMetaInfoJob();
- void componentsToInstallNeedsRecalculation();
- void calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes);
- void clearComponentsToInstallCalculated();
+ void componentsToInstallNeedsRecalculation(); // TODO: deprecated, remove
+ void clearComponentsToInstallCalculated() {} // TODO: deprecated, remove
Q_SIGNALS:
void aboutCalculateComponentsToInstall() const;
@@ -363,11 +418,9 @@ 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);
+ void defaultTranslationsLoadedForLanguage(QLocale lang);
void currentPageChanged(int page);
void finishButtonClicked();
@@ -430,17 +483,18 @@ private:
QString findDisplayVersion(const QString &componentName, const QHash<QString, QInstaller::Component*> &components,
const QString& versionKey, QHash<QString, bool> &visited);
ComponentModel *componentModel(PackageManagerCore *core, const QString &objectName) const;
- QList<Component *> componentsMarkedForInstallation() const;
bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesMap installedPackages);
bool componentUninstallableFromCommandLine(const QString &componentName);
- bool checkComponentsForInstallation(const QStringList &components, 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 45fe890f9..10ca27d00 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.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.
@@ -45,6 +45,7 @@
#include "qsettingswrapper.h"
#include "installercalculator.h"
#include "uninstallercalculator.h"
+#include "componentalias.h"
#include "componentchecker.h"
#include "globals.h"
#include "binarycreator.h"
@@ -56,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>
@@ -123,54 +126,27 @@ static QStringList checkRunningProcessesFromList(const QStringList &processList)
static void deferredRename(const QString &oldName, const QString &newName, bool restart = false)
{
#ifdef Q_OS_WIN
- QStringList arguments;
-
- // Check if .vbs extension can be used for running renaming script. If not, create own extension
- QString extension = QLatin1String(".vbs");
- QSettingsWrapper settingRoot(QLatin1String("HKEY_CLASSES_ROOT\\.vbs"), QSettingsWrapper::NativeFormat);
- if (settingRoot.value(QLatin1String(".")).toString() != QLatin1String("VBSFile")) {
- extension = QLatin1String(".qtInstaller");
- QSettingsWrapper settingsUser(QLatin1String("HKEY_CURRENT_USER\\Software\\Classes"), QSettingsWrapper::NativeFormat);
- QString value = settingsUser.value(extension).toString();
- if (value != QLatin1String("VBSFile"))
- settingsUser.setValue(extension, QLatin1String("VBSFile"));
- }
- QTemporaryFile f(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX%1")).arg(extension));
+ const QString currentExecutable = QCoreApplication::applicationFilePath();
+ const QString tmpExecutable = generateTemporaryFileName(currentExecutable);
- QInstaller::openForWrite(&f);
- f.setAutoRemove(false);
+ QFile::rename(currentExecutable, tmpExecutable);
+ QFile::rename(oldName, newName);
- arguments << QDir::toNativeSeparators(f.fileName()) << QDir::toNativeSeparators(oldName)
- << QDir::toNativeSeparators(QFileInfo(oldName).dir().absoluteFilePath(QFileInfo(newName)
- .fileName()));
-
- QTextStream batch(&f);
- batch.setCodec("UTF-16");
- batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
- batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n";
- batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]);
- batch << "on error resume next\n";
-
- batch << "while fso.FileExists(file)\n";
- batch << " fso.DeleteFile(file)\n";
- batch << " WScript.Sleep(1000)\n";
- batch << "wend\n";
- batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]);
+ QStringList arguments;
if (restart) {
- //Restart with same command line arguments as first executable
- QStringList commandLineArguments = QCoreApplication::arguments();
- batch << QString::fromLatin1("tmp.exec \"%1 --%2")
- .arg(arguments[2]).arg(CommandLineOptions::scStartUpdaterLong);
- //Skip the first argument as that is executable itself
- for (int i = 1; i < commandLineArguments.count(); i++) {
- batch << QString::fromLatin1(" %1").arg(commandLineArguments.at(i));
- }
- batch << QString::fromLatin1("\"\n");
+ // Restart with same command line arguments as first executable
+ arguments = QCoreApplication::arguments();
+ arguments.removeFirst(); // Remove program name
+ arguments.prepend(tmpExecutable);
+ arguments.prepend(QLatin1String("--")
+ + CommandLineOptions::scCleanupUpdate);
+ } else {
+ arguments.append(QLatin1String("--")
+ + CommandLineOptions::scCleanupUpdateOnly);
+ arguments.append(tmpExecutable);
}
- batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
+ QProcessWrapper::startDetached2(newName, arguments);
- QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo")
- << arguments[0]);
#else
QFile::remove(newName);
QFile::rename(oldName, newName);
@@ -178,26 +154,56 @@ 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
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
: m_updateFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(0) // initialize with pseudo marker
, m_magicMarkerSupplement(BinaryContent::Default)
- , m_componentsToInstallCalculated(false)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -205,37 +211,46 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(nullptr)
- , m_foundEssentialUpdate(false)
- , m_commandLineInstance(false)
- , m_defaultInstall(false)
- , m_userSetBinaryMarker(false)
- , m_checkAvailableSpace(true)
- , m_autoAcceptLicenses(false)
- , m_disableWriteMaintenanceTool(false)
- , m_autoConfirmCommand(false)
+ , m_datFileName(QString())
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
}
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
- const QList<OperationBlob> &performedOperations)
+ const QList<OperationBlob> &performedOperations, const QString &datFileName)
: m_updateFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(magicInstallerMaker)
, m_magicMarkerSupplement(BinaryContent::Default)
- , m_componentsToInstallCalculated(false)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -243,21 +258,21 @@ 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_foundEssentialUpdate(false)
- , m_commandLineInstance(false)
- , m_defaultInstall(false)
- , m_userSetBinaryMarker(false)
- , m_checkAvailableSpace(true)
- , m_autoAcceptLicenses(false)
- , m_disableWriteMaintenanceTool(false)
- , m_autoConfirmCommand(false)
+ , m_datFileName(datFileName)
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
foreach (const OperationBlob &operation, performedOperations) {
- QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
+ std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
.create(operation.name, core));
- if (op.isNull()) {
+ if (!op) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load unknown operation"
<< operation.name;
continue;
@@ -268,7 +283,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
<< operation.name;
continue;
}
- m_performedOperationsOld.append(op.take());
+ m_performedOperationsOld.append(op.release());
}
connect(this, &PackageManagerCorePrivate::installationStarted,
@@ -297,6 +312,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate()
qDeleteAll(m_performedOperationsCurrentSession);
delete m_updateFinder;
+ delete m_aliasFinder;
delete m_proxyFactory;
delete m_defaultModel;
@@ -402,10 +418,9 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
// after everything is set up, load the scripts if needed and create helper hashes
// for autodependency and dependency components for quicker search later
- foreach (QInstaller::Component *component, components) {
- if (loadScript)
- component->loadComponentScript();
- }
+ if (loadScript && !loadComponentScripts(components))
+ return false;
+
// now we can preselect components in the tree
foreach (QInstaller::Component *component, components) {
// set the checked state for all components without child (means without tristate)
@@ -429,10 +444,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
component->setCheckState(Qt::Checked);
clearInstallerCalculator();
- if (installerCalculator()->appendComponentsToInstall(components.values()) == false) {
- setStatus(PackageManagerCore::Failure, installerCalculator()->componentsToInstallError());
+ if (installerCalculator()->solve(components.values()) == false) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
- tr("Unresolved dependencies"), installerCalculator()->componentsToInstallError());
+ tr("Unresolved dependencies"), installerCalculator()->error());
return false;
}
@@ -458,12 +473,120 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
return true;
}
+bool PackageManagerCorePrivate::buildComponentAliases()
+{
+ // For now, aliases are only used for command line runs
+ if (!m_core->isCommandLineInstance())
+ return true;
+
+ {
+ const QList<ComponentAlias *> aliasList = componentAliases();
+ if (aliasList.isEmpty())
+ return true;
+
+ for (const auto *alias : aliasList) {
+ // Create a new alias object for package manager core to take ownership of
+ ComponentAlias *newAlias = new ComponentAlias(m_core);
+ for (const QString &key : alias->keys())
+ newAlias->setValue(key, alias->value(key));
+
+ m_componentAliases.insert(alias->name(), newAlias);
+ }
+ }
+ // After aliases are loaded, perform sanity checks:
+
+ // 1. Component check state is changed by alias selection, so store the initial state
+ storeCheckState();
+
+ Graph<QString> aliasGraph;
+ for (auto *alias : qAsConst(m_componentAliases)) {
+ aliasGraph.addNode(alias->name());
+ aliasGraph.addEdges(alias->name(),
+ QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) <<
+ QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+
+ if (!m_core->componentByName(alias->name())) {
+ // Name ok, select for sanity check calculation
+ alias->setSelected(true);
+ } else {
+ alias->setUnstable(ComponentAlias::ComponentNameConfict,
+ tr("Alias declares name that conflicts with an existing component \"%1\"")
+ .arg(alias->name()));
+ }
+ }
+
+ const QList<QString> sortedAliases = aliasGraph.sort();
+ // 2. Check for cyclic dependency errors
+ if (aliasGraph.hasCycle()) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"),
+ tr("Cyclic dependency between aliases \"%1\" and \"%2\" detected.")
+ .arg(aliasGraph.cycle().first, aliasGraph.cycle().second));
+
+ return false;
+ }
+
+ // 3. Test for required aliases and components, this triggers setting the
+ // alias unstable in case of a broken reference.
+ for (const auto &aliasName : sortedAliases) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias) // sortedAliases may contain dependencies that don't exist, we don't know it yet
+ continue;
+
+ alias->components();
+ alias->aliases();
+ }
+
+ clearInstallerCalculator();
+ // 4. Check for other errors preventing resolving components to install
+ if (!installerCalculator()->solve(m_componentAliases.values())) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"), installerCalculator()->error());
+
+ return false;
+ }
+
+ for (auto *alias : qAsConst(m_componentAliases))
+ alias->setSelected(false);
+
+ // 5. Restore original state
+ restoreCheckState();
+
+ return true;
+}
+
+template <typename T>
+bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
+{
+ infoMessage(nullptr, tr("Loading component scripts..."));
+
+ quint64 loadedComponents = 0;
+ for (auto *component : components) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ component->loadComponentScript(postScript);
+ ++loadedComponents;
+
+ const int currentProgress = qRound(double(loadedComponents) / components.count() * 100);
+ infoProgress(nullptr, currentProgress, 100);
+ qApp->processEvents();
+ }
+ return true;
+}
+
+template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
+
void PackageManagerCorePrivate::cleanUpComponentEnvironment()
{
m_componentReplaces.clear();
m_autoDependencyComponentHash.clear();
m_localDependencyComponentHash.clear();
m_localVirtualComponents.clear();
+ m_componentByNameHash.clear();
// clean up registered (downloaded) data
if (m_core->isMaintainer())
BinaryFormatEngineHandler::instance()->clear();
@@ -472,6 +595,10 @@ void PackageManagerCorePrivate::cleanUpComponentEnvironment()
// so we need to remove the current component script engine
delete m_componentScriptEngine;
m_componentScriptEngine = nullptr;
+
+ // Calculators become invalid after clearing components
+ clearInstallerCalculator();
+ clearUninstallerCalculator();
}
ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const
@@ -490,6 +617,9 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const
void PackageManagerCorePrivate::clearAllComponentLists()
{
+ qDeleteAll(m_componentAliases);
+ m_componentAliases.clear();
+
QList<QInstaller::Component*> toDelete;
toDelete << m_rootComponents << m_deletedReplacedComponents;
@@ -498,7 +628,7 @@ void PackageManagerCorePrivate::clearAllComponentLists()
m_deletedReplacedComponents.clear();
m_componentsToReplaceAllMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(toDelete);
cleanUpComponentEnvironment();
@@ -506,8 +636,10 @@ void PackageManagerCorePrivate::clearAllComponentLists()
void PackageManagerCorePrivate::clearUpdaterComponentLists()
{
- QSet<Component*> usedComponents =
- QSet<Component*>::fromList(m_updaterComponents + m_updaterComponentsDeps);
+
+ QSet<Component*> usedComponents(m_updaterComponents.begin(), m_updaterComponents.end());
+ usedComponents.unite(QSet<Component*>(m_updaterComponentsDeps.begin(),
+ m_updaterComponentsDeps.end()));
const QList<QPair<Component*, Component*> > list = m_componentsToReplaceUpdaterMode.values();
for (int i = 0; i < list.count(); ++i) {
@@ -523,7 +655,7 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists()
m_updaterDependencyReplacements.clear();
m_componentsToReplaceUpdaterMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(usedComponents);
cleanUpComponentEnvironment();
@@ -544,21 +676,6 @@ QHash<QString, QStringList> &PackageManagerCorePrivate::componentReplaces()
return m_componentReplaces;
}
-QList<Component*> PackageManagerCorePrivate::replacedComponentsByName(const QString &name)
-{
- // Creates a list of components which are replaced by component 'name'
- QList<Component*> replacedComponents;
- if (m_componentReplaces.contains(name)) {
- for (const QString &replacedComponentName : m_componentReplaces.value(name)) {
- Component *replacedComponent = m_core->componentByName(replacedComponentName,
- m_core->components(PackageManagerCore::ComponentType::All));
- if (replacedComponent)
- replacedComponents.append(replacedComponent);
- }
- }
- return replacedComponents;
-}
-
void PackageManagerCorePrivate::clearInstallerCalculator()
{
delete m_installerCalculator;
@@ -569,8 +686,7 @@ InstallerCalculator *PackageManagerCorePrivate::installerCalculator() const
{
if (!m_installerCalculator) {
PackageManagerCorePrivate *const pmcp = const_cast<PackageManagerCorePrivate *> (this);
- pmcp->m_installerCalculator = new InstallerCalculator(m_core,
- m_core->components(PackageManagerCore::ComponentType::AllNoReplacements), pmcp->m_autoDependencyComponentHash);
+ pmcp->m_installerCalculator = new InstallerCalculator(m_core, pmcp->m_autoDependencyComponentHash);
}
return m_installerCalculator;
}
@@ -594,7 +710,7 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const
}
}
- pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents, m_core,
+ pmcp->m_uninstallerCalculator = new UninstallerCalculator(m_core,
pmcp->m_autoDependencyComponentHash, pmcp->m_localDependencyComponentHash, pmcp->m_localVirtualComponents);
}
return m_uninstallerCalculator;
@@ -604,7 +720,6 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
{
m_coreCheckedHash.clear();
m_data = PackageManagerCoreData(params, isInstaller());
- m_componentsToInstallCalculated = false;
#ifdef Q_OS_LINUX
if (m_launchedAsRoot && isInstaller())
@@ -664,9 +779,14 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
if (isInstaller())
m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1));
+ const QString aliasFilePath = m_core->settings().aliasDefinitionsFile();
+ if (!aliasFilePath.isEmpty())
+ m_aliasSources.insert(AliasSource(AliasSource::SourceFileFormat::Xml, aliasFilePath, -1));
+
m_metadataJob.disconnect();
m_metadataJob.setAutoDelete(false);
m_metadataJob.setPackageManagerCore(m_core);
+
connect(&m_metadataJob, &Job::infoMessage, this, &PackageManagerCorePrivate::infoMessage);
connect(&m_metadataJob, &Job::progress, this, &PackageManagerCorePrivate::infoProgress);
connect(&m_metadataJob, &Job::totalProgress, this, &PackageManagerCorePrivate::totalProgress);
@@ -769,7 +889,12 @@ Operation *PackageManagerCorePrivate::takeOwnedOperation(Operation *operation)
QString PackageManagerCorePrivate::maintenanceToolName() const
{
- QString filename = m_data.settings().maintenanceToolName();
+ QString filename;
+ if (isInstaller())
+ filename = m_data.settings().maintenanceToolName();
+ else
+ filename = QCoreApplication::applicationName();
+
#if defined(Q_OS_MACOS)
if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
filename += QLatin1String(".app/Contents/MacOS/") + filename;
@@ -782,12 +907,16 @@ QString PackageManagerCorePrivate::maintenanceToolName() const
QString PackageManagerCorePrivate::maintenanceToolAliasPath() const
{
#ifdef Q_OS_MACOS
- const bool isRoot = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive());
+ const QString aliasName = m_data.settings().maintenanceToolAlias();
+ if (aliasName.isEmpty())
+ return QString();
+
+ const bool isRoot = m_core->hasAdminRights();
const QString applicationsDir = m_core->value(
isRoot ? QLatin1String("ApplicationsDir") : QLatin1String("ApplicationsDirUser")
);
QString maintenanceToolAlias = QString::fromLatin1("%1/%2")
- .arg(applicationsDir, m_data.settings().maintenanceToolAlias());
+ .arg(applicationsDir, aliasName);
if (!maintenanceToolAlias.endsWith(QLatin1String(".app")))
maintenanceToolAlias += QLatin1String(".app");
@@ -808,6 +937,13 @@ QString PackageManagerCorePrivate::offlineBinaryName() const
return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename);
}
+QString PackageManagerCorePrivate::datFileName()
+{
+ if (m_datFileName.isEmpty())
+ m_datFileName = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName() + QLatin1String(".dat");
+ return m_datFileName;
+}
+
static QNetworkProxy readProxy(QXmlStreamReader &reader)
{
QNetworkProxy proxy(QNetworkProxy::HttpProxy);
@@ -861,12 +997,12 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
QVariantHash variables; // Do not change to QVariantMap! Breaks existing .ini files,
// cause the variant types do not match while restoring the variables from the file.
- QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat);
+ QSettingsWrapper cfg(iniPath, QSettings::IniFormat);
foreach (const QString &key, m_data.keys()) {
if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments)
continue;
QVariant value = m_data.value(key);
- if (value.canConvert(QVariant::String))
+ if (value.canConvert<QString>())
value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable));
variables.insert(key, value);
}
@@ -890,8 +1026,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml"));
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- QXmlStreamWriter writer(&file);
- writer.setCodec("UTF-8");
+ QString outputStr;
+ QXmlStreamWriter writer(&outputStr);
writer.setAutoFormatting(true);
writer.writeStartDocument();
@@ -922,7 +1058,10 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
writer.writeEndElement();
}
writer.writeEndElement();
+ writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath());
writer.writeEndElement();
+
+ file.write(outputStr.toUtf8());
}
setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable);
}
@@ -930,7 +1069,7 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir)
{
QSettingsWrapper cfg(targetDir + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(),
- QSettingsWrapper::IniFormat);
+ QSettings::IniFormat);
const QVariantHash v = cfg.value(QLatin1String("Variables")).toHash(); // Do not change to
// QVariantMap! Breaks reading from existing .ini files, cause the variant types do not match.
for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); ++it) {
@@ -962,7 +1101,7 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
case QXmlStreamReader::StartElement: {
if (reader.name() == QLatin1String("Network")) {
while (reader.readNextStartElement()) {
- const QStringRef name = reader.name();
+ const QStringView name = reader.name();
if (name == QLatin1String("Ftp")) {
m_data.settings().setFtpProxy(readProxy(reader));
} else if (name == QLatin1String("Http")) {
@@ -971,6 +1110,8 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
m_data.settings().addUserRepositories(readRepositories(reader, false));
} else if (name == QLatin1String("ProxyType")) {
m_data.settings().setProxyType(Settings::ProxyType(reader.readElementText().toInt()));
+ } else if (name == QLatin1String("LocalCachePath")) {
+ m_data.settings().setLocalCachePath(reader.readElementText());
} else {
reader.skipCurrentElement();
}
@@ -1069,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++;
}
}
}
@@ -1149,12 +1293,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
// other code a lot (since installers don't have any appended data either)
QFile dataOut(generateTemporaryFileName());
QInstaller::openForWrite(&dataOut);
- QInstaller::appendInt64(&dataOut, 0); // operations start
- QInstaller::appendInt64(&dataOut, 0); // operations end
- QInstaller::appendInt64(&dataOut, 0); // resource count
- QInstaller::appendInt64(&dataOut, 4 * sizeof(qint64)); // data block size
- QInstaller::appendInt64(&dataOut, BinaryContent::MagicUninstallerMarker);
- QInstaller::appendInt64(&dataOut, BinaryContent::MagicCookie);
+ QInstallerTools::createMTDatFile(dataOut);
{
QFile dummy(resourcePath.filePath(QLatin1String("installer.dat")));
@@ -1259,19 +1398,9 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *outp
QInstaller::appendInt64(output, BinaryContent::MagicUninstallerMarker);
}
-void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations)
+void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &performedOperations)
{
- if (m_disableWriteMaintenanceTool) {
- qCDebug(QInstaller::lcInstallerInstallLog()) << "Maintenance tool writing disabled.";
- return;
- }
-
- bool gainedAdminRights = false;
- if (!directoryWritable(targetDir())) {
- m_core->gainAdminRights();
- gainedAdminRights = true;
- }
-
+#ifdef Q_OS_MACOS
const QString targetAppDirPath = QFileInfo(maintenanceToolName()).path();
if (!QDir().exists(targetAppDirPath)) {
// create the directory containing the maintenance tool (like a bundle structure on macOS...)
@@ -1281,8 +1410,6 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
performOperationThreaded(op);
performedOperations.append(takeOwnedOperation(op));
}
-
-#ifdef Q_OS_MACOS
// if it is a bundle, we need some stuff in it...
const QString sourceAppDirPath = QCoreApplication::applicationDirPath();
if (isInstaller() && QInstaller::isInBundle(sourceAppDirPath)) {
@@ -1312,7 +1439,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
const QString after = QLatin1String("<string>") + QFileInfo(maintenanceToolName()).baseName()
+ QLatin1String("</string>");
while (!in.atEnd())
- out << in.readLine().replace(before, after) << endl;
+ out << in.readLine().replace(before, after) << Qt::endl;
// copy qt_menu.nib if it exists
op = createOwnedOperation(QLatin1String("Mkdir"));
@@ -1352,7 +1479,23 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
<< (targetAppDirPath + QLatin1String("/../plugins")));
performOperationThreaded(op);
}
+#else
+ Q_UNUSED(performedOperations);
#endif
+}
+
+void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations)
+{
+ if (m_disableWriteMaintenanceTool) {
+ qCDebug(QInstaller::lcInstallerInstallLog()) << "Maintenance tool writing disabled.";
+ return;
+ }
+
+ bool gainedAdminRights = false;
+ if (!directoryWritable(targetDir())) {
+ m_core->gainAdminRights();
+ gainedAdminRights = true;
+ }
try {
// 1 - check if we have a installer base replacement
@@ -1388,45 +1531,71 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
// 5.1 - this will only happen -if- we wrote out a new binary
bool newBinaryWritten = false;
- bool replacementExists = false;
+ QString mtName = maintenanceToolName();
const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced);
- if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) {
+ if (!installerBaseBinary.isEmpty() && QFileInfo::exists(installerBaseBinary)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< installerBaseBinary;
-
- QFile replacementBinary(installerBaseBinary);
- try {
- QInstaller::openForRead(&replacementBinary);
- writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true);
- qCDebug(QInstaller::lcInstallerInstallLog) << "Wrote the binary with the new replacement.";
-
- newBinaryWritten = true;
- replacementExists = true;
- } catch (const Error &error) {
- qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
- }
-
- if (!replacementBinary.remove()) {
- // Is there anything more sensible we can do with this error? I think not. It's not serious
- // enough for throwing / aborting the process.
- qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
- << installerBaseBinary << "after updating the maintenance tool:"
- << replacementBinary.errorString();
+ if (QInstaller::isInBundle(installerBaseBinary)) {
+ // In macOS the installerbase is a whole app bundle. We do not modify the maintenancetool name in app bundle
+ // so that possible signing and notarization will remain. Therefore, the actual maintenance tool name might
+ // differ from the one defined in the settings.
+ try {
+ const QString maintenanceToolRenamedName = installerBaseBinary + QLatin1String(".new");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Writing maintenance tool " << maintenanceToolRenamedName;
+ QInstaller::copyDirectoryContents(installerBaseBinary, maintenanceToolRenamedName);
+
+ newBinaryWritten = true;
+ mtName = installerBaseBinary;
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ }
+ try {
+ QInstaller::removeDirectory(installerBaseBinary);
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool.";
+ } catch (const Error &error) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool:"
+ << error.message();
+ }
} else {
- qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
- << installerBaseBinary << "after updating the maintenance tool.";
+ writeMaintenanceToolAppBundle(performedOperations);
+ QFile replacementBinary(installerBaseBinary);
+ try {
+ QInstaller::openForRead(&replacementBinary);
+ writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true);
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Wrote the binary with the new replacement.";
+
+ newBinaryWritten = true;
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ }
+
+ if (!replacementBinary.remove()) {
+ // Is there anything more sensible we can do with this error? I think not. It's not serious
+ // enough for throwing / aborting the process.
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool:"
+ << replacementBinary.errorString();
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool.";
+ }
}
m_installerBaseBinaryUnreplaced.clear();
- } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) {
+ } else if (!installerBaseBinary.isEmpty() && !QFileInfo::exists(installerBaseBinary)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "The current maintenance tool could not be updated."
<< installerBaseBinary << "does not exist. Please fix the \"setInstallerBaseBinary"
"(<temp_installer_base_binary_path>)\" call in your script.";
+ writeMaintenanceToolAppBundle(performedOperations);
+ } else {
+ writeMaintenanceToolAppBundle(performedOperations);
}
QFile input;
BinaryLayout layout;
- const QString dataFile = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName()
- + QLatin1String(".dat");
+ const QString dataFile = datFileName();
try {
if (isInstaller()) {
if (QFile::exists(dataFile)) {
@@ -1511,23 +1680,32 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
if (newBinaryWritten) {
// Remove old alias as the name may have changed.
deleteMaintenanceToolAlias();
- // The new alias file is created after the maintenance too binary is renamed,
- // but we need to set the value before the variables get written to disk.
- m_core->setValue(QLatin1String("CreatedMaintenanceToolAlias"), maintenanceToolAliasPath());
+ const QString aliasPath = maintenanceToolAliasPath();
+ if (!aliasPath.isEmpty()) {
+ // The new alias file is created after the maintenance too binary is renamed,
+ // but we need to set the value before the variables get written to disk.
+ m_core->setValue(QLatin1String("CreatedMaintenanceToolAlias"), aliasPath);
+ }
}
#endif
writeMaintenanceConfigFiles();
QFile::remove(dataFile);
- QFile::rename(dataFile + QLatin1String(".new"), dataFile);
+ QFileInfo fi(mtName);
+ //Rename the dat file according to maintenancetool name
+ QFile::rename(dataFile + QLatin1String(".new"), targetDir() + QLatin1Char('/') + fi.baseName() + QLatin1String(".dat"));
const bool restart = !statusCanceledOrFailed() && m_needsHardRestart;
qCDebug(QInstaller::lcInstallerInstallLog) << "Maintenance tool hard restart:"
<< (restart ? "true." : "false.");
if (newBinaryWritten) {
- deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart);
- writeMaintenanceToolAlias();
+ if (isInstaller())
+ QFile::rename(mtName + QLatin1String(".new"), mtName);
+ else
+ deferredRename(mtName + QLatin1String(".new"), mtName, restart);
+ QFileInfo mtFileName(mtName);
+ writeMaintenanceToolAlias(mtFileName.fileName());
} else if (restart) {
SelfRestarter::setRestartOnQuit(true);
}
@@ -1599,21 +1777,25 @@ void PackageManagerCorePrivate::writeOfflineBaseBinary()
}
}
-void PackageManagerCorePrivate::writeMaintenanceToolAlias()
+void PackageManagerCorePrivate::writeMaintenanceToolAlias(const QString &maintenanceToolName)
{
#ifdef Q_OS_MACOS
+ const QString aliasPath = maintenanceToolAliasPath();
+ if (aliasPath.isEmpty())
+ return;
+
QString maintenanceToolBundle = QString::fromLatin1("%1/%2")
- .arg(targetDir(), m_data.settings().maintenanceToolName());
+ .arg(targetDir(), maintenanceToolName);
if (!maintenanceToolBundle.endsWith(QLatin1String(".app")))
maintenanceToolBundle += QLatin1String(".app");
- const QString aliasPath = maintenanceToolAliasPath();
const QDir targetDir(QFileInfo(aliasPath).absolutePath());
-
if (!targetDir.exists())
targetDir.mkpath(targetDir.absolutePath());
mkalias(maintenanceToolBundle, aliasPath);
+#else
+ Q_UNUSED(maintenanceToolName)
#endif
}
@@ -1701,7 +1883,7 @@ bool PackageManagerCorePrivate::runInstaller()
throw Error(tr("It is not possible to install from network location"));
}
- if (!adminRightsGained) {
+ if (!m_core->hasAdminRights()) {
foreach (Component *component, componentsToInstall) {
if (component->value(scRequiresAdminRights, scFalse) == scFalse)
continue;
@@ -1735,7 +1917,7 @@ bool PackageManagerCorePrivate::runInstaller()
double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
// Now install the requested components
- unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained);
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) {
emit m_core->titleMessageChanged(tr("Creating local repository"));
@@ -1794,7 +1976,8 @@ bool PackageManagerCorePrivate::runInstaller()
if (progress < 100)
ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation finished!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installation finished!"));
if (adminRightsGained)
m_core->dropAdminRights();
@@ -1812,7 +1995,9 @@ bool PackageManagerCorePrivate::runInstaller()
m_core->rollBackInstallation();
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation aborted!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installation aborted!"));
+
if (adminRightsGained)
m_core->dropAdminRights();
emit installationFinished();
@@ -1890,7 +2075,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// There is a replacement, but the replacement is not scheduled for update, keep it as well.
if (m_componentsToReplaceUpdaterMode.contains(name)
- && !m_componentsToReplaceUpdaterMode.value(name).first->updateRequested()) {
+ && !m_installerCalculator->resolvedComponents().contains(m_componentsToReplaceUpdaterMode.value(name).first)) {
nonRevertedOperations.append(operation);
continue;
}
@@ -1930,7 +2115,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
}
// we did not request admin rights till we found out that a component/ undo needs admin rights
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -1951,7 +2136,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
if (undoOperations.count() > 0) {
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components..."));
- runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true);
+ runUndoOperations(undoOperations, undoOperationProgressSize, true);
}
m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted
@@ -1959,7 +2144,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
// Now install the requested new components
- unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained);
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
emit m_core->titleMessageChanged(tr("Creating Maintenance Tool"));
@@ -1971,7 +2156,8 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// usually this should be only the reserved one from the beginning
if (progress < 100)
ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate finished!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Update finished!"));
if (adminRightsGained)
m_core->dropAdminRights();
@@ -1991,7 +2177,9 @@ bool PackageManagerCorePrivate::runPackageUpdater()
m_core->rollBackInstallation();
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate aborted!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Update aborted!"));
+
if (adminRightsGained)
m_core->dropAdminRights();
emit installationFinished();
@@ -2025,7 +2213,7 @@ bool PackageManagerCorePrivate::runUninstaller()
}
// We did not yet request elevated permissions but they are required.
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -2033,7 +2221,7 @@ bool PackageManagerCorePrivate::runUninstaller()
const int uninstallOperationCount = countProgressOperations(undoOperations);
const double undoOperationProgressSize = double(1) / double(uninstallOperationCount);
- runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false);
+ runUndoOperations(undoOperations, undoOperationProgressSize, false);
// No operation delete here, as all old undo operations are deleted in the destructor.
deleteMaintenanceTool(); // this will also delete the TargetDir on Windows
@@ -2042,7 +2230,7 @@ bool PackageManagerCorePrivate::runUninstaller()
// If not on Windows, we need to remove TargetDir manually.
#ifndef Q_OS_WIN
if (QVariant(m_core->value(scRemoveTargetDir)).toBool() && !targetDir().isEmpty()) {
- if (updateAdminRights && !adminRightsGained)
+ if (updateAdminRights && !m_core->hasAdminRights())
adminRightsGained = m_core->gainAdminRights();
removeDirectoryThreaded(targetDir(), true);
qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen.";
@@ -2118,7 +2306,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
m_core->downloadNeededArchives(double(1));
const QString installerBaseReplacement = replaceVariables(m_offlineBaseBinaryUnreplaced);
- if (!installerBaseReplacement.isEmpty() && QFileInfo(installerBaseReplacement).exists()) {
+ if (!installerBaseReplacement.isEmpty() && QFileInfo::exists(installerBaseReplacement)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< offlineBinaryTempName;
@@ -2204,12 +2392,13 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
}
void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components,
- double progressOperationSize, bool adminRightsGained)
+ double progressOperationSize)
{
OperationList unpackOperations;
bool becameAdmin = false;
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nPreparing to unpack components..."));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Preparing to unpack components..."));
// 1. Collect operations
quint64 totalOperationsSizeHint = 0;
@@ -2227,7 +2416,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
// There's currently no way to control this on a per-operation basis, so
// any op requesting execution as admin means all extracts are done as admin.
- if (!adminRightsGained && !becameAdmin && op->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && op->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
}
}
@@ -2275,7 +2464,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
continue;
}
// Backup may request performing operation as admin
- if (!adminRightsGained && !becameAdmin && operation->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
}
@@ -2290,11 +2479,12 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
return lhs->sizeHint() > rhs->sizeHint();
});
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUnpacking components..."));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Unpacking components..."));
runner.setType(Operation::Perform);
const QHash<Operation *, bool> results = runner.run();
- const OperationList performedOperations = backupResults.keys();
+ const OperationList performedOperations = results.keys();
QString error;
for (auto &operation : performedOperations) {
@@ -2343,8 +2533,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
-void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained)
+void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize)
{
OperationList operations = component->operations(Operation::Install);
if (!component->operationsCreatedSuccessfully())
@@ -2354,8 +2543,8 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// show only components which do something, MinimumProgress is only for progress calculation safeness
bool showDetailsLog = false;
if (opCount > 1 || (opCount == 1 && operations.at(0)->name() != QLatin1String("MinimumProgress"))) {
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstalling component %1")
- .arg(component->displayName()));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installing component %1").arg(component->displayName()));
showDetailsLog = true;
}
@@ -2365,7 +2554,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// maybe this operations wants us to be admin...
bool becameAdmin = false;
- if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) {
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) {
becameAdmin = m_core->gainAdminRights();
qCDebug(QInstaller::lcInstallerInstallLog) << operation->name() << "as admin:" << becameAdmin;
}
@@ -2454,23 +2643,54 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
-bool PackageManagerCorePrivate::runningProcessesFound()
+PackageManagerCore::Status PackageManagerCorePrivate::fetchComponentsAndInstall(const QStringList& components)
{
- //Check if there are processes running in the install
- QStringList excludeFiles = m_allowedRunningProcesses;
- excludeFiles.append(maintenanceToolName());
+ // init default model before fetching remote packages tree
+ ComponentModel *model = m_core->defaultComponentModel();
+ Q_UNUSED(model);
- const QString performModeWarning = m_completeUninstall
- ? QLatin1String("Unable to remove components.")
- : QLatin1String("Unable to update components.");
+ bool fallbackReposFetched = false;
+ auto fetchComponents = [&]() {
+ bool packagesFound = m_core->fetchPackagesWithFallbackRepositories(components, fallbackReposFetched);
- QStringList runningProcesses = runningInstallerProcesses(excludeFiles);
- if (!runningProcesses.isEmpty()) {
- qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << performModeWarning
- << " Please stop these processes: " << runningProcesses << " and try again.";
+ 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 false;
+
+ return m_core->status();
}
void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state)
@@ -2491,6 +2711,14 @@ void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::Che
void PackageManagerCorePrivate::deleteMaintenanceTool()
{
+ QDir resourcePath(QFileInfo(maintenanceToolName()).dir());
+ resourcePath.remove(QLatin1String("installer.dat"));
+ QDir installDir(targetDir());
+ installDir.remove(m_data.settings().maintenanceToolName() + QLatin1String(".dat"));
+ installDir.remove(QLatin1String("network.xml"));
+ installDir.remove(m_data.settings().maintenanceToolIniFile());
+ QInstaller::VerboseWriter::instance()->setFileName(QString());
+ installDir.remove(m_core->value(QLatin1String("LogFileName"), QLatin1String("InstallationLog.txt")));
#ifdef Q_OS_WIN
// Since Windows does not support that the maintenance tool deletes itself we have to go with a rather dirty
// hack. What we do is to create a batchfile that will try to remove the maintenance tool once per second. Then
@@ -2503,6 +2731,7 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
throw Error(tr("Cannot prepare removal"));
+ const bool removeTargetDir = QVariant(m_core->value(scRemoveTargetDir)).toBool();
QTextStream batch(&f);
batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
batch << "file = WScript.Arguments.Item(0)\n";
@@ -2514,10 +2743,12 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
batch << " fso.DeleteFile(file)\n";
batch << " WScript.Sleep(1000)\n";
batch << "wend\n";
-// batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
+ if (!removeTargetDir)
+ batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
batch << " Set folder = Nothing\n";
batch << " fso.DeleteFolder folderpath, true\n";
-// batch << "end if\n";
+ if (!removeTargetDir)
+ batch << "end if\n";
batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
f.close();
@@ -2525,11 +2756,7 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
QStringList arguments;
arguments << QLatin1String("//Nologo") << batchfile; // execute the batchfile
arguments << QDir::toNativeSeparators(QFileInfo(installerBinaryPath()).absoluteFilePath());
- if (!m_performedOperationsOld.isEmpty()) {
- const Operation *const op = m_performedOperationsOld.first();
- if (op->name() == QLatin1String("Mkdir")) // the target directory name
- arguments << QDir::toNativeSeparators(QFileInfo(op->arguments().first()).absoluteFilePath());
- }
+ arguments << targetDir();
if (!QProcessWrapper::startDetached(QLatin1String("cscript"), arguments, QDir::rootPath()))
throw Error(tr("Cannot start removal"));
@@ -2569,7 +2796,11 @@ void PackageManagerCorePrivate::deleteMaintenanceToolAlias()
void PackageManagerCorePrivate::registerMaintenanceTool()
{
#ifdef Q_OS_WIN
- QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ auto quoted = [](const QString &s) {
+ return QString::fromLatin1("\"%1\"").arg(s);
+ };
+
+ QSettingsWrapper settings(registerPath(), QSettings::NativeFormat);
settings.setValue(scDisplayName, m_data.value(QLatin1String("ProductName")));
settings.setValue(QLatin1String("DisplayVersion"), m_data.value(QLatin1String("ProductVersion")));
const QString maintenanceTool = QDir::toNativeSeparators(maintenanceToolName());
@@ -2579,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"), maintenanceTool);
- settings.setValue(QLatin1String("ModifyPath"), QString(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
@@ -2613,13 +2847,13 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
void PackageManagerCorePrivate::unregisterMaintenanceTool()
{
#ifdef Q_OS_WIN
- QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ QSettingsWrapper settings(registerPath(), QSettings::NativeFormat);
settings.remove(QString());
#endif
}
-void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize,
- bool adminRightsGained, bool deleteOperation)
+void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations,
+ double progressSize, bool deleteOperation)
{
try {
const int operationsCount = undoOperations.size();
@@ -2630,7 +2864,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
throw Error(tr("Installation canceled by user"));
bool becameAdmin = false;
- if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && undoOperation->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
connectOperationToInstaller(undoOperation, progressSize);
@@ -2738,6 +2972,25 @@ LocalPackagesMap PackageManagerCorePrivate::localInstalledPackages()
return m_localPackageHub->localPackages();
}
+QList<ComponentAlias *> PackageManagerCorePrivate::componentAliases()
+{
+ if (m_aliases && m_aliasFinder)
+ return m_aliasFinder->aliases();
+
+ m_aliases = false;
+ delete m_aliasFinder;
+
+ m_aliasFinder = new AliasFinder(m_core);
+ m_aliasFinder->setAliasSources(m_aliasSources);
+ if (!m_aliasFinder->run()) {
+ qCDebug(lcDeveloperBuild) << "No component aliases found." << Qt::endl;
+ return QList<ComponentAlias *>();
+ }
+
+ m_aliases = true;
+ return m_aliasFinder->aliases();
+}
+
bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadType type)
{
m_updates = false;
@@ -2768,12 +3021,12 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadTyp
return m_repoFetched;
}
-bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository)
+bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compressedRepository)
{
if (!compressedRepository && m_updateSourcesAdded)
return m_updateSourcesAdded;
- const QList<Metadata> metadata = m_metadataJob.metadata();
+ const QList<Metadata *> metadata = m_metadataJob.metadata();
if (metadata.isEmpty()) {
m_updateSourcesAdded = true;
return m_updateSourcesAdded;
@@ -2789,50 +3042,22 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe
m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1));
}
- foreach (const Metadata &data, metadata) {
- if (compressedRepository && !data.repository.isCompressed()) {
+ foreach (const Metadata *data, metadata) {
+ if (compressedRepository && !data->repository().isCompressed()) {
continue;
}
if (statusCanceledOrFailed())
return false;
- if (data.directory.isEmpty())
+ if (data->path().isEmpty())
continue;
- if (parseChecksum) {
- const QString updatesXmlPath = data.directory + QLatin1String("/Updates.xml");
- QFile updatesFile(updatesXmlPath);
- try {
- QInstaller::openForRead(&updatesFile);
- } catch(const Error &e) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Error opening Updates.xml:"
- << e.message();
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- int line = 0;
- int column = 0;
- QString error;
- QDomDocument doc;
- if (!doc.setContent(&updatesFile, &error, &line, &column)) {
- qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Parse error in file "
- << updatesFile.fileName() << ": " << error << " at line " << line
- << " col " << column;
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum"));
- if (!checksum.isNull())
- m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue);
- }
- if (data.repository.isCompressed())
- m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 2));
+ if (data->repository().isCompressed())
+ m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2, data->repository().postLoadComponentScript()));
else
- m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 0));
+ m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0, data->repository().postLoadComponentScript()));
- ProductKeyCheck::instance()->addPackagesFromXml(data.directory + QLatin1String("/Updates.xml"));
+ ProductKeyCheck::instance()->addPackagesFromXml(data->path() + QLatin1String("/Updates.xml"));
}
if ((compressedRepository && m_compressedPackageSources.count() == 0 ) ||
(!compressedRepository && m_packageSources.count() == 0)) {
@@ -2858,7 +3083,6 @@ void PackageManagerCorePrivate::restoreCheckState()
}
m_coreCheckedHash.clear();
- m_componentsToInstallCalculated = false;
}
void PackageManagerCorePrivate::storeCheckState()
@@ -2871,19 +3095,76 @@ void PackageManagerCorePrivate::storeCheckState()
m_coreCheckedHash.insert(component, component->checkState());
}
-void PackageManagerCorePrivate::updateComponentCheckedState()
+void PackageManagerCorePrivate::updateComponentInstallActions()
{
for (Component *component : m_core->components(PackageManagerCore::ComponentType::All)) {
component->setInstallAction(component->isInstalled()
? ComponentModelHelper::KeepInstalled
: ComponentModelHelper::KeepUninstalled);
}
- for (Component *component : uninstallerCalculator()->componentsToUninstall())
+ for (Component *component : uninstallerCalculator()->resolvedComponents())
component->setInstallAction(ComponentModelHelper::Uninstall);
- for (Component *component : installerCalculator()->orderedComponentsToInstall())
+ for (Component *component : installerCalculator()->resolvedComponents())
component->setInstallAction(ComponentModelHelper::Install);
}
+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 select " << 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);
@@ -2934,17 +3215,26 @@ void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &inv
QMetaObject::invokeMethod(obj, qPrintable(invokableMethodName));
}
+/*
+ Adds the \a path for deletetion. Unlike files for delayed deletion, which are deleted
+ on the start of next installer run, these paths are deleted on exit.
+*/
+void PackageManagerCorePrivate::addPathForDeletion(const QString &path)
+{
+ m_tmpPathDeleter.add(path);
+}
+
void PackageManagerCorePrivate::unpackAndInstallComponents(const QList<Component *> &components,
- const double progressOperationSize, const bool adminRightsGained)
+ const double progressOperationSize)
{
// Perform extract operations
- unpackComponents(components, progressOperationSize, adminRightsGained);
+ unpackComponents(components, progressOperationSize);
// Perform rest of the operations and mark component as installed
const int componentsToInstallCount = components.size();
int installedComponents = 0;
foreach (Component *component, components) {
- installComponent(component, progressOperationSize, adminRightsGained);
+ installComponent(component, progressOperationSize);
++installedComponents;
ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 components installed.")
@@ -2970,36 +3260,24 @@ void PackageManagerCorePrivate::processFilesForDelayedDeletion()
}
}
-void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result)
-{
- QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories );
-
- while (it.hasNext())
- result->append(QDir::toNativeSeparators(it.next().toLower()));
-
- foreach (const QString &process, excludeFiles)
- result->removeAll(QDir::toNativeSeparators(process.toLower()));
-}
-
-QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles)
-{
- QStringList resultFiles;
- findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles);
- return checkRunningProcessesFromList(resultFiles);
-}
-
bool PackageManagerCorePrivate::calculateComponentsAndRun()
{
- QString htmlOutput;
- bool componentsOk = m_core->calculateComponents(&htmlOutput);
+ bool componentsOk = m_core->recalculateAllComponents();
+
if (statusCanceledOrFailed()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled.";
} else if (componentsOk && acceptLicenseAgreements()) {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput);
+ try {
+ loadComponentScripts(installerCalculator()->resolvedComponents(), true);
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ return false;
+ }
+ 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.";
@@ -3012,31 +3290,6 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun()
return false;
}
-void PackageManagerCorePrivate::calculateUninstallComponents()
-{
- clearUninstallerCalculator();
- const QList<Component *> componentsToInstallList = installerCalculator()->orderedComponentsToInstall();
- QSet<Component*> componentsToInstall(componentsToInstallList.begin(), componentsToInstallList.end());
-
- QList<Component *> selectedComponentsToUninstall;
- foreach (Component* component, m_core->components(PackageManagerCore::ComponentType::Replacements)) {
- // Uninstall the component if replacement is selected for install or update
- QPair<Component*, Component*> comp = componentsToReplace().value(component->name());
- if (comp.first) {
- if (comp.first->isSelectedForInstallation() || comp.first->updateRequested()) {
- uninstallerCalculator()->insertUninstallReason(component,
- UninstallerCalculator::Replaced, comp.first->name());
- selectedComponentsToUninstall.append(comp.second);
- }
- }
- }
- foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::AllNoReplacements)) {
- if (component->uninstallationRequested() && !componentsToInstallList.contains(component))
- selectedComponentsToUninstall.append(component);
- }
- uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall);
-}
-
bool PackageManagerCorePrivate::acceptLicenseAgreements() const
{
// Always skip for uninstaller
@@ -3051,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();
@@ -3099,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 24782b598..c0c55c4cc 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.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.
@@ -36,6 +36,7 @@
#include "packagesource.h"
#include "qinstallerglobal.h"
#include "component.h"
+#include "fileutils.h"
#include "sysinfo.h"
#include "updatefinder.h"
@@ -53,12 +54,15 @@ using namespace KDUpdater;
namespace QInstaller {
struct BinaryLayout;
+struct AliasSource;
+class AliasFinder;
class ScriptEngine;
class ComponentModel;
-class TempDirDeleter;
+class ComponentAlias;
class InstallerCalculator;
class UninstallerCalculator;
class RemoteFileEngineHandler;
+class ComponentSortFilterProxyModel;
class PackageManagerCorePrivate : public QObject
{
@@ -69,7 +73,7 @@ class PackageManagerCorePrivate : public QObject
public:
explicit PackageManagerCorePrivate(PackageManagerCore *core);
explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
- const QList<OperationBlob> &performedOperations);
+ const QList<OperationBlob> &performedOperations, const QString &datFileName);
~PackageManagerCorePrivate();
static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes);
@@ -92,6 +96,7 @@ public:
QString maintenanceToolAliasPath() const;
QString installerBinaryPath() const;
QString offlineBinaryName() const;
+ QString datFileName();
void writeMaintenanceConfigFiles();
void readMaintenanceConfigFiles(const QString &targetDir);
@@ -99,12 +104,16 @@ public:
void writeMaintenanceTool(OperationList performedOperations);
void writeOfflineBaseBinary();
- void writeMaintenanceToolAlias();
+ void writeMaintenanceToolAlias(const QString &maintenanceToolName);
QString componentsXmlPath() const;
QString configurationFileName() const;
bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript);
+ bool buildComponentAliases();
+
+ template <typename T>
+ bool loadComponentScripts(const T &components, const bool postScript = false);
void cleanUpComponentEnvironment();
ScriptEngine *componentScriptEngine() const;
@@ -115,7 +124,6 @@ public:
QList<Component*> &replacementDependencyComponents();
QHash<QString, QPair<Component*, Component*> > &componentsToReplace();
QHash<QString, QStringList > &componentReplaces();
- QList<Component*> replacedComponentsByName(const QString &name);
void clearInstallerCalculator();
InstallerCalculator *installerCalculator() const;
@@ -166,13 +174,11 @@ public:
m_performedOperationsCurrentSession.clear();
}
- void unpackComponents(const QList<Component *> &components, double progressOperationSize,
- bool adminRightsGained = false);
+ void unpackComponents(const QList<Component *> &components, double progressOperationSize);
- void installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained = false);
+ void installComponent(Component *component, double progressOperationSize);
+ PackageManagerCore::Status fetchComponentsAndInstall(const QStringList& components);
- bool runningProcessesFound();
void setComponentSelection(const QString &id, Qt::CheckState state);
signals:
@@ -185,8 +191,10 @@ signals:
public:
UpdateFinder *m_updateFinder;
+ AliasFinder *m_aliasFinder;
QSet<PackageSource> m_packageSources;
QSet<PackageSource> m_compressedPackageSources;
+ QSet<AliasSource> m_aliasSources;
std::shared_ptr<LocalPackageHub> m_localPackageHub;
QStringList m_filesForDelayedDeletion;
@@ -214,6 +222,8 @@ public:
QList<QInstaller::Component*> m_updaterComponentsDeps;
QList<QInstaller::Component*> m_updaterDependencyReplacements;
+ QHash<QString, QInstaller::ComponentAlias *> m_componentAliases;
+
OperationList m_ownedOperations;
OperationList m_performedOperationsOld;
OperationList m_performedOperationsCurrentSession;
@@ -237,10 +247,11 @@ private slots:
}
void handleMethodInvocationRequest(const QString &invokableMethodName);
+ void addPathForDeletion(const QString &path);
private:
void unpackAndInstallComponents(const QList<Component *> &components,
- const double progressOperationSize, const bool adminRightsGained);
+ const double progressOperationSize);
void deleteMaintenanceTool();
void deleteMaintenanceToolAlias();
@@ -250,27 +261,33 @@ private:
void writeMaintenanceToolBinary(QFile *const input, qint64 size, bool writeBinaryLayout);
void writeMaintenanceToolBinaryData(QFileDevice *output, QFile *const input,
const OperationList &performed, const BinaryLayout &layout);
+ void writeMaintenanceToolAppBundle(OperationList &performedOperations);
void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize,
- bool adminRightsGained, bool deleteOperation);
+ bool deleteOperation);
PackagesList remotePackages();
LocalPackagesMap localInstalledPackages();
+ QList<ComponentAlias *> componentAliases();
+
bool fetchMetaInformationFromRepositories(DownloadType type = DownloadType::All);
- bool addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository = false);
+ bool addUpdateResourcesFromRepositories(bool compressedRepository = false);
void processFilesForDelayedDeletion();
- void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result);
- QStringList runningInstallerProcesses(const QStringList &exludeFiles);
bool calculateComponentsAndRun();
- void calculateUninstallComponents();
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();
void createAutoDependencyHash(const QString &componentName, const QString &oldValue, const QString &newValue);
void createLocalDependencyHash(const QString &componentName, const QString &dependencies);
- void updateComponentCheckedState();
+ 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();
@@ -279,15 +296,16 @@ private:
private:
PackageManagerCore *m_core;
MetadataJob m_metadataJob;
+ TempPathDeleter m_tmpPathDeleter;
bool m_updates;
+ bool m_aliases;
bool m_repoFetched;
bool m_updateSourcesAdded;
qint64 m_magicBinaryMarker;
int m_magicMarkerSupplement;
- bool m_componentsToInstallCalculated;
- bool m_foundEssentialUpdate;;
+ bool m_foundEssentialUpdate;
mutable ScriptEngine *m_componentScriptEngine;
mutable ScriptEngine *m_controlScriptEngine;
@@ -304,6 +322,7 @@ private:
ComponentModel *m_defaultModel;
ComponentModel *m_updaterModel;
+ ComponentSortFilterProxyModel *m_componentSortFilterProxyModel;
QObject *m_guiObject;
QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler;
@@ -313,11 +332,16 @@ private:
QList<Component*> m_deletedReplacedComponents;
AutoDependencyHash m_autoDependencyComponentHash;
LocalDependencyHash m_localDependencyComponentHash;
+ QHash<QString, Component *> m_componentByNameHash;
QStringList m_localVirtualComponents;
// < name (component replacing others), components to replace>
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 567f97785..1113908bd 100644
--- a/src/libs/installer/packagemanagercoredata.cpp
+++ b/src/libs/installer/packagemanagercoredata.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,7 +34,9 @@
#include <QDesktopServices>
#include <QDir>
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QSettings>
+#include <QStandardPaths>
#ifdef Q_OS_WIN
# include <windows.h>
@@ -76,7 +78,7 @@ PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &va
// fill the variables defined in the settings
addNewVariable(QLatin1String("ProductName"), m_settings.applicationName());
- addNewVariable(QLatin1String("ProductVersion"), m_settings.version());
+ addNewVariable(QLatin1String("ProductVersion"), replaceVariables(m_settings.version()));
addNewVariable(scTitle, replaceVariables(m_settings.title()));
addNewVariable(scPublisher, m_settings.publisher());
addNewVariable(QLatin1String("Url"), m_settings.url());
@@ -94,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());
@@ -145,7 +147,7 @@ void PackageManagerCoreData::addDynamicPredefinedVariables()
QString dirX64 = dir;
#ifdef Q_OS_WIN
QSettingsWrapper current(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion")
- , QSettingsWrapper::NativeFormat);
+ , QSettings::NativeFormat);
BOOL onWow64Or64bit = TRUE;
#ifndef Q_OS_WIN64
IsWow64Process(GetCurrentProcess(), &onWow64Or64bit);
@@ -167,9 +169,9 @@ void PackageManagerCoreData::addDynamicPredefinedVariables()
#ifdef Q_OS_WIN
QSettingsWrapper user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Explorer\\User Shell Folders"), QSettingsWrapper::NativeFormat);
+ "CurrentVersion\\Explorer\\User Shell Folders"), QSettings::NativeFormat);
QSettingsWrapper system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Explorer\\Shell Folders"), QSettingsWrapper::NativeFormat);
+ "CurrentVersion\\Explorer\\Shell Folders"), QSettings::NativeFormat);
const QString programs = user.value(QLatin1String("Programs"), QString()).toString();
const QString allPrograms = system.value(QLatin1String("Common Programs"), QString())
@@ -232,7 +234,7 @@ bool PackageManagerCoreData::setValue(const QString &key, const QString &normali
return true;
}
-QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_default) const
+QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_default, const QSettings::Format &format) const
{
if (key == scTargetDir) {
QString dir = m_variables.value(key);
@@ -249,13 +251,15 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa
#ifdef Q_OS_WIN
if (!m_variables.contains(key)) {
- static const QRegExp regex(QLatin1String("\\\\|/"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/"));
const QString filename = key.section(regex, 0, -2);
const QString regKey = key.section(regex, -1);
- const QSettingsWrapper registry(filename, QSettingsWrapper::NativeFormat);
+ const QSettingsWrapper registry(filename, format);
if (!filename.isEmpty() && !regKey.isEmpty() && registry.contains(regKey))
return registry.value(regKey).toString();
}
+#else
+ Q_UNUSED(format)
#endif
if (m_variables.contains(key))
@@ -292,7 +296,7 @@ QString PackageManagerCoreData::replaceVariables(const QString &str) const
QByteArray PackageManagerCoreData::replaceVariables(const QByteArray &ba) const
{
- static const QChar at = QLatin1Char('@');
+ static const char at = '@';
QByteArray res;
int pos = 0;
while (true) {
diff --git a/src/libs/installer/packagemanagercoredata.h b/src/libs/installer/packagemanagercoredata.h
index 3cfb77c95..2dcddf102 100644
--- a/src/libs/installer/packagemanagercoredata.h
+++ b/src/libs/installer/packagemanagercoredata.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#define PACKAGEMANAGERCOREDATA_H
#include "settings.h"
+#include <QSettings>
namespace QInstaller {
@@ -53,7 +54,7 @@ public:
bool contains(const QString &key) const;
bool setValue(const QString &key, const QString &normalizedValue);
- QVariant value(const QString &key, const QVariant &_default = QVariant()) const;
+ QVariant value(const QString &key, const QVariant &_default = QVariant(), const QSettings::Format &format = QSettings::NativeFormat) const;
QString key(const QString &value) const;
QString replaceVariables(const QString &str) const;
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index 4bda0bd06..1f0462eea 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.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.
@@ -80,13 +80,15 @@
#include <QShowEvent>
#include <QFileDialog>
#include <QGroupBox>
-#include <QDesktopWidget>
+#include <QScreen>
#ifdef Q_OS_WIN
# include <qt_windows.h>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace KDUpdater;
using namespace QInstaller;
@@ -210,8 +212,9 @@ Q_DECLARE_METATYPE(DynamicInstallerPage*)
class PackageManagerGui::Private
{
public:
- Private()
- : m_currentId(-1)
+ Private(PackageManagerGui *qq)
+ : q(qq)
+ , m_currentId(-1)
, m_modified(false)
, m_autoSwitchPage(true)
, m_showSettingsButton(false)
@@ -235,6 +238,20 @@ public:
QLatin1String("unknown button"));
}
+ void showSettingsButton(bool show)
+ {
+ if (m_showSettingsButton == show)
+ return;
+ q->setOption(QWizard::HaveCustomButton1, show);
+ q->setButtonText(QWizard::CustomButton1, tr("&Settings"));
+ q->button(QWizard::CustomButton1)->setToolTip(
+ PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components."));
+
+ q->updateButtonLayout();
+ m_showSettingsButton = show;
+ }
+
+ PackageManagerGui *q;
int m_currentId;
bool m_modified;
bool m_autoSwitchPage;
@@ -283,6 +300,11 @@ public:
*/
/*!
+ \fn void QInstaller::PackageManagerGui::aboutApplicationClicked()
+ \sa {gui::aboutApplicationClicked}{gui.aboutApplicationClicked}
+*/
+
+/*!
\fn void QInstaller::PackageManagerGui::packageManagerCore() const
Returns the package manager core.
@@ -294,7 +316,7 @@ public:
*/
PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
: QWizard(parent)
- , d(new Private)
+ , d(new Private(this))
, m_core(core)
{
if (m_core->isInstaller())
@@ -390,10 +412,13 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled);
connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt);
- // both queued to show the finished page once everything is done
+ // all queued to show the finished page once everything is done
connect(m_core, &PackageManagerCore::installationFinished,
this, &PackageManagerGui::showFinishedPage,
Qt::QueuedConnection);
+ connect(m_core, &PackageManagerCore::offlineGenerationFinished,
+ this, &PackageManagerGui::showFinishedPage,
+ Qt::QueuedConnection);
connect(m_core, &PackageManagerCore::uninstallationFinished,
this, &PackageManagerGui::showFinishedPage,
Qt::QueuedConnection);
@@ -446,7 +471,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
*/
void PackageManagerGui::setMaxSize()
{
- QSize size = qApp->desktop()->availableGeometry(this).size();
+ QSize size = this->screen()->availableGeometry().size();
int windowFrameHeight = frameGeometry().height() - geometry().height();
int availableHeight = size.height() - windowFrameHeight;
@@ -498,7 +523,7 @@ void PackageManagerGui::updatePageListWidget()
item->setFont(currentItemFont);
// Current item should be always visible on the list
m_pageListWidget->scrollToItem(item);
- } else if (id > d->m_currentId) {
+ } else {
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
}
@@ -667,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.
*/
@@ -1029,16 +1070,18 @@ void PackageManagerGui::showFinishedPage()
*/
void PackageManagerGui::showSettingsButton(bool show)
{
- if (d->m_showSettingsButton == show)
- return;
-
- d->m_showSettingsButton = show;
- setOption(QWizard::HaveCustomButton1, show);
- setButtonText(QWizard::CustomButton1, tr("&Settings"));
- button(QWizard::CustomButton1)->setToolTip(
- PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components."));
+ m_core->setValue(QLatin1String("ShowSettingsButton"), QString::number(show));
+ d->showSettingsButton(show);
+}
- updateButtonLayout();
+/*!
+ Shows the \uicontrol Settings button if \a request is \c true. If script has
+ set the settings button visibility, this function has no effect.
+*/
+void PackageManagerGui::requestSettingsButtonByInstaller(bool request)
+{
+ if (m_core->value(QLatin1String("ShowSettingsButton")).isEmpty())
+ d->showSettingsButton(request);
}
/*!
@@ -1221,10 +1264,10 @@ void PackageManagerGui::currentPageChanged(int newId)
PackageManagerPage::PackageManagerPage(PackageManagerCore *core)
: m_complete(true)
, m_titleColor(QString())
+ , m_showOnPageList(true)
, m_needsSettingsButton(false)
, m_core(core)
, validatorComponent(nullptr)
- , m_showOnPageList(true)
{
if (!m_core->settings().titleColor().isEmpty())
m_titleColor = m_core->settings().titleColor();
@@ -1288,7 +1331,7 @@ QString PackageManagerPage::productName() const
*/
void PackageManagerPage::setColoredTitle(const QString &title)
{
- setTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, title));
+ setTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, title));
}
/*!
@@ -1296,7 +1339,7 @@ void PackageManagerPage::setColoredTitle(const QString &title)
*/
void PackageManagerPage::setColoredSubTitle(const QString &subTitle)
{
- setSubTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, subTitle));
+ setSubTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, subTitle));
}
/*!
@@ -1428,20 +1471,12 @@ int PackageManagerPage::nextId() const
if (next == PackageManagerCore::LicenseCheck) {
// calculate the page after the license page
const int nextNextId = gui()->pageIds().value(gui()->pageIds().indexOf(next) + 1, -1);
- const PackageManagerCore *const core = packageManagerCore();
+ PackageManagerCore *const core = packageManagerCore();
if (core->isUninstaller())
return nextNextId; // forcibly hide the license page if we run as uninstaller
-
- core->calculateComponentsToInstall();
- 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;
- }
+ core->recalculateAllComponents();
+ 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
@@ -1470,6 +1505,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
, m_updatesFetched(false)
, m_allPackagesFetched(false)
, m_forceUpdate(false)
+ , m_offlineMaintenanceTool(false)
, m_label(nullptr)
, m_msgLabel(nullptr)
, m_errorLabel(nullptr)
@@ -1479,7 +1515,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
, m_removeAllComponents(nullptr)
{
setObjectName(QLatin1String("IntroductionPage"));
- setColoredTitle(tr("Setup - %1").arg(productName()));
+ setColoredTitle(tr("Welcome"));
QVBoxLayout *layout = new QVBoxLayout(this);
setLayout(layout);
@@ -1487,7 +1523,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_msgLabel = new QLabel(this);
m_msgLabel->setWordWrap(true);
m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
- m_msgLabel->setText(tr("Welcome to the %1 Setup Wizard.").arg(productName()));
+ m_msgLabel->setText(tr("Welcome to the %1 Setup.").arg(productName()));
QWidget *widget = new QWidget(this);
QVBoxLayout *boxLayout = new QVBoxLayout(widget);
@@ -1527,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"));
@@ -1540,9 +1578,10 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
connect(core, &PackageManagerCore::coreNetworkSettingsChanged,
this, &IntroductionPage::onCoreNetworkSettingsChanged);
- m_updateComponents->setEnabled(ProductKeyCheck::instance()->hasValidKey());
+ m_updateComponents->setEnabled(!m_offlineMaintenanceTool && ProductKeyCheck::instance()->hasValidKey());
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
connect(core, &PackageManagerCore::metaJobProgress,
@@ -1551,6 +1590,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -1581,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();
@@ -1596,6 +1636,7 @@ bool IntroductionPage::validatePage()
}
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0 ,0)
if (m_taskButton) {
if (!m_taskButton->window()) {
if (QWidget *widget = QApplication::activeWindow())
@@ -1607,6 +1648,7 @@ bool IntroductionPage::validatePage()
m_taskButton->progress()->setVisible(true);
}
#endif
+#endif
// fetch updater packages
if (core->isUpdater()) {
@@ -1626,21 +1668,12 @@ bool IntroductionPage::validatePage()
// fetch common packages
if (core->isInstaller() || core->isPackageManager()) {
- bool localPackagesTreeFetched = false;
if (!m_allPackagesFetched) {
// first try to fetch the server side packages tree
m_allPackagesFetched = core->fetchRemotePackagesTree();
if (!m_allPackagesFetched) {
QString error = core->error();
- if (core->isPackageManager() && core->status() != PackageManagerCore::ForceUpdate) {
- // if that fails and we're in maintenance mode, try to fetch local installed tree
- localPackagesTreeFetched = core->fetchLocalPackagesTree();
- if (localPackagesTreeFetched) {
- // if that succeeded, adjust error message
- error = QLatin1String("<font color=\"red\">") + error + tr(" Only local package "
- "management available.") + QLatin1String("</font>");
- }
- } else if (core->status() == PackageManagerCore::ForceUpdate) {
+ if (core->status() == PackageManagerCore::ForceUpdate) {
// replaces the error string from packagemanagercore
error = tr("There is an important update available. Please select '%1' first")
.arg(m_updateComponents->text().remove(QLatin1Char('&')));
@@ -1658,7 +1691,7 @@ bool IntroductionPage::validatePage()
}
}
- if (m_allPackagesFetched || localPackagesTreeFetched)
+ if (m_allPackagesFetched)
setComplete(true);
}
@@ -1671,9 +1704,11 @@ bool IntroductionPage::validatePage()
gui()->setSettingsButtonEnabled(true);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton)
m_taskButton->progress()->setVisible(!isComplete());
#endif
+#endif
return isComplete();
}
@@ -1715,15 +1750,41 @@ void IntroductionPage::showMaintenanceTools()
/*!
Sets \a enable to \c true to enable the options to install, add, and
- uninstall components on the page.
+ uninstall components on the page. For a maintenance tool without any enabled
+ repositories, the package manager and updater stay disabled regardless of
+ the value of \a enable.
*/
void IntroductionPage::setMaintenanceToolsEnabled(bool enable)
{
- m_packageManager->setEnabled(enable);
- m_updateComponents->setEnabled(enable && ProductKeyCheck::instance()->hasValidKey());
+ m_packageManager->setEnabled(enable && !m_offlineMaintenanceTool);
+ m_updateComponents->setEnabled(enable && !m_offlineMaintenanceTool
+ && ProductKeyCheck::instance()->hasValidKey());
m_removeAllComponents->setEnabled(enable);
}
+/*!
+ Enables or disables the options to add or update components based on the
+ value of \a enable. For a maintenance tool without any enabled repositories,
+ the package manager and updater stay disabled regardless of the value of \a enable.
+*/
+void IntroductionPage::setMaintainerToolsEnabled(bool enable)
+{
+ m_packageManager->setEnabled(enable && !m_offlineMaintenanceTool);
+ m_updateComponents->setEnabled(enable && !m_offlineMaintenanceTool
+ && ProductKeyCheck::instance()->hasValidKey());
+}
+
+/*!
+ Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be
+ fetched again.
+*/
+void IntroductionPage::resetFetchedState()
+{
+ m_updatesFetched = false;
+ m_allPackagesFetched = false;
+ m_forceUpdate = false;
+}
+
// -- public slots
/*!
@@ -1768,29 +1829,15 @@ void IntroductionPage::setErrorMessage(const QString &error)
m_errorLabel->setPalette(palette);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
m_taskButton->progress()->stop();
m_taskButton->progress()->setValue(100);
}
#endif
+#endif
}
-/*!
- 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
@@ -1798,7 +1845,7 @@ void IntroductionPage::setUpdater(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setUpdater();
emit packageManagerCoreTypeChanged();
@@ -1810,7 +1857,7 @@ void IntroductionPage::setUninstaller(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(false);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setUninstaller();
emit packageManagerCoreTypeChanged();
@@ -1822,7 +1869,7 @@ void IntroductionPage::setPackageManager(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setPackageManager();
emit packageManagerCoreTypeChanged();
@@ -1835,6 +1882,8 @@ void IntroductionPage::setPackageManager(bool value)
void IntroductionPage::initializePage()
{
PackageManagerCore *core = packageManagerCore();
+ const bool repositoriesAvailable = core->validRepositoriesAvailable();
+
if (core->isPackageManager()) {
m_packageManager->setChecked(true);
} else if (core->isUpdater()) {
@@ -1843,25 +1892,35 @@ void IntroductionPage::initializePage()
// If we are running maintenance tool and the default uninstaller
// marker is not overridden, set the default checked radio button
// based on if we have valid repositories available.
- if (!core->isUserSetBinaryMarker() && validRepositoriesAvailable()) {
+ if (!core->isUserSetBinaryMarker() && repositoriesAvailable) {
m_packageManager->setChecked(true);
} else {
// No repositories available, default to complete uninstallation.
m_removeAllComponents->setChecked(true);
core->setCompleteUninstallation(true);
}
+ // Disable options that are unusable without repositories
+ m_offlineMaintenanceTool = !repositoriesAvailable;
+ setMaintainerToolsEnabled(repositoriesAvailable);
}
}
/*!
Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be
- fetched again.
+ fetched again. For maintenance tool, enables or disables options requiring enabled repositories
+ based on the current repository settings.
*/
void IntroductionPage::onCoreNetworkSettingsChanged()
{
- m_updatesFetched = false;
- m_allPackagesFetched = false;
- m_forceUpdate = false;
+ resetFetchedState();
+
+ PackageManagerCore *core = packageManagerCore();
+ if (core->isUninstaller() || core->isMaintainer()) {
+ m_offlineMaintenanceTool = !core->validRepositoriesAvailable();
+
+ setMaintainerToolsEnabled(!m_offlineMaintenanceTool);
+ m_removeAllComponents->setChecked(m_offlineMaintenanceTool);
+ }
}
// -- private
@@ -1887,7 +1946,7 @@ void IntroductionPage::entering()
if (m_forceUpdate)
m_packageManager->setEnabled(false);
- setSettingsButtonRequested((!core->isOfflineOnly()) && (!core->isUninstaller()));
+ setSettingsButtonRequested((!core->isOfflineOnly()));
}
/*!
@@ -1896,9 +1955,6 @@ void IntroductionPage::entering()
*/
void IntroductionPage::leaving()
{
- // force a recalculation of components to install to keep the state correct
- if (!packageManagerCore()->isUninstaller())
- packageManagerCore()->componentsToInstallNeedsRecalculation();
m_progressBar->setValue(0);
m_progressBar->setRange(0, 0);
setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton));
@@ -2028,7 +2084,6 @@ void LicenseAgreementPage::entering()
m_textBrowser->setHtml(QString());
m_licenseListWidget->setVisible(false);
- packageManagerCore()->calculateComponentsToInstall();
foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall())
packageManagerCore()->addLicenseItem(component->licenses());
@@ -2051,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)
@@ -2144,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.")
};
@@ -2159,18 +2214,16 @@ void ComponentSelectionPage::entering()
d->updateTreeView();
// check component model state so we can enable needed component selection buttons
- if (core->isUpdater())
- d->onModelStateChanged(d->m_currentModel->checkedState());
+ 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);
+
+ // Reset to default supplement state. The page may set it to OfflineGenerator
+ // which needs to be reset after navigating back to the page.
+ core->resetBinaryMarkerSupplement();
}
/*!
@@ -2180,6 +2233,7 @@ void ComponentSelectionPage::entering()
void ComponentSelectionPage::leaving()
{
d->hideCompressedRepositoryButton();
+ d->showCreateOfflineInstallerButton(false);
}
/*!
@@ -2198,6 +2252,29 @@ void ComponentSelectionPage::showEvent(QShowEvent *event)
}
/*!
+ Called when \c ComponentSelectionPage is validated.
+ Tries to load \c component scripts for components about to be installed.
+ Returns \c true if the script loading succeeded and the next page is shown.
+*/
+bool ComponentSelectionPage::validatePage()
+{
+ PackageManagerCore *core = packageManagerCore();
+ try {
+ core->loadComponentScripts(core->orderedComponentsToInstall(), true);
+ } catch (const Error &error) {
+ // As component script loading failed, there is error in the script and component is
+ // marked as unselected. Recalculate so that unselected component is removed from install.
+ // User is then able to select other components for install.
+ core->clearComponentsToInstallCalculated();
+ core->calculateComponentsToInstall();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
Selects all components in the component tree.
*/
void ComponentSelectionPage::selectAll()
@@ -2240,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.
@@ -2262,13 +2329,18 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name)
name, allComponents);
if (component && component->isInstalled() && component->isVirtual()) {
component->setCheckState(Qt::Unchecked);
- core->componentsToInstallNeedsRecalculation();
+ core->recalculateAllComponents();
qCDebug(QInstaller::lcDeveloperBuild) << "Virtual component " << name << " was selected for uninstall by script.";
return true;
}
return false;
}
+void ComponentSelectionPage::setAllowCreateOfflineInstaller(bool allow)
+{
+ d->setAllowCreateOfflineInstaller(allow);
+}
+
void ComponentSelectionPage::setModified(bool modified)
{
setComplete(modified);
@@ -2279,18 +2351,10 @@ void ComponentSelectionPage::setModified(bool modified)
*/
bool ComponentSelectionPage::isComplete() const
{
- if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater())
- return d->m_currentModel->checked().count();
-
- if (d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false)
- return true;
+ if (!d->componentsResolved())
+ return false;
- 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();
}
@@ -2616,7 +2680,7 @@ void ReadyForInstallationPage::entering()
m_taskDetailsBrowser->setVisible(false);
setButtonText(QWizard::CommitButton, tr("U&ninstall"));
setColoredTitle(tr("Ready to Uninstall"));
- m_msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.<br>"
+ m_msgLabel->setText(tr("All required information is now available to begin removing %1 from your computer.<br>"
"<font color=\"red\">The program directory %2 will be deleted completely</font>, "
"including all content in that directory!")
.arg(productName(),
@@ -2627,26 +2691,31 @@ void ReadyForInstallationPage::entering()
} else if (packageManagerCore()->isMaintainer()) {
setButtonText(QWizard::CommitButton, tr("U&pdate"));
setColoredTitle(tr("Ready to Update Packages"));
- m_msgLabel->setText(tr("Setup is now ready to begin updating your installation."));
+ m_msgLabel->setText(tr("All required information is now available to begin updating your installation."));
+ } else if (packageManagerCore()->isOfflineGenerator()) {
+ setButtonText(QWizard::CommitButton, tr("Create Offline Installer"));
+ setColoredTitle(tr("Ready to Create Offline Installer"));
+ m_msgLabel->setText(tr("All required information is now available to create an offline installer for selected components."));
} else {
Q_ASSERT(packageManagerCore()->isInstaller());
setButtonText(QWizard::CommitButton, tr("&Install"));
setColoredTitle(tr("Ready to Install"));
- m_msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.")
+ m_msgLabel->setText(tr("All required information is now available to begin installing %1 on your computer.")
.arg(productName()));
}
- QString htmlOutput;
- bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput);
+ bool componentsOk = packageManagerCore()->recalculateAllComponents();
+ const QString htmlOutput = packageManagerCore()->componentResolveReasons();
+
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput);
m_taskDetailsBrowser->setHtml(htmlOutput);
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);
}
}
@@ -2666,7 +2735,9 @@ void ReadyForInstallationPage::leaving()
void ReadyForInstallationPage::updatePageListTitle()
{
PackageManagerCore *core = packageManagerCore();
- if (core->isInstaller())
+ if (core->isOfflineGenerator())
+ setPageListTitle(tr("Ready to Create Offline Installer"));
+ else if (core->isInstaller())
setPageListTitle(tr("Ready to Install"));
else if (core->isMaintainer())
setPageListTitle(tr("Ready to Update"));
@@ -2724,6 +2795,11 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core)
connect(core, &PackageManagerCore::installationFinished,
this, &PerformInstallationPage::installationFinished);
+ connect(core, &PackageManagerCore::offlineGenerationStarted,
+ this, &PerformInstallationPage::installationStarted);
+ connect(core, &PackageManagerCore::offlineGenerationFinished,
+ this, &PerformInstallationPage::installationFinished);
+
connect(core, &PackageManagerCore::uninstallationStarted,
this, &PerformInstallationPage::uninstallationStarted);
connect(core, &PackageManagerCore::uninstallationFinished,
@@ -2793,6 +2869,11 @@ void PerformInstallationPage::entering()
setColoredTitle(tr("Updating components of %1").arg(productName()));
QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater()));
+ } else if (packageManagerCore()->isOfflineGenerator()) {
+ setButtonText(QWizard::CommitButton, tr("&Create Offline Installer"));
+ setColoredTitle(tr("Creating Offline Installer for %1").arg(productName()));
+
+ QTimer::singleShot(30, packageManagerCore(), SLOT(runOfflineGenerator()));
} else {
setButtonText(QWizard::CommitButton, tr("&Install"));
setColoredTitle(tr("Installing %1").arg(productName()));
@@ -2817,7 +2898,9 @@ void PerformInstallationPage::leaving()
void PerformInstallationPage::updatePageListTitle()
{
PackageManagerCore *core = packageManagerCore();
- if (core->isInstaller())
+ if (core->isOfflineGenerator())
+ setPageListTitle(tr("Creating Offline Installer"));
+ else if (core->isInstaller())
setPageListTitle(tr("Installing"));
else if (core->isMaintainer())
setPageListTitle(tr("Updating"));
@@ -2921,7 +3004,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core)
, m_commitButton(nullptr)
{
setObjectName(QLatin1String("FinishedPage"));
- setColoredTitle(tr("Completing the %1 Wizard").arg(productName()));
+ setColoredTitle(tr("Finished the %1 Setup").arg(productName()));
setPageListTitle(tr("Finished"));
m_msgLabel = new QLabel(this);
@@ -2946,7 +3029,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core)
*/
void FinishedPage::entering()
{
- m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.")
+ m_msgLabel->setText(tr("Click %1 to exit the %2 Setup.")
.arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&')))
.arg(productName()));
@@ -3008,7 +3091,7 @@ void FinishedPage::entering()
}
} else {
// TODO: how to handle this using the config.xml
- setColoredTitle(tr("The %1 Wizard failed.").arg(productName()));
+ setColoredTitle(tr("The %1 Setup failed.").arg(productName()));
}
m_runItCheckBox->hide();
@@ -3039,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();
}
/*!
@@ -3093,7 +3168,7 @@ RestartPage::RestartPage(PackageManagerCore *core)
: PackageManagerPage(core)
{
setObjectName(QLatin1String("RestartPage"));
- setColoredTitle(tr("Completing the %1 Setup Wizard").arg(productName()));
+ setColoredTitle(tr("Finished the %1 Setup").arg(productName()));
// Never show this page on the page list
setShowOnPageList(false);
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index 21c885284..d83643005 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.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.
@@ -82,8 +82,10 @@ 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);
void setSettingsButtonEnabled(bool enable);
void updateButtonLayout();
@@ -234,6 +236,9 @@ public:
void showMetaInfoUpdate();
void showMaintenanceTools();
void setMaintenanceToolsEnabled(bool enable);
+ void setMaintainerToolsEnabled(bool enable);
+
+ void resetFetchedState();
public Q_SLOTS:
void onCoreNetworkSettingsChanged();
@@ -257,12 +262,12 @@ private:
void leaving() override;
void showWidgets(bool show);
- bool validRepositoriesAvailable() const;
private:
bool m_updatesFetched;
bool m_allPackagesFetched;
bool m_forceUpdate;
+ bool m_offlineMaintenanceTool;
QLabel *m_label;
QLabel *m_msgLabel;
@@ -325,13 +330,15 @@ 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);
+
protected:
void entering() override;
void leaving() override;
void showEvent(QShowEvent *event) override;
+ bool validatePage() override;
private Q_SLOTS:
void setModified(bool modified);
diff --git a/src/libs/installer/packagesource.cpp b/src/libs/installer/packagesource.cpp
index 3b9fbe813..0f87e0def 100644
--- a/src/libs/installer/packagesource.cpp
+++ b/src/libs/installer/packagesource.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -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.
@@ -69,7 +69,7 @@ namespace QInstaller {
/*!
Returns the hash value for the \a key, using \a seed to seed the calculation.
*/
-uint qHash(const PackageSource &key, uint seed)
+hashValue qHash(const PackageSource &key, hashValue seed)
{
return qHash(key.url, seed) ^ key.priority;
}
diff --git a/src/libs/installer/packagesource.h b/src/libs/installer/packagesource.h
index 1193c1f76..f63b53cd8 100644
--- a/src/libs/installer/packagesource.h
+++ b/src/libs/installer/packagesource.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,16 +40,18 @@ struct INSTALLER_EXPORT PackageSource
PackageSource()
: priority(-1)
{}
- PackageSource(const QUrl &u, int p)
+ PackageSource(const QUrl &u, int p, bool pl = false)
: url(u)
, priority(p)
+ , postLoadComponentScript(pl)
{}
QUrl url;
int priority;
+ bool postLoadComponentScript;
};
-INSTALLER_EXPORT uint qHash(const PackageSource &key, uint seed);
+INSTALLER_EXPORT hashValue qHash(const PackageSource &key, hashValue seed);
INSTALLER_EXPORT bool operator==(const PackageSource &lhs, const PackageSource &rhs);
} // namespace QInstaller
diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp
index d8d1e3dcb..0e4c561f6 100644
--- a/src/libs/installer/performinstallationform.cpp
+++ b/src/libs/installer/performinstallationform.cpp
@@ -49,9 +49,11 @@
#include <QtCore/QTimer>
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace QInstaller;
@@ -96,6 +98,7 @@ PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObje
, m_core(core)
{
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
m_taskButton->progress()->setVisible(true);
@@ -103,6 +106,7 @@ PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObje
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -204,12 +208,14 @@ void PerformInstallationForm::updateProgress()
m_progressBar->setValue(progressPercentage);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
if (!m_taskButton->window() && QApplication::activeWindow())
m_taskButton->setWindow(QApplication::activeWindow()->windowHandle());
m_taskButton->progress()->setValue(progressPercentage);
}
#endif
+#endif
static QString lastLabelText;
if (lastLabelText == progressCoordninator->labelText())
diff --git a/src/libs/installer/permissionsettings.cpp b/src/libs/installer/permissionsettings.cpp
index 235c6667b..d70cf5625 100644
--- a/src/libs/installer/permissionsettings.cpp
+++ b/src/libs/installer/permissionsettings.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,30 +37,6 @@ using namespace QInstaller;
\internal
*/
-PermissionSettings::PermissionSettings(const QString &organization, const QString &application, QObject *parent)
- : QSettings(organization, application, parent)
-{
- setIniCodec("UTF-8"); // to workaround QTBUG-102334
-}
-
-PermissionSettings::PermissionSettings(Scope scope, const QString &organization, const QString &application, QObject *parent)
- : QSettings(scope, organization, application, parent)
-{
- setIniCodec("UTF-8"); // QTBUG-102334
-}
-
-PermissionSettings::PermissionSettings(Format format, Scope scope, const QString &organization, const QString &application, QObject *parent)
- : QSettings(format, scope, organization, application, parent)
-{
- setIniCodec("UTF-8"); // QTBUG-102334
-}
-
-PermissionSettings::PermissionSettings(const QString &fileName, Format format, QObject *parent)
- : QSettings(fileName, format, parent)
-{
- setIniCodec("UTF-8"); // QTBUG-102334
-}
-
PermissionSettings::~PermissionSettings()
{
if (!fileName().isEmpty()) {
diff --git a/src/libs/installer/permissionsettings.h b/src/libs/installer/permissionsettings.h
index 2621624c2..c950d9c17 100644
--- a/src/libs/installer/permissionsettings.h
+++ b/src/libs/installer/permissionsettings.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,12 +37,19 @@ class PermissionSettings : public QSettings
{
public:
explicit PermissionSettings(const QString &organization,
- const QString &application = QString(), QObject *parent = 0);
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(organization, application, parent) {}
+
PermissionSettings(Scope scope, const QString &organization,
- const QString &application = QString(), QObject *parent = 0);
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(scope, organization, application, parent) {}
+
PermissionSettings(Format format, Scope scope, const QString &organization,
- const QString &application = QString(), QObject *parent = 0);
- PermissionSettings(const QString &fileName, Format format, QObject *parent = 0);
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(format, scope, organization, application, parent) {}
+ PermissionSettings(const QString &fileName, Format format, QObject *parent = 0)
+ : QSettings(fileName, format, parent) {}
+
~PermissionSettings();
};
diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp
index c1dfe83d6..ed128fa61 100644
--- a/src/libs/installer/productkeycheck.cpp
+++ b/src/libs/installer/productkeycheck.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#include "productkeycheck.h"
#include "packagemanagercore.h"
+#include <QtUiTools/QUiLoader>
+
class ProductKeyCheckPrivate
{
};
@@ -49,6 +51,12 @@ ProductKeyCheck *ProductKeyCheck::instance()
return &instance;
}
+QUiLoader *ProductKeyCheck::uiLoader()
+{
+ static QUiLoader loader;
+ return &loader;
+}
+
void ProductKeyCheck::init(QInstaller::PackageManagerCore *core)
{
Q_UNUSED(core)
@@ -106,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 b7e8c6d52..8e7d6724f 100644
--- a/src/libs/installer/productkeycheck.h
+++ b/src/libs/installer/productkeycheck.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "installer_global.h"
#include <QString>
+#include <QUiLoader>
namespace QInstaller {
@@ -49,6 +50,8 @@ public:
static ProductKeyCheck *instance();
void init(QInstaller::PackageManagerCore *core);
+ static QUiLoader *uiLoader();
+
// was validLicense
bool hasValidKey();
QString lastErrorString();
@@ -67,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 8b02711b1..6413efe28 100644
--- a/src/libs/installer/progresscoordinator.cpp
+++ b/src/libs/installer/progresscoordinator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,7 +40,7 @@
using namespace QInstaller;
QT_BEGIN_NAMESPACE
-uint qHash(QPointer<QObject> key)
+hashValue qHash(QPointer<QObject> key)
{
return qHash(key.data());
}
@@ -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/protocol.h b/src/libs/installer/protocol.h
index 841b1a2a3..8b2288a89 100644
--- a/src/libs/installer/protocol.h
+++ b/src/libs/installer/protocol.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -50,6 +50,7 @@ enum struct StartAs {
const char DefaultSocket[] = "ifw_srv";
const char DefaultAuthorizationKey[] = "DefaultAuthorizationKey";
+const char DefaultReply[] = "DefaultReply";
const char Create[] = "Create";
const char Shutdown[] = "Shutdown";
@@ -66,6 +67,7 @@ const char QProcessReadAll[] = "QProcess::readAll";
const char QProcessReadAllStandardOutput[] = "QProcess::readAllStandardOutput";
const char QProcessReadAllStandardError[] = "QProcess::readAllStandardError";
const char QProcessStartDetached[] = "QProcess::startDetached";
+const char QProcessStartDetached2[] = "QProcess::startDetached2";
const char QProcessSetWorkingDirectory[] = "QProcess::setWorkingDirectory";
const char QProcessSetEnvironment[] = "QProcess::setEnvironment";
const char QProcessEnvironment[] = "QProcess::environment";
@@ -88,7 +90,7 @@ const char GetQProcessSignals[] = "GetQProcessSignals";
const char QProcessSignalBytesWritten[] = "QProcess::bytesWritten";
const char QProcessSignalAboutToClose[] = "QProcess::aboutToClose";
const char QProcessSignalReadChannelFinished[] = "QProcess::readChannelFinished";
-const char QProcessSignalError[] = "QProcess::error";
+const char QProcessSignalError[] = "QProcess::errorOccurred";
const char QProcessSignalReadyReadStandardOutput[] = "QProcess::readyReadStandardOutput";
const char QProcessSignalReadyReadStandardError[] = "QProcess::readyReadStandardError";
const char QProcessSignalStarted[] = "QProcess::started";
diff --git a/src/libs/installer/qinstallerglobal.cpp b/src/libs/installer/qinstallerglobal.cpp
index 8b1b9c6ad..457690892 100644
--- a/src/libs/installer/qinstallerglobal.cpp
+++ b/src/libs/installer/qinstallerglobal.cpp
@@ -39,6 +39,7 @@
\value ExtractionError
\value UserIgnoreError
\value RepositoryUpdatesReceived
+ \value CacheError
*/
/*!
@@ -66,9 +67,10 @@
*/
/*!
- \typedef QInstaller::LocalPackagesHash
+ \typedef QInstaller::LocalPackagesMap
- Synonym for QHash<QString, KDUpdater::LocalPackage>.
+ Synonym for QMap<QString, KDUpdater::LocalPackage>. The map key is component name,
+ and value contains information about the local package.
*/
/*!
@@ -85,7 +87,7 @@
*/
/*!
- \typedef QInstaller::DependencyHash
+ \typedef QInstaller::LocalDependencyHash
Synonym for QHash<QString, QStringList>. The hash key is component name,
which other components depend on. The value can contain several component
diff --git a/src/libs/installer/qinstallerglobal.h b/src/libs/installer/qinstallerglobal.h
index 88f255af6..a750e6583 100644
--- a/src/libs/installer/qinstallerglobal.h
+++ b/src/libs/installer/qinstallerglobal.h
@@ -46,7 +46,8 @@ enum INSTALLER_EXPORT JobError
InvalidMetaInfo,
ExtractionError,
UserIgnoreError,
- RepositoryUpdatesReceived
+ RepositoryUpdatesReceived,
+ CacheError
};
typedef KDUpdater::UpdateOperation Operation;
diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp
index b33e7d43c..44117eefb 100644
--- a/src/libs/installer/qprocesswrapper.cpp
+++ b/src/libs/installer/qprocesswrapper.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "utils.h"
#include <QDir>
+#include <QVariant>
namespace QInstaller {
@@ -53,7 +54,7 @@ QProcessWrapper::QProcessWrapper(QObject *parent)
connect(&process, &QIODevice::bytesWritten, this, &QProcessWrapper::bytesWritten);
connect(&process, &QIODevice::aboutToClose, this, &QProcessWrapper::aboutToClose);
connect(&process, &QIODevice::readChannelFinished, this, &QProcessWrapper::readChannelFinished);
- connect(&process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError)));
+ connect(&process, SIGNAL(errorOccurred(QProcess::ProcessError)), SIGNAL(errorOccurred(QProcess::ProcessError)));
connect(&process, &QProcess::readyReadStandardOutput, this, &QProcessWrapper::readyReadStandardOutput);
connect(&process, &QProcess::readyReadStandardError, this, &QProcessWrapper::readyReadStandardError);
connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
@@ -87,7 +88,7 @@ void QProcessWrapper::processSignals()
} else if (name == QLatin1String(Protocol::QProcessSignalReadChannelFinished)) {
emit readChannelFinished();
} else if (name == QLatin1String(Protocol::QProcessSignalError)) {
- emit error(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt()));
+ emit errorOccurred(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt()));
} else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)) {
emit readyReadStandardOutput();
} else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardError)) {
@@ -107,6 +108,11 @@ void QProcessWrapper::processSignals()
m_lock.unlock();
}
+/*!
+ Starts the \a program with \a arguments in the working directory \a workingDirectory as a detached
+ process. The process id can be retrieved with the \a pid parameter. Compared to the QProcess
+ implementation of the same method this does not show a window for the started process on Windows.
+*/
bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory, qint64 *pid)
{
@@ -123,22 +129,70 @@ bool QProcessWrapper::startDetached(const QString &program, const QStringList &a
return QInstaller::startDetached(program, arguments, workingDirectory, pid);
}
+/*!
+ Starts the \a program with \a arguments as a detached process. Compared to the QProcess
+ implementation of the same method this does not show a window for the started process on Windows.
+*/
bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments)
{
return startDetached(program, arguments, QDir::currentPath());
}
+/*!
+ Starts the \a program as a detached process. Compared to the QProcess implementation of the same
+ method this does not show a window for the started process on Windows.
+*/
bool QProcessWrapper::startDetached(const QString &program)
{
return startDetached(program, QStringList());
}
+/*!
+ Starts the \a program as a detached process. The variants of the function suffixed with \c 2
+ use the base \c QProcess::startDetached implementation internally to start the process.
+*/
+bool QProcessWrapper::startDetached2(const QString &program)
+{
+ return startDetached2(program, QStringList());
+}
+
+/*!
+ Starts the \a program with \a arguments as a detached process. The variants of the function
+ suffixed with \c 2 use the base \c QProcess::startDetached implementation internally to
+ start the process.
+*/
+bool QProcessWrapper::startDetached2(const QString &program, const QStringList &arguments)
+{
+ return startDetached2(program, arguments, QDir::currentPath());
+}
+
+/*!
+ Starts the \a program with \a arguments in the working directory \a workingDirectory as a detached
+ process. The process id can be retrieved with the \a pid parameter. The variants
+ of the function suffixed with \c 2 use the base \c QProcess::startDetached implementation
+ internally to start the process.
+*/
+bool QProcessWrapper::startDetached2(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+{
+ QProcessWrapper w;
+ if (w.connectToServer()) {
+ const QPair<bool, qint64> result =
+ w.callRemoteMethod<QPair<bool, qint64> >(QLatin1String(Protocol::QProcessStartDetached2),
+ program, arguments, workingDirectory);
+ if (pid != nullptr)
+ *pid = result.second;
+ w.processSignals();
+ return result.first;
+ }
+ return QProcess::startDetached(program, arguments, workingDirectory, pid);
+}
+
void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode mode)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetProcessChannelMode),
- static_cast<QProcess::ProcessChannelMode>(mode), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetProcessChannelMode),
+ static_cast<QProcess::ProcessChannelMode>(mode));
m_lock.unlock();
} else {
process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode));
@@ -162,8 +216,8 @@ void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetReadChannel),
- static_cast<QProcess::ProcessChannel>(chan), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetReadChannel),
+ static_cast<QProcess::ProcessChannel>(chan));
m_lock.unlock();
} else {
process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan));
@@ -209,7 +263,7 @@ void QProcessWrapper::closeWriteChannel()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessCloseWriteChannel));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessCloseWriteChannel));
m_lock.unlock();
} else {
process.closeWriteChannel();
@@ -242,7 +296,7 @@ void QProcessWrapper::kill()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessKill));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessKill));
m_lock.unlock();
} else {
process.kill();
@@ -289,7 +343,7 @@ void QProcessWrapper::start(const QString &param1, const QStringList &param2,
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3);
m_lock.unlock();
} else {
process.start(param1, param2, param3);
@@ -300,10 +354,10 @@ void QProcessWrapper::start(const QString &param1, QIODevice::OpenMode param2)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
m_lock.unlock();
} else {
- process.start(param1, param2);
+ process.start(param1, {}, param2);
}
}
@@ -322,7 +376,7 @@ void QProcessWrapper::terminate()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessTerminate));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessTerminate));
m_lock.unlock();
} else {
process.terminate();
@@ -389,7 +443,7 @@ void QProcessWrapper::setEnvironment(const QStringList &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetEnvironment), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetEnvironment), param1);
m_lock.unlock();
} else {
process.setEnvironment(param1);
@@ -401,7 +455,7 @@ void QProcessWrapper::setNativeArguments(const QString &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetNativeArguments), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetNativeArguments), param1);
m_lock.unlock();
} else {
process.setNativeArguments(param1);
@@ -413,7 +467,7 @@ void QProcessWrapper::setWorkingDirectory(const QString &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1);
m_lock.unlock();
} else {
process.setWorkingDirectory(param1);
diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h
index e76217eda..b34d6f82c 100644
--- a/src/libs/installer/qprocesswrapper.h
+++ b/src/libs/installer/qprocesswrapper.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -104,6 +104,11 @@ public:
static bool startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory, qint64 *pid = 0);
+ static bool startDetached2(const QString &program);
+ static bool startDetached2(const QString &program, const QStringList &arguments);
+ static bool startDetached2(const QString &program, const QStringList &arguments,
+ const QString &workingDirectory, qint64 *pid = 0);
+
QString errorString() const;
qint64 write(const QByteArray &byteArray);
#ifdef Q_OS_WIN
@@ -114,7 +119,7 @@ Q_SIGNALS:
void bytesWritten(qint64);
void aboutToClose();
void readChannelFinished();
- void error(QProcess::ProcessError);
+ void errorOccurred(QProcess::ProcessError);
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
diff --git a/src/libs/installer/qsettingswrapper.cpp b/src/libs/installer/qsettingswrapper.cpp
index f57750bc1..d322728f5 100644
--- a/src/libs/installer/qsettingswrapper.cpp
+++ b/src/libs/installer/qsettingswrapper.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -107,18 +107,18 @@ QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Scope scope, const QString
{
}
-QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Format format, QSettingsWrapper::Scope scope,
+QSettingsWrapper::QSettingsWrapper(QSettings::Format format, QSettingsWrapper::Scope scope,
const QString &organization, const QString &application, QObject *parent)
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
- , d(new Private(static_cast<QSettings::Format>(format), static_cast<QSettings::Scope> (scope),
+ , d(new Private(format, static_cast<QSettings::Scope> (scope),
organization, application))
{
}
-QSettingsWrapper::QSettingsWrapper(const QString &fileName, QSettingsWrapper::Format format,
+QSettingsWrapper::QSettingsWrapper(const QString &fileName, QSettings::Format format,
QObject *parent)
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
- , d(new Private(fileName, static_cast<QSettings::Format>(format)))
+ , d(new Private(fileName, format))
{
}
@@ -141,27 +141,39 @@ QString QSettingsWrapper::applicationName() const
return d->settings.applicationName();
}
-void QSettingsWrapper::beginGroup(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::beginGroup(const QString &prefix)
+#else
+ void QSettingsWrapper::beginGroup(QAnyStringView prefix)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsBeginGroup), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginGroup), prefix);
else
- d->settings.beginGroup(param1);
+ d->settings.beginGroup(prefix);
}
-int QSettingsWrapper::beginReadArray(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ int QSettingsWrapper::beginReadArray(const QString &prefix)
+#else
+ int QSettingsWrapper::beginReadArray(QAnyStringView prefix)
+#endif
{
if (createSocket())
- return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), param1);
- return d->settings.beginReadArray(param1);
+ return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), prefix);
+ return d->settings.beginReadArray(prefix);
}
-void QSettingsWrapper::beginWriteArray(const QString &param1, int param2)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::beginWriteArray(const QString &prefix, int size)
+#else
+ void QSettingsWrapper::beginWriteArray(QAnyStringView prefix, int size)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsBeginWriteArray), param1, qint32(param2));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginWriteArray), prefix, qint32(size));
else
- d->settings.beginWriteArray(param1, param2);
+ d->settings.beginWriteArray(prefix, size);
}
QStringList QSettingsWrapper::childGroups() const
@@ -181,21 +193,25 @@ QStringList QSettingsWrapper::childKeys() const
void QSettingsWrapper::clear()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsClear));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsClear));
else d->settings.clear();
}
-bool QSettingsWrapper::contains(const QString &param1) const
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ bool QSettingsWrapper::contains(const QString &key) const
+#else
+ bool QSettingsWrapper::contains(QAnyStringView key) const
+#endif
{
if (createSocket())
- return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), param1);
- return d->settings.contains(param1);
+ return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), key);
+ return d->settings.contains(key);
}
void QSettingsWrapper::endArray()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsEndArray));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndArray));
else
d->settings.endArray();
}
@@ -203,7 +219,7 @@ void QSettingsWrapper::endArray()
void QSettingsWrapper::endGroup()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsEndGroup));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndGroup));
else
d->settings.endGroup();
}
@@ -222,10 +238,10 @@ QString QSettingsWrapper::fileName() const
return d->settings.fileName();
}
-QSettingsWrapper::Format QSettingsWrapper::format() const
+QSettings::Format QSettingsWrapper::format() const
{
// No need to talk to the server, we've setup the local settings object the same way.
- return static_cast<QSettingsWrapper::Format>(d->settings.format());
+ return d->settings.format();
}
QString QSettingsWrapper::group() const
@@ -249,12 +265,16 @@ QString QSettingsWrapper::organizationName() const
return d->settings.organizationName();
}
-void QSettingsWrapper::remove(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::remove(const QString &key)
+#else
+ void QSettingsWrapper::remove(QAnyStringView key)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsRemove), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsRemove), key);
else
- d->settings.remove(param1);
+ d->settings.remove(key);
}
QSettingsWrapper::Scope QSettingsWrapper::scope() const
@@ -266,7 +286,7 @@ QSettingsWrapper::Scope QSettingsWrapper::scope() const
void QSettingsWrapper::setArrayIndex(int param1)
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1));
else
d->settings.setArrayIndex(param1);
}
@@ -274,17 +294,20 @@ void QSettingsWrapper::setArrayIndex(int param1)
void QSettingsWrapper::setFallbacksEnabled(bool param1)
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1);
else
d->settings.setFallbacksEnabled(param1);
}
-
-void QSettingsWrapper::setValue(const QString &param1, const QVariant &param2)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+void QSettingsWrapper::setValue(const QString &key, const QVariant &value)
+#else
+void QSettingsWrapper::setValue(QAnyStringView key, const QVariant &value)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetValue), param1, param2);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetValue), key, value);
else
- d->settings.setValue(param1, param2);
+ d->settings.setValue(key, value);
}
QSettingsWrapper::Status QSettingsWrapper::status() const
@@ -299,27 +322,36 @@ QSettingsWrapper::Status QSettingsWrapper::status() const
void QSettingsWrapper::sync()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSync));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSync));
else
d->settings.sync();
}
-QVariant QSettingsWrapper::value(const QString &param1, const QVariant &param2) const
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+QVariant QSettingsWrapper::value(const QString &key, const QVariant &value) const
+#else
+QVariant QSettingsWrapper::value(QAnyStringView key, const QVariant &value) const
+#endif
+{
+ if (createSocket())
+ return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key, value);
+ return d->settings.value(key, value);
+}
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
+QVariant QSettingsWrapper::value(QAnyStringView key) const
{
if (createSocket())
- return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), param1, param2);
- return d->settings.value(param1, param2);
+ return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key);
+ return d->settings.value(key);
}
+#endif
// -- private
bool QSettingsWrapper::createSocket() const
{
- if ((d->m_format != QSettings::NativeFormat) && (d->m_format != QSettings::IniFormat)) {
- Q_ASSERT_X(false, Q_FUNC_INFO, "Settings wrapper only supports QSettingsWrapper::NativeFormat"
- " and QSettingsWrapper::IniFormat.");
- }
return (const_cast<QSettingsWrapper *>(this))->connectToServer(QVariantList()
<< d->m_application << d->m_organization << d->m_scope << d->m_format << d->m_filename);
}
diff --git a/src/libs/installer/qsettingswrapper.h b/src/libs/installer/qsettingswrapper.h
index c9e6d6265..f5d428b1e 100644
--- a/src/libs/installer/qsettingswrapper.h
+++ b/src/libs/installer/qsettingswrapper.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,10 @@
#include <QVariant>
+#include <QSettings>
+
+QT_FORWARD_DECLARE_CLASS(QTextCodec)
+
namespace QInstaller {
class INSTALLER_EXPORT QSettingsWrapper : public RemoteObject
@@ -48,12 +52,6 @@ public:
FormatError
};
- enum Format {
- NativeFormat,
- IniFormat,
- InvalidFormat = 16
- };
-
enum Scope {
UserScope,
SystemScope
@@ -63,21 +61,30 @@ public:
const QString &application = QString(), QObject *parent = 0);
QSettingsWrapper(Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0);
- QSettingsWrapper(Format format, Scope scope, const QString &organization,
+ QSettingsWrapper(QSettings::Format format, Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0);
- QSettingsWrapper(const QString &fileName, Format format, QObject *parent = 0);
+ QSettingsWrapper(const QString &fileName, QSettings::Format format, QObject *parent = 0);
~QSettingsWrapper();
void clear();
void sync();
Status status() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void beginGroup(const QString &prefix);
+#else
+ void beginGroup(QAnyStringView prefix);
+#endif
void endGroup();
QString group() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
int beginReadArray(const QString &prefix);
void beginWriteArray(const QString &prefix, int size = -1);
+#else
+ int beginReadArray(QAnyStringView prefix);
+ void beginWriteArray(QAnyStringView prefix, int size = -1);
+#endif
void endArray();
void setArrayIndex(int i);
@@ -86,17 +93,27 @@ public:
QStringList childGroups() const;
bool isWritable() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void setValue(const QString &key, const QVariant &value);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+#else
+ void setValue(QAnyStringView key, const QVariant &value);
+ QVariant value(QAnyStringView key, const QVariant &defaultValue) const;
+ QVariant value(QAnyStringView key) const;
+#endif
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void remove(const QString &key);
bool contains(const QString &key) const;
-
+#else
+ void remove(QAnyStringView key);
+ bool contains(QAnyStringView key) const;
+#endif
void setFallbacksEnabled(bool b);
bool fallbacksEnabled() const;
QString fileName() const;
- Format format() const;
+ QSettings::Format format() const;
Scope scope() const;
QString organizationName() const;
QString applicationName() const;
@@ -109,28 +126,27 @@ private: // we cannot support the following functionality
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
{}
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void setIniCodec(QTextCodec * /*codec*/);
void setIniCodec(const char * /*codecName*/);
QTextCodec *iniCodec() const { return 0; }
-
- static void setDefaultFormat(Format /*format*/);
- static Format defaultFormat() { return NativeFormat; }
- static void setSystemIniPath(const QString & /*dir*/);
- static void setUserIniPath(const QString & /*dir*/);
- static void setPath(Format /*format*/, Scope /*scope*/, const QString & /*path*/);
+#endif
+ static void setDefaultFormat(QSettings::Format /*format*/);
+ static QSettings::Format defaultFormat() { return QSettings::NativeFormat; }
+ static void setPath(QSettings::Format /*format*/, Scope /*scope*/, const QString & /*path*/);
typedef QMap<QString, QVariant> SettingsMap;
typedef bool(*ReadFunc)(QIODevice &device, SettingsMap &map);
typedef bool(*WriteFunc)(QIODevice &device, const SettingsMap &map);
- static Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc,
+ static QSettings::Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc,
Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
{
Q_UNUSED(extension)
Q_UNUSED(readFunc)
Q_UNUSED(writeFunc)
Q_UNUSED(caseSensitivity)
- return NativeFormat;
+ return QSettings::NativeFormat;
}
private:
diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp
index f72e67867..a29e99f94 100644
--- a/src/libs/installer/qtpatch.cpp
+++ b/src/libs/installer/qtpatch.cpp
@@ -67,11 +67,11 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
QFileInfo qmake(qmakePath);
if (!qmake.exists()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
if (!qmake.isExecutable()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
@@ -88,7 +88,7 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
, QString::fromLatin1("Standard output: \"%1\".").arg(QLatin1String(output))
, QString::fromLatin1("Error output: \"%1\".").arg(QLatin1String(process.readAllStandardError()))
};
- qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')));
+ qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')).toUtf8());
return qmakeValueHash;
}
qmakeOutput->append(output);
diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp
index c1ee7a038..852714dfb 100644
--- a/src/libs/installer/registerfiletypeoperation.cpp
+++ b/src/libs/installer/registerfiletypeoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -105,7 +105,7 @@ bool RegisterFileTypeOperation::performOperation()
allUsers = true;
QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER")
- , QSettingsWrapper::NativeFormat);
+ , QSettings::NativeFormat);
const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId;
const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0));
@@ -153,7 +153,7 @@ bool RegisterFileTypeOperation::undoOperation()
{
#ifdef Q_OS_WIN
ensureOptionalArgumentsRead();
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
QStringList args = arguments();
@@ -166,7 +166,7 @@ bool RegisterFileTypeOperation::undoOperation()
allUsers = true;
QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER")
- , QSettingsWrapper::NativeFormat);
+ , QSettings::NativeFormat);
const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId;
const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0));
diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h
index e1809e0af..3dec97c95 100644
--- a/src/libs/installer/remoteclient_p.h
+++ b/src/libs/installer/remoteclient_p.h
@@ -40,8 +40,8 @@
#include <QCoreApplication>
#include <QDeadlineTimer>
+#include <QRecursiveMutex>
#include <QTimer>
-#include <QMutex>
#include <QThread>
namespace QInstaller {
@@ -55,7 +55,6 @@ public:
RemoteClientPrivate(RemoteClient *parent)
: RemoteObject(QLatin1String("RemoteClientPrivate"))
, q_ptr(parent)
- , m_mutex(QMutex::Recursive)
, m_startServerAs(Protocol::StartAs::User)
, m_serverStarted(false)
, m_active(false)
@@ -201,7 +200,7 @@ public:
private:
RemoteClient *q_ptr;
- QMutex m_mutex;
+ QRecursiveMutex m_mutex;
QString m_socketName;
Protocol::StartAs m_startServerAs;
bool m_serverStarted;
diff --git a/src/libs/installer/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp
index b600da618..2ead83861 100644
--- a/src/libs/installer/remotefileengine.cpp
+++ b/src/libs/installer/remotefileengine.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.
@@ -55,10 +55,10 @@ QAbstractFileEngine* RemoteFileEngineHandler::create(const QString &fileName) co
if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":")))
return 0; // empty filename or Qt resource
- QScopedPointer<RemoteFileEngine> client(new RemoteFileEngine());
+ std::unique_ptr<RemoteFileEngine> client(new RemoteFileEngine());
client->setFileName(fileName);
if (client->isConnectedToServer())
- return client.take();
+ return client.release();
return 0;
}
@@ -313,6 +313,7 @@ bool RemoteFileEngine::link(const QString &newName)
/*!
\reimp
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
{
if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
@@ -320,18 +321,38 @@ bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectorie
dirName, createParentDirectories);
}
return m_fileEngine.mkdir(dirName, createParentDirectories);
+
+}
+#else
+bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions) const
+{
+ if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
+ return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineMkdir),
+ dirName, createParentDirectories);
+ }
+ return m_fileEngine.mkdir(dirName, createParentDirectories, permissions);
}
+#endif
/*!
\reimp
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool RemoteFileEngine::open(QIODevice::OpenMode mode)
+#else
+bool RemoteFileEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions)
+#endif
{
if (connectToServer()) {
return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineOpen),
static_cast<qint32>(mode | QIODevice::Unbuffered));
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
return m_fileEngine.open(mode | QIODevice::Unbuffered);
+#else
+ return m_fileEngine.open(mode | QIODevice::Unbuffered, permissions);
+#endif
}
/*!
@@ -418,8 +439,7 @@ bool RemoteFileEngine::seek(qint64 offset)
void RemoteFileEngine::setFileName(const QString &fileName)
{
if (connectToServer()) {
- callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName,
- dummy);
+ callRemoteMethodDefaultReply(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName);
}
m_fileEngine.setFileName(fileName);
}
@@ -534,7 +554,11 @@ bool RemoteFileEngine::renameOverwrite(const QString &newName)
return m_fileEngine.renameOverwrite(newName);
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
QDateTime RemoteFileEngine::fileTime(FileTime time) const
+#else
+QDateTime RemoteFileEngine::fileTime(QFile::FileTime time) const
+#endif
{
if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
return callRemoteMethod<QDateTime>
diff --git a/src/libs/installer/remotefileengine.h b/src/libs/installer/remotefileengine.h
index 35ebf7742..c46e861ca 100644
--- a/src/libs/installer/remotefileengine.h
+++ b/src/libs/installer/remotefileengine.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.
@@ -53,7 +53,12 @@ public:
RemoteFileEngine();
~RemoteFileEngine();
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open(QIODevice::OpenMode mode) override;
+#else
+ bool open(QIODevice::OpenMode mode,
+ std::optional<QFile::Permissions> permissions = std::nullopt) override;
+#endif
bool close() override;
bool flush() override;
bool syncToDisk() override;
@@ -66,7 +71,12 @@ public:
bool rename(const QString &newName) override;
bool renameOverwrite(const QString &newName) override;
bool link(const QString &newName) override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+#else
+ bool mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions = std::nullopt) const override;
+#endif
bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
bool setSize(qint64 size) override;
bool caseSensitive() const override;
@@ -77,7 +87,11 @@ public:
QString fileName(FileName file = DefaultName) const override;
uint ownerId(FileOwner owner) const override;
QString owner(FileOwner owner) const override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
QDateTime fileTime(FileTime time) const override;
+#else
+ QDateTime fileTime(QFile::FileTime time) const override;
+#endif
void setFileName(const QString &fileName) override;
int handle() const override;
bool atEnd() const;
diff --git a/src/libs/installer/remoteobject.cpp b/src/libs/installer/remoteobject.cpp
index 54034eb65..b4dd0cbb7 100644
--- a/src/libs/installer/remoteobject.cpp
+++ b/src/libs/installer/remoteobject.cpp
@@ -46,7 +46,6 @@ namespace QInstaller {
RemoteObject::RemoteObject(const QString &wrappedType, QObject *parent)
: QObject(parent)
- , dummy(nullptr)
, m_type(wrappedType)
, m_socket(nullptr)
{
@@ -122,6 +121,11 @@ bool RemoteObject::connectToServer(const QVariantList &arguments)
sendPacket(m_socket, Protocol::Create, data);
m_socket->flush();
+ while (m_socket->bytesToWrite())
+ m_socket->waitForBytesWritten();
+
+ const QString reply = readData<QString>(QLatin1String(Protocol::Create));
+ Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
return true;
}
@@ -135,9 +139,4 @@ bool RemoteObject::isConnectedToServer() const
return false;
}
-void RemoteObject::callRemoteMethod(const QString &name)
-{
- writeData(name, dummy, dummy, dummy);
-}
-
} // namespace QInstaller
diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h
index ecd86c984..ddd512588 100644
--- a/src/libs/installer/remoteobject.h
+++ b/src/libs/installer/remoteobject.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,8 +35,10 @@
#include <QCoreApplication>
#include <QDataStream>
-#include <QObject>
#include <QLocalSocket>
+#include <QObject>
+#include <QVariant>
+
namespace QInstaller {
@@ -50,45 +52,65 @@ public:
virtual ~RemoteObject() = 0;
bool isConnectedToServer() const;
- void callRemoteMethod(const QString &name);
- template<typename T1, typename T2>
- void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2)
+ template<typename... Args>
+ void callRemoteMethodDefaultReply(const QString &name, const Args&... args)
{
- writeData(name, arg, arg2, dummy);
+ const QString reply = sendReceivePacket<QString>(name, args...);
+ Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
}
- template<typename T1, typename T2, typename T3>
- void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 & arg3)
+ template<typename T, typename... Args>
+ T callRemoteMethod(const QString &name, const Args&... args) const
{
- writeData(name, arg, arg2, arg3);
+ return sendReceivePacket<T>(name, args...);
}
- template<typename T>
- T callRemoteMethod(const QString &name) const
+protected:
+ bool authorize();
+ bool connectToServer(const QVariantList &arguments = QVariantList());
+
+private:
+
+ template<typename T, typename... Args>
+ T sendReceivePacket(const QString &name, const Args&... args) const
{
- return callRemoteMethod<T>(name, dummy, dummy, dummy);
+ writeData(name, args...);
+ while (m_socket->bytesToWrite())
+ m_socket->waitForBytesWritten();
+
+ return readData<T>(name);
}
- template<typename T, typename T1>
- T callRemoteMethod(const QString &name, const T1 &arg) const
+ template <class T> int writeObject(QDataStream& out, const T& t) const
{
- return callRemoteMethod<T>(name, arg, dummy, dummy);
+ static_assert(!std::is_pointer<T>::value, "Pointer passed to remote server");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ out << t;
+#else
+ if constexpr (std::is_same<T, QAnyStringView>::value)
+ out << t.toString();
+ else
+ out << t;
+#endif
+
+ return 0;
}
- template<typename T, typename T1, typename T2>
- T callRemoteMethod(const QString &name, const T1 & arg, const T2 &arg2) const
+ template<typename... Args>
+ void writeData(const QString &name, const Args&... args) const
{
- return callRemoteMethod<T>(name, arg, arg2, dummy);
+ QByteArray data;
+ QDataStream out(&data, QIODevice::WriteOnly);
+
+ (void)std::initializer_list<int>{writeObject(out, args)...};
+ sendPacket(m_socket, name.toLatin1(), data);
+ m_socket->flush();
}
- template<typename T, typename T1, typename T2, typename T3>
- T callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
+ template<typename T>
+ T readData(const QString &name) const
{
- writeData(name, arg, arg2, arg3);
- while (m_socket->bytesToWrite())
- m_socket->waitForBytesWritten();
-
QByteArray command;
QByteArray data;
while (!receivePacket(m_socket, &command, &data)) {
@@ -110,46 +132,6 @@ public:
return result;
}
-protected:
- bool authorize();
- bool connectToServer(const QVariantList &arguments = QVariantList());
-
- // Use this structure to allow derived classes to manipulate the template
- // function signature of the callRemoteMethod templates, since most of the
- // generated functions will differ in return type rather given arguments.
- struct Dummy {}; Dummy *dummy;
-
-private:
- template<typename T> bool isValueType(T) const
- {
- return true;
- }
-
- template<typename T> bool isValueType(T *dummy) const
- {
- // Force compiler error while passing anything different then Dummy* to the function.
- // It really doesn't make sense to send any pointer over to the server, so bail early.
- Q_UNUSED(static_cast<Dummy*> (dummy))
- return false;
- }
-
- template<typename T1, typename T2, typename T3>
- void writeData(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
- {
- QByteArray data;
- QDataStream out(&data, QIODevice::WriteOnly);
-
- if (isValueType(arg))
- out << arg;
- if (isValueType(arg2))
- out << arg2;
- if (isValueType(arg3))
- out << arg3;
-
- sendPacket(m_socket, name.toLatin1(), data);
- m_socket->flush();
- }
-
private:
QString m_type;
QLocalSocket *m_socket;
diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp
index 2464c8469..55c4f48d9 100644
--- a/src/libs/installer/remoteserverconnection.cpp
+++ b/src/libs/installer/remoteserverconnection.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.
@@ -46,13 +46,62 @@ namespace QInstaller {
/*!
\inmodule QtInstallerFramework
- \class QInstaller::RemoteServerConnection
+ \class QInstaller::QProcessSignalReceiver
\internal
*/
/*!
\inmodule QtInstallerFramework
- \class QInstaller::QProcessSignalReceiver
+ \class QInstaller::RemoteServerReply
+ \internal
+ \brief The RemoteServerReply class is used for sending a reply to a local socket.
+
+ The class ensures exactly one reply is sent in the lifetime of an object
+ instantiated from this class. If the calling client has not explicitly sent
+ a reply, a default reply is automatically sent on destruction.
+*/
+
+/*!
+ Constructs reply object for \a socket.
+*/
+RemoteServerReply::RemoteServerReply(QLocalSocket *socket)
+ : m_socket(socket)
+ , m_sent(false)
+{}
+
+/*!
+ Destroys the object and sends the default reply, if
+ no other replies have been sent.
+*/
+RemoteServerReply::~RemoteServerReply()
+{
+ send(QString::fromLatin1(Protocol::DefaultReply));
+}
+
+/*!
+ \fn template <typename T> QInstaller::RemoteServerReply::send(const T &data)
+
+ Sends a reply packet with \a¸ data to the socket. If a reply
+ has been already sent, this function does not send another.
+*/
+template <typename T>
+void RemoteServerReply::send(const T &data)
+{
+ if (m_sent || m_socket->state() != QLocalSocket::ConnectedState)
+ return;
+
+ QByteArray result;
+ QDataStream returnStream(&result, QIODevice::WriteOnly);
+ returnStream << data;
+
+ sendPacket(m_socket, Protocol::Reply, result);
+ m_socket->flush();
+ m_sent = true;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteServerConnection
\internal
*/
@@ -106,18 +155,18 @@ void RemoteServerConnection::run()
stream.setDevice(&buf);
StreamChecker streamChecker(&stream);
+ RemoteServerReply reply(&socket);
+
if (authorized && command == QLatin1String(Protocol::Shutdown)) {
authorized = false;
- sendData(&socket, true);
- socket.flush();
+ reply.send(true);
socket.close();
emit shutdownRequested();
return;
} else if (command == QLatin1String(Protocol::Authorize)) {
QString key;
stream >> key;
- sendData(&socket, (authorized = (key == m_authorizationKey)));
- socket.flush();
+ reply.send(authorized = (key == m_authorizationKey));
if (!authorized) {
socket.close();
return;
@@ -157,6 +206,8 @@ void RemoteServerConnection::run()
#else
Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
#endif
+ } else {
+ qCDebug(QInstaller::lcServer) << "Unknown type for Create command:" << type;
}
continue;
}
@@ -164,8 +215,7 @@ void RemoteServerConnection::run()
if (command == QLatin1String(Protocol::GetQProcessSignals)) {
if (m_processSignalReceiver) {
QMutexLocker _(&m_processSignalReceiver->m_lock);
- sendData(&socket, m_processSignalReceiver->m_receivedSignals);
- socket.flush();
+ reply.send(m_processSignalReceiver->m_receivedSignals);
m_processSignalReceiver->m_receivedSignals.clear();
}
continue;
@@ -173,8 +223,7 @@ void RemoteServerConnection::run()
#ifdef IFW_LIBARCHIVE
if (m_archiveSignalReceiver) {
QMutexLocker _(&m_archiveSignalReceiver->m_lock);
- sendData(&socket, m_archiveSignalReceiver->m_receivedSignals);
- socket.flush();
+ reply.send(m_archiveSignalReceiver->m_receivedSignals);
m_archiveSignalReceiver->m_receivedSignals.clear();
}
continue;
@@ -184,52 +233,41 @@ void RemoteServerConnection::run()
}
if (command.startsWith(QLatin1String(Protocol::QProcess))) {
- handleQProcess(&socket, command, stream);
+ handleQProcess(&reply, command, stream);
} else if (command.startsWith(QLatin1String(Protocol::QSettings))) {
- handleQSettings(&socket, command, stream, settings.data());
+ handleQSettings(&reply, command, stream, settings.data());
} else if (command.startsWith(QLatin1String(Protocol::QAbstractFileEngine))) {
- handleQFSFileEngine(&socket, command, stream);
+ handleQFSFileEngine(&reply, command, stream);
} else if (command.startsWith(QLatin1String(Protocol::AbstractArchive))) {
- handleArchive(&socket, command, stream);
+ handleArchive(&reply, command, stream);
} else {
qCDebug(QInstaller::lcServer) << "Unknown command:" << command;
}
- socket.flush();
} else {
// authorization failed, connection not wanted
socket.close();
- qCDebug(QInstaller::lcServer) << "Unknown command:" << command;
+ qCDebug(QInstaller::lcServer) << "Authorization failed.";
return;
}
}
}
-template <typename T>
-void RemoteServerConnection::sendData(QIODevice *device, const T &data)
-{
- QByteArray result;
- QDataStream returnStream(&result, QIODevice::WriteOnly);
- returnStream << data;
-
- sendPacket(device, Protocol::Reply, result);
-}
-
-void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &command, QDataStream &data)
+void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QString &command, QDataStream &data)
{
if (command == QLatin1String(Protocol::QProcessCloseWriteChannel)) {
m_process->closeWriteChannel();
} else if (command == QLatin1String(Protocol::QProcessExitCode)) {
- sendData(socket, m_process->exitCode());
+ reply->send(m_process->exitCode());
} else if (command == QLatin1String(Protocol::QProcessExitStatus)) {
- sendData(socket, static_cast<qint32> (m_process->exitStatus()));
+ reply->send(static_cast<qint32> (m_process->exitStatus()));
} else if (command == QLatin1String(Protocol::QProcessKill)) {
m_process->kill();
} else if (command == QLatin1String(Protocol::QProcessReadAll)) {
- sendData(socket, m_process->readAll());
+ reply->send(m_process->readAll());
} else if (command == QLatin1String(Protocol::QProcessReadAllStandardOutput)) {
- sendData(socket, m_process->readAllStandardOutput());
+ reply->send(m_process->readAllStandardOutput());
} else if (command == QLatin1String(Protocol::QProcessReadAllStandardError)) {
- sendData(socket, m_process->readAllStandardError());
+ reply->send(m_process->readAllStandardError());
} else if (command == QLatin1String(Protocol::QProcessStartDetached)) {
QString program;
QStringList arguments;
@@ -240,7 +278,18 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
qint64 pid = -1;
bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid);
- sendData(socket, qMakePair< bool, qint64>(success, pid));
+ reply->send(QPair<bool, qint64>(success, pid));
+ } else if (command == QLatin1String(Protocol::QProcessStartDetached2)) {
+ QString program;
+ QStringList arguments;
+ QString workingDirectory;
+ data >> program;
+ data >> arguments;
+ data >> workingDirectory;
+
+ qint64 pid = -1;
+ bool success = QProcess::startDetached(program, arguments, workingDirectory, &pid);
+ reply->send(QPair<bool, qint64>(success, pid));
} else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) {
QString dir;
data >> dir;
@@ -250,7 +299,7 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
data >> env;
m_process->setEnvironment(env);
} else if (command == QLatin1String(Protocol::QProcessEnvironment)) {
- sendData(socket, m_process->environment());
+ reply->send(m_process->environment());
} else if (command == QLatin1String(Protocol::QProcessStart3Arg)) {
QString program;
QStringList arguments;
@@ -264,25 +313,25 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
qint32 mode;
data >> program;
data >> mode;
- m_process->start(program, static_cast<QIODevice::OpenMode> (mode));
+ m_process->start(program, {}, static_cast<QIODevice::OpenMode> (mode));
} else if (command == QLatin1String(Protocol::QProcessState)) {
- sendData(socket, static_cast<qint32> (m_process->state()));
+ reply->send(static_cast<qint32> (m_process->state()));
} else if (command == QLatin1String(Protocol::QProcessTerminate)) {
m_process->terminate();
} else if (command == QLatin1String(Protocol::QProcessWaitForFinished)) {
qint32 msecs;
data >> msecs;
- sendData(socket, m_process->waitForFinished(msecs));
+ reply->send(m_process->waitForFinished(msecs));
} else if (command == QLatin1String(Protocol::QProcessWaitForStarted)) {
qint32 msecs;
data >> msecs;
- sendData(socket, m_process->waitForStarted(msecs));
+ reply->send(m_process->waitForStarted(msecs));
} else if (command == QLatin1String(Protocol::QProcessWorkingDirectory)) {
- sendData(socket, m_process->workingDirectory());
+ reply->send(m_process->workingDirectory());
} else if (command == QLatin1String(Protocol::QProcessErrorString)) {
- sendData(socket, m_process->errorString());
+ reply->send(m_process->errorString());
} else if (command == QLatin1String(Protocol::QProcessReadChannel)) {
- sendData(socket, static_cast<qint32> (m_process->readChannel()));
+ reply->send(static_cast<qint32> (m_process->readChannel()));
} else if (command == QLatin1String(Protocol::QProcessSetReadChannel)) {
qint32 processChannel;
data >> processChannel;
@@ -290,9 +339,9 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
} else if (command == QLatin1String(Protocol::QProcessWrite)) {
QByteArray byteArray;
data >> byteArray;
- sendData(socket, m_process->write(byteArray));
+ reply->send(m_process->write(byteArray));
} else if (command == QLatin1String(Protocol::QProcessProcessChannelMode)) {
- sendData(socket, static_cast<qint32> (m_process->processChannelMode()));
+ reply->send(static_cast<qint32> (m_process->processChannelMode()));
} else if (command == QLatin1String(Protocol::QProcessSetProcessChannelMode)) {
qint32 processChannel;
data >> processChannel;
@@ -310,14 +359,14 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
}
}
-void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &command,
+void RemoteServerConnection::handleQSettings(RemoteServerReply *reply, const QString &command,
QDataStream &data, PermissionSettings *settings)
{
if (!settings)
return;
if (command == QLatin1String(Protocol::QSettingsAllKeys)) {
- sendData(socket, settings->allKeys());
+ reply->send(settings->allKeys());
} else if (command == QLatin1String(Protocol::QSettingsBeginGroup)) {
QString prefix;
data >> prefix;
@@ -331,29 +380,29 @@ void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &c
} else if (command == QLatin1String(Protocol::QSettingsBeginReadArray)) {
QString prefix;
data >> prefix;
- sendData(socket, settings->beginReadArray(prefix));
+ reply->send(settings->beginReadArray(prefix));
} else if (command == QLatin1String(Protocol::QSettingsChildGroups)) {
- sendData(socket, settings->childGroups());
+ reply->send(settings->childGroups());
} else if (command == QLatin1String(Protocol::QSettingsChildKeys)) {
- sendData(socket, settings->childKeys());
+ reply->send(settings->childKeys());
} else if (command == QLatin1String(Protocol::QSettingsClear)) {
settings->clear();
} else if (command == QLatin1String(Protocol::QSettingsContains)) {
QString key;
data >> key;
- sendData(socket, settings->contains(key));
+ reply->send(settings->contains(key));
} else if (command == QLatin1String(Protocol::QSettingsEndArray)) {
settings->endArray();
} else if (command == QLatin1String(Protocol::QSettingsEndGroup)) {
settings->endGroup();
} else if (command == QLatin1String(Protocol::QSettingsFallbacksEnabled)) {
- sendData(socket, settings->fallbacksEnabled());
+ reply->send(settings->fallbacksEnabled());
} else if (command == QLatin1String(Protocol::QSettingsFileName)) {
- sendData(socket, settings->fileName());
+ reply->send(settings->fileName());
} else if (command == QLatin1String(Protocol::QSettingsGroup)) {
- sendData(socket, settings->group());
+ reply->send(settings->group());
} else if (command == QLatin1String(Protocol::QSettingsIsWritable)) {
- sendData(socket, settings->isWritable());
+ reply->send(settings->isWritable());
} else if (command == QLatin1String(Protocol::QSettingsRemove)) {
QString key;
data >> key;
@@ -367,7 +416,7 @@ void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &c
data >> b;
settings->setFallbacksEnabled(b);
} else if (command == QLatin1String(Protocol::QSettingsStatus)) {
- sendData(socket, settings->status());
+ reply->send(settings->status());
} else if (command == QLatin1String(Protocol::QSettingsSync)) {
settings->sync();
} else if (command == QLatin1String(Protocol::QSettingsSetValue)) {
@@ -380,26 +429,27 @@ void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &c
QString key;
QVariant defaultValue;
data >> key;
- data >> defaultValue;
- sendData(socket, settings->value(key, defaultValue));
+ if (!data.atEnd())
+ data >> defaultValue;
+ reply->send(settings->value(key, defaultValue));
} else if (command == QLatin1String(Protocol::QSettingsOrganizationName)) {
- sendData(socket, settings->organizationName());
+ reply->send(settings->organizationName());
} else if (command == QLatin1String(Protocol::QSettingsApplicationName)) {
- sendData(socket, settings->applicationName());
+ reply->send(settings->applicationName());
} else if (!command.isEmpty()) {
qCDebug(QInstaller::lcServer) << "Unknown QSettings command:" << command;
}
}
-void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QString &command,
+void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const QString &command,
QDataStream &data)
{
if (command == QLatin1String(Protocol::QAbstractFileEngineAtEnd)) {
- sendData(socket, m_engine->atEnd());
+ reply->send(m_engine->atEnd());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineCaseSensitive)) {
- sendData(socket, m_engine->caseSensitive());
+ reply->send(m_engine->caseSensitive());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineClose)) {
- sendData(socket, m_engine->close());
+ reply->send(m_engine->close());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineCopy)) {
QString newName;
data >>newName;
@@ -407,90 +457,99 @@ void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QStrin
// QFileSystemEngine::copyFile() is currently unimplemented on Linux,
// copy using QFile instead of directly with QFSFileEngine.
QFile file(m_engine->fileName(QAbstractFileEngine::AbsoluteName));
- sendData(socket, file.copy(newName));
+ reply->send(file.copy(newName));
#else
- sendData(socket, m_engine->copy(newName));
+ reply->send(m_engine->copy(newName));
#endif
} else if (command == QLatin1String(Protocol::QAbstractFileEngineEntryList)) {
qint32 filters;
QStringList filterNames;
data >>filters;
data >>filterNames;
- sendData(socket, m_engine->entryList(static_cast<QDir::Filters> (filters), filterNames));
+ reply->send(m_engine->entryList(static_cast<QDir::Filters> (filters), filterNames));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineError)) {
- sendData(socket, static_cast<qint32> (m_engine->error()));
+ reply->send(static_cast<qint32> (m_engine->error()));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineErrorString)) {
- sendData(socket, m_engine->errorString());
+ reply->send(m_engine->errorString());
}
else if (command == QLatin1String(Protocol::QAbstractFileEngineFileFlags)) {
qint32 flags;
data >>flags;
flags = m_engine->fileFlags(static_cast<QAbstractFileEngine::FileFlags>(flags));
- sendData(socket, static_cast<qint32>(flags));
+ reply->send(static_cast<qint32>(flags));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFileName)) {
qint32 file;
data >>file;
- sendData(socket, m_engine->fileName(static_cast<QAbstractFileEngine::FileName> (file)));
+ reply->send(m_engine->fileName(static_cast<QAbstractFileEngine::FileName> (file)));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFlush)) {
- sendData(socket, m_engine->flush());
+ reply->send(m_engine->flush());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineHandle)) {
- sendData(socket, m_engine->handle());
+ reply->send(m_engine->handle());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineIsRelativePath)) {
- sendData(socket, m_engine->isRelativePath());
+ reply->send(m_engine->isRelativePath());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineIsSequential)) {
- sendData(socket, m_engine->isSequential());
+ reply->send(m_engine->isSequential());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineLink)) {
QString newName;
data >>newName;
- sendData(socket, m_engine->link(newName));
+ reply->send(m_engine->link(newName));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineMkdir)) {
QString dirName;
bool createParentDirectories;
data >>dirName;
data >>createParentDirectories;
- sendData(socket, m_engine->mkdir(dirName, createParentDirectories));
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ reply->send(m_engine->mkdir(dirName, createParentDirectories));
+#else
+ reply->send(m_engine->mkdir(dirName, createParentDirectories, std::nullopt));
+#endif
+
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOpen)) {
qint32 openMode;
data >>openMode;
- sendData(socket, m_engine->open(static_cast<QIODevice::OpenMode> (openMode)));
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode)));
+#else
+ reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode), std::nullopt));
+#endif
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOwner)) {
qint32 owner;
data >>owner;
- sendData(socket, m_engine->owner(static_cast<QAbstractFileEngine::FileOwner> (owner)));
+ reply->send(m_engine->owner(static_cast<QAbstractFileEngine::FileOwner> (owner)));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOwnerId)) {
qint32 owner;
data >>owner;
- sendData(socket, m_engine->ownerId(static_cast<QAbstractFileEngine::FileOwner> (owner)));
+ reply->send(m_engine->ownerId(static_cast<QAbstractFileEngine::FileOwner> (owner)));
} else if (command == QLatin1String(Protocol::QAbstractFileEnginePos)) {
- sendData(socket, m_engine->pos());
+ reply->send(m_engine->pos());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRead)) {
qint64 maxlen;
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->read(byteArray.data(), maxlen);
- sendData(socket, qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineReadLine)) {
qint64 maxlen;
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->readLine(byteArray.data(), maxlen);
- sendData(socket, qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRemove)) {
- sendData(socket, m_engine->remove());
+ reply->send(m_engine->remove());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRename)) {
QString newName;
data >>newName;
- sendData(socket, m_engine->rename(newName));
+ reply->send(m_engine->rename(newName));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRmdir)) {
QString dirName;
bool recurseParentDirectories;
data >>dirName;
data >>recurseParentDirectories;
- sendData(socket, m_engine->rmdir(dirName, recurseParentDirectories));
+ reply->send(m_engine->rmdir(dirName, recurseParentDirectories));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSeek)) {
quint64 offset;
data >>offset;
- sendData(socket, m_engine->seek(offset));
+ reply->send(m_engine->seek(offset));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSetFileName)) {
QString fileName;
data >>fileName;
@@ -498,43 +557,47 @@ void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QStrin
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSetPermissions)) {
uint perms;
data >>perms;
- sendData(socket, m_engine->setPermissions(perms));
+ reply->send(m_engine->setPermissions(perms));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSetSize)) {
qint64 size;
data >>size;
- sendData(socket, m_engine->setSize(size));
+ reply->send(m_engine->setSize(size));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSize)) {
- sendData(socket, m_engine->size());
+ reply->send(m_engine->size());
} else if ((command == QLatin1String(Protocol::QAbstractFileEngineSupportsExtension))
|| (command == QLatin1String(Protocol::QAbstractFileEngineExtension))) {
// Implemented client side.
} else if (command == QLatin1String(Protocol::QAbstractFileEngineWrite)) {
QByteArray content;
data >> content;
- sendData(socket, m_engine->write(content.data(), content.size()));
+ reply->send(m_engine->write(content.data(), content.size()));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSyncToDisk)) {
- sendData(socket, m_engine->syncToDisk());
+ reply->send(m_engine->syncToDisk());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRenameOverwrite)) {
QString newFilename;
data >> newFilename;
- sendData(socket, m_engine->renameOverwrite(newFilename));
+ reply->send(m_engine->renameOverwrite(newFilename));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFileTime)) {
qint32 filetime;
data >> filetime;
- sendData(socket, m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime)));
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
+ reply->send(m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime)));
+#else
+ reply->send(m_engine->fileTime(static_cast<QFile::FileTime> (filetime)));
+#endif
} else if (!command.isEmpty()) {
qCDebug(QInstaller::lcServer) << "Unknown QAbstractFileEngine command:" << command;
}
}
-void RemoteServerConnection::handleArchive(QIODevice *socket, const QString &command, QDataStream &data)
+void RemoteServerConnection::handleArchive(RemoteServerReply *reply, const QString &command, QDataStream &data)
{
#ifdef IFW_LIBARCHIVE
LibArchiveArchive *archive = static_cast<LibArchiveArchive *>(m_archive.get());
if (command == QLatin1String(Protocol::AbstractArchiveOpen)) {
qint32 openMode;
data >> openMode;
- sendData(socket, archive->open(static_cast<QIODevice::OpenMode>(openMode)));
+ reply->send(archive->open(static_cast<QIODevice::OpenMode>(openMode)));
} else if (command == QLatin1String(Protocol::AbstractArchiveClose)) {
archive->close();
} else if (command == QLatin1String(Protocol::AbstractArchiveSetFilename)) {
@@ -542,7 +605,7 @@ void RemoteServerConnection::handleArchive(QIODevice *socket, const QString &com
data >> fileName;
archive->setFilename(fileName);
} else if (command == QLatin1String(Protocol::AbstractArchiveErrorString)) {
- sendData(socket, archive->errorString());
+ reply->send(archive->errorString());
} else if (command == QLatin1String(Protocol::AbstractArchiveExtract)) {
QString dirPath;
quint64 total;
@@ -552,11 +615,11 @@ void RemoteServerConnection::handleArchive(QIODevice *socket, const QString &com
} else if (command == QLatin1String(Protocol::AbstractArchiveCreate)) {
QStringList entries;
data >> entries;
- sendData(socket, archive->create(entries));
+ reply->send(archive->create(entries));
} else if (command == QLatin1String(Protocol::AbstractArchiveList)) {
- sendData(socket, archive->list());
+ reply->send(archive->list());
} else if (command == QLatin1String(Protocol::AbstractArchiveIsSupported)) {
- sendData(socket, archive->isSupported());
+ reply->send(archive->isSupported());
} else if (command == QLatin1String(Protocol::AbstractArchiveSetCompressionLevel)) {
qint32 level;
data >> level;
@@ -572,7 +635,7 @@ void RemoteServerConnection::handleArchive(QIODevice *socket, const QString &com
data >> pos;
archive->workerSetFilePosition(pos);
} else if (command == QLatin1String(Protocol::AbstractArchiveWorkerStatus)) {
- sendData(socket, static_cast<qint32>(archive->workerStatus()));
+ reply->send(static_cast<qint32>(archive->workerStatus()));
} else if (command == QLatin1String(Protocol::AbstractArchiveCancel)) {
archive->workerCancel();
} else if (!command.isEmpty()) {
diff --git a/src/libs/installer/remoteserverconnection.h b/src/libs/installer/remoteserverconnection.h
index 1fd179430..53f6a412b 100644
--- a/src/libs/installer/remoteserverconnection.h
+++ b/src/libs/installer/remoteserverconnection.h
@@ -38,7 +38,7 @@
#include <QtCore/private/qfsfileengine_p.h>
QT_BEGIN_NAMESPACE
-class QIODevice;
+class QLocalSocket;
QT_END_NAMESPACE
namespace QInstaller {
@@ -48,6 +48,20 @@ class PermissionSettings;
class QProcessSignalReceiver;
class AbstractArchiveSignalReceiver;
+class RemoteServerReply
+{
+public:
+ explicit RemoteServerReply(QLocalSocket *socket);
+ ~RemoteServerReply();
+
+ template <typename T>
+ void send(const T &data);
+
+private:
+ QLocalSocket *m_socket;
+ bool m_sent;
+};
+
class RemoteServerConnection : public QThread
{
Q_OBJECT
@@ -63,13 +77,11 @@ signals:
void shutdownRequested();
private:
- template <typename T>
- void sendData(QIODevice *device, const T &arg);
- void handleQProcess(QIODevice *device, const QString &command, QDataStream &data);
- void handleQSettings(QIODevice *device, const QString &command, QDataStream &data,
+ void handleQProcess(RemoteServerReply *reply, const QString &command, QDataStream &data);
+ void handleQSettings(RemoteServerReply *reply, const QString &command, QDataStream &data,
PermissionSettings *settings);
- void handleQFSFileEngine(QIODevice *device, const QString &command, QDataStream &data);
- void handleArchive(QIODevice *device, const QString &command, QDataStream &data);
+ void handleQFSFileEngine(RemoteServerReply *reply, const QString &command, QDataStream &data);
+ void handleArchive(RemoteServerReply *reply, const QString &command, QDataStream &data);
private:
qintptr m_socketDescriptor;
diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h
index 977a64711..09e6de7d7 100644
--- a/src/libs/installer/remoteserverconnection_p.h
+++ b/src/libs/installer/remoteserverconnection_p.h
@@ -52,7 +52,7 @@ private:
connect(process, &QIODevice::bytesWritten, this, &QProcessSignalReceiver::onBytesWritten);
connect(process, &QIODevice::aboutToClose, this, &QProcessSignalReceiver::onAboutToClose);
connect(process, &QIODevice::readChannelFinished, this, &QProcessSignalReceiver::onReadChannelFinished);
- connect(process, SIGNAL(error(QProcess::ProcessError)),
+ connect(process, SIGNAL(errorOccurred(QProcess::ProcessError)),
SLOT(onError(QProcess::ProcessError)));
connect(process, &QProcess::readyReadStandardOutput,
this, &QProcessSignalReceiver::onReadyReadStandardOutput);
diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp
index 50f3eceb6..f7035e732 100644
--- a/src/libs/installer/repository.cpp
+++ b/src/libs/installer/repository.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,7 +35,7 @@
#include <QDir>
/*!
- \fn inline uint QInstaller::qHash(const Repository &repository)
+ \fn inline hashValue QInstaller::qHash(const Repository &repository)
Returns a hash of the \a repository.
*/
@@ -49,8 +49,8 @@ Repository::Repository()
: m_default(false)
, m_enabled(false)
, m_compressed(false)
+ , m_postLoadComponentScript(false)
{
- registerMetaType();
}
/*!
@@ -63,10 +63,11 @@ Repository::Repository(const Repository &other)
, m_username(other.m_username)
, m_password(other.m_password)
, m_displayname(other.m_displayname)
- , m_compressed(other.m_compressed)
, m_categoryname(other.m_categoryname)
+ , m_compressed(other.m_compressed)
+ , m_xmlChecksum(other.m_xmlChecksum)
+ , m_postLoadComponentScript(other.m_postLoadComponentScript)
{
- registerMetaType();
}
/*!
@@ -78,8 +79,8 @@ Repository::Repository(const QUrl &url, bool isDefault, bool compressed)
, m_default(isDefault)
, m_enabled(true)
, m_compressed(compressed)
+ , m_postLoadComponentScript(false)
{
- registerMetaType();
}
/*!
@@ -224,6 +225,28 @@ void Repository::setCategoryName(const QString &categoryname)
}
/*!
+ Returns the expected checksum of the repository, which is the checksum
+ calculated from the \c Updates.xml document at the root of the repository.
+
+ This value is used as a hint when looking for already fetched repositories
+ from the local cache. If the installer has cached a repository with a matching
+ checksum, it can skip downloading the \c Updates.xml file for that repository again.
+*/
+QByteArray Repository::xmlChecksum() const
+{
+ return m_xmlChecksum;
+}
+
+/*!
+ Sets the expected checksum of the repository to \c checksum. The checksum
+ is calculated from the \c Updates.xml document at the root of the repository.
+*/
+void Repository::setXmlChecksum(const QByteArray &checksum)
+{
+ m_xmlChecksum = checksum;
+}
+
+/*!
Returns true if repository is compressed
*/
bool Repository::isCompressed() const
@@ -232,13 +255,31 @@ bool Repository::isCompressed() const
}
/*!
+ \internal
+*/
+bool Repository::postLoadComponentScript() const
+{
+ return m_postLoadComponentScript;
+}
+
+/*!
+ \internal
+*/
+void Repository::setPostLoadComponentScript(const bool postLoad)
+{
+ m_postLoadComponentScript = postLoad;
+}
+
+/*!
Compares the values of this repository to \a other and returns true if they are equal (same server,
default state, enabled state as well as username and password). \sa operator!=()
*/
bool Repository::operator==(const Repository &other) const
{
return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled
- && m_username == other.m_username && m_password == other.m_password && m_displayname == other.m_displayname;
+ && m_username == other.m_username && m_password == other.m_password
+ && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum
+ && m_postLoadComponentScript == other.m_postLoadComponentScript;
}
/*!
@@ -266,6 +307,8 @@ const Repository &Repository::operator=(const Repository &other)
m_displayname = other.m_displayname;
m_compressed = other.m_compressed;
m_categoryname = other.m_categoryname;
+ m_xmlChecksum = other.m_xmlChecksum;
+ m_postLoadComponentScript = other.m_postLoadComponentScript;
return *this;
}
@@ -273,7 +316,9 @@ const Repository &Repository::operator=(const Repository &other)
void Repository::registerMetaType()
{
qRegisterMetaType<Repository>("Repository");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<Repository>("Repository");
+#endif
}
/*!
@@ -283,7 +328,7 @@ QDataStream &operator>>(QDataStream &istream, Repository &repository)
{
QByteArray url, username, password, displayname, compressed;
istream >> url >> repository.m_default >> repository.m_enabled >> username >> password
- >> displayname >> repository.m_categoryname;
+ >> displayname >> repository.m_categoryname >> repository.m_xmlChecksum >> repository.m_postLoadComponentScript;
repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url)));
repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username)));
repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password)));
@@ -298,7 +343,8 @@ QDataStream &operator<<(QDataStream &ostream, const Repository &repository)
{
return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled
<< repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64()
- << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64();
+ << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64()
+ << repository.m_xmlChecksum.toBase64() << repository.m_postLoadComponentScript;
}
}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
index 3f28e4d99..0a589a43e 100644
--- a/src/libs/installer/repository.h
+++ b/src/libs/installer/repository.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -67,11 +67,17 @@ public:
QString categoryname() const;
void setCategoryName(const QString &categoryname);
+ QByteArray xmlChecksum() const;
+ void setXmlChecksum(const QByteArray &checksum);
+
bool isCompressed() const;
+ bool postLoadComponentScript() const;
+ void setPostLoadComponentScript(const bool postLoad);
+
bool operator==(const Repository &other) const;
bool operator!=(const Repository &other) const;
- uint qHash(const Repository &repository);
+ hashValue qHash(const Repository &repository);
const Repository &operator=(const Repository &other);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, Repository &repository);
@@ -86,9 +92,11 @@ private:
QString m_displayname;
QString m_categoryname;
bool m_compressed;
+ QByteArray m_xmlChecksum;
+ bool m_postLoadComponentScript;
};
-inline uint qHash(const Repository &repository)
+inline hashValue qHash(const Repository &repository)
{
return qHash(repository.url());
}
diff --git a/src/libs/installer/repositorycategory.cpp b/src/libs/installer/repositorycategory.cpp
index e651b7f0c..c9bee6e3a 100644
--- a/src/libs/installer/repositorycategory.cpp
+++ b/src/libs/installer/repositorycategory.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,7 +35,7 @@
#include <QStringList>
/*!
- \fn inline uint QInstaller::qHash(const RepositoryCategory &repository)
+ \fn inline hashValue QInstaller::qHash(const RepositoryCategory &repository)
Returns a hash of the repository category \a repository.
*/
@@ -65,8 +65,8 @@ RepositoryCategory::RepositoryCategory()
Constructs a new category by using all fields of the given category \a other.
*/
RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
- : m_displayname(other.m_displayname), m_data(other.m_data), m_enabled(other.m_enabled),
- m_tooltip(other.m_tooltip)
+ : m_data(other.m_data), m_displayname(other.m_displayname), m_tooltip(other.m_tooltip),
+ m_enabled(other.m_enabled)
{
registerMetaType();
}
@@ -75,7 +75,9 @@ RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
void RepositoryCategory::registerMetaType()
{
qRegisterMetaType<RepositoryCategory>("RepositoryCategory");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<RepositoryCategory>("RepositoryCategory");
+#endif
}
/*!
diff --git a/src/libs/installer/repositorycategory.h b/src/libs/installer/repositorycategory.h
index dc45527eb..993ae78aa 100644
--- a/src/libs/installer/repositorycategory.h
+++ b/src/libs/installer/repositorycategory.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,7 @@
#include "installer_global.h"
#include "repository.h"
+#include "qinstallerglobal.h"
#include <QtCore/QMetaType>
#include <QtCore/QUrl>
@@ -63,7 +64,7 @@ public:
bool operator==(const RepositoryCategory &other) const;
bool operator!=(const RepositoryCategory &other) const;
- uint qHash(const RepositoryCategory &repository);
+ hashValue qHash(const RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository);
@@ -75,7 +76,7 @@ private:
bool m_enabled;
};
-inline uint qHash(const RepositoryCategory &repository)
+inline hashValue qHash(const RepositoryCategory &repository)
{
return qHash(repository.displayname());
}
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/runextensions.h b/src/libs/installer/runextensions.h
index e39ae6c81..9c7147141 100644
--- a/src/libs/installer/runextensions.h
+++ b/src/libs/installer/runextensions.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,384 +37,70 @@ QT_BEGIN_NAMESPACE
namespace QtConcurrent {
-template <typename T, typename FunctionPointer>
-class StoredInterfaceFunctionCall0 : public QRunnable
+template <typename T, typename FunctionPointer, typename... Args>
+class StoredInterfaceFunctionCall : public QRunnable
{
public:
- StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &))
- : fn(fn) { }
+ StoredInterfaceFunctionCall(void (fn)(QFutureInterface<T> &, Args...), const Args&&... args)
+ : m_fn(fn), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
void run() override
{
- fn(futureInterface);
- futureInterface.reportFinished();
+ fn(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
-
-};
-template <typename T, typename FunctionPointer, typename Class>
-class StoredInterfaceMemberFunctionCall0 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object)
- : fn(fn), object(object) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
-
-};
-
-template <typename T, typename FunctionPointer, typename Arg1>
-class StoredInterfaceFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
- : fn(fn), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1>
-class StoredInterfaceMemberFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1)
- : fn(fn), object(object), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2>
-class StoredInterfaceFunctionCall2 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), arg1(arg1), arg2(arg2) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2;
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2>
-class StoredInterfaceMemberFunctionCall2 : public QRunnable
+template <typename T, typename FunctionPointer, typename Class, typename... Args>
+class StoredInterfaceMemberFunctionCall : public QRunnable
{
public:
- StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), object(object), arg1(arg1), arg2(arg2) { }
+ StoredInterfaceMemberFunctionCall(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, const Args&&... args)
+ : m_fn(fn), m_object(object), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
void run() override
{
- (object->*fn)(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
+ (m_object->*m_fn)(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2;
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ Class *m_object;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceFunctionCall3 : public QRunnable
+template <typename T, typename... Args>
+QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Args...), Args... args)
{
-public:
- StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceMemberFunctionCall3 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceMemberFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceMemberFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-
-template <typename T>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &))
-{
- return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start();
-}
-template <typename T, typename Arg1>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
-{
- return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start();
-}
-template <typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start();
+ return (new StoredInterfaceFunctionCall<T, void (*)(QFutureInterface<T> &, Args...), Args...>(functionPointer, args...))->start();
}
-template <typename Class, typename T>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object)
+template <typename Class, typename T, typename... Args>
+QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, Args... args)
{
- return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start();
+ return (new StoredInterfaceMemberFunctionCall<T, void (Class::*)(QFutureInterface<T> &, Args...), Class, Args...>(fn, object, args...))->start();
}
-template <typename Class, typename T, typename Arg1>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1)
-{
- return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start();
-}
} // namespace QtConcurrent
QT_END_NAMESPACE
diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index 26fe79646..7e3e69eb2 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 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 "loggingutils.h"
#include "packagemanagergui.h"
#include "component.h"
+#include "settings.h"
#include <QMetaEnum>
#include <QQmlEngine>
@@ -55,18 +56,6 @@ namespace QInstaller {
/*!
\inmodule QtInstallerFramework
- \class QInstaller::ConsoleProxy
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
- \class QInstaller::InstallerProxy
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
\class QInstaller::QDesktopServicesProxy
\internal
*/
@@ -77,28 +66,6 @@ namespace QInstaller {
\internal
*/
-QJSValue InstallerProxy::components(const QString &regexp) const
-{
- if (m_core) {
- const QList<Component*> all = m_core->components(PackageManagerCore::ComponentType::All, regexp);
- QJSValue scriptComponentsObject = m_engine->newArray(all.count());
- for (int i = 0; i < all.count(); ++i) {
- Component *const component = all.at(i);
- QQmlEngine::setObjectOwnership(component, QQmlEngine::CppOwnership);
- scriptComponentsObject.setProperty(i, m_engine->newQObject(component));
- }
- return scriptComponentsObject;
- }
- return m_engine->newArray();
-}
-
-QJSValue InstallerProxy::componentByName(const QString &componentName)
-{
- if (m_core)
- return m_engine->newQObject(m_core->componentByName(componentName));
- return QJSValue();
-}
-
QJSValue QDesktopServicesProxy::findFiles(const QString &path, const QString &pattern)
{
QStringList result;
@@ -222,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)
@@ -377,24 +350,23 @@ QString QFileDialogProxy::getExistingFileOrDirectory(const QString &caption,
/*!
Constructs a script engine with \a core as parent.
*/
-ScriptEngine::ScriptEngine(PackageManagerCore *core) :
- QObject(core),
- m_guiProxy(new GuiProxy(this, this))
+ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core)
+ , m_guiProxy(new GuiProxy(this, this))
+ , m_core(core)
{
- m_engine.installExtensions(QJSEngine::TranslationExtension);
+ m_engine.installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension);
QJSValue global = m_engine.globalObject();
- global.setProperty(QLatin1String("console"), m_engine.newQObject(new ConsoleProxy));
+
global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy(core)));
- const QJSValue proxy = m_engine.newQObject(new InstallerProxy(this, core));
- global.setProperty(QLatin1String("InstallerProxy"), proxy);
- global.setProperty(QLatin1String("print"), m_engine.newQObject(new ConsoleProxy)
- .property(QLatin1String("log")));
global.setProperty(QLatin1String("systemInfo"), m_engine.newQObject(new SystemInfo));
global.setProperty(QLatin1String("QInstaller"), generateQInstallerObject());
global.setProperty(QLatin1String("buttons"), generateWizardButtonsObject());
global.setProperty(QLatin1String("QMessageBox"), generateMessageBoxObject());
global.setProperty(QLatin1String("QDesktopServices"), generateDesktopServicesObject());
+#ifdef Q_OS_WIN
+ global.setProperty(QLatin1String("QSettings"), generateSettingsObject());
+#endif
if (core) {
setGuiQObject(core->guiObject());
@@ -405,19 +377,14 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
global.setProperty(QLatin1String("installer"), m_engine.newQObject(new QObject));
}
global.setProperty(QLatin1String("gui"), m_engine.newQObject(m_guiProxy));
-
- global.property(QLatin1String("installer")).setProperty(QLatin1String("components"),
- proxy.property(QLatin1String("components")));
- global.property(QLatin1String("installer")).setProperty(QLatin1String("componentByName"),
- proxy.property(QLatin1String("componentByName")));
}
/*!
Creates a JavaScript object that wraps the given QObject \a object.
- Signals and slots, properties and children of \a object are
- available as properties of the created QJSValue. In addition some helper methods and properties
- are added:
+ Signals and slots, properties and children of \a object are available as properties
+ of the created QJSValue. If \a qtScriptCompat is set to \c true (default), some helper
+ methods and properties from the legacy \c QtScript module are added:
\list
\li findChild(), findChildren() recursively search for child objects with the given
@@ -426,7 +393,7 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
names.
\endlist
*/
-QJSValue ScriptEngine::newQObject(QObject *object)
+QJSValue ScriptEngine::newQObject(QObject *object, bool qtScriptCompat)
{
QJSValue jsValue = m_engine.newQObject(object);
if (!jsValue.isQObject())
@@ -434,6 +401,9 @@ QJSValue ScriptEngine::newQObject(QObject *object)
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
+ if (!qtScriptCompat) // skip adding the extra properties
+ return jsValue;
+
// add findChild(), findChildren() methods known from QtScript
QJSValue findChild = m_engine.evaluate(
QLatin1String("(function() { return gui.findChild(this, arguments[0]); })"));
@@ -568,14 +538,17 @@ QJSValue ScriptEngine::callScriptMethod(const QJSValue &scriptContext, const QSt
if (!method.isCallable())
return QJSValue(QJSValue::UndefinedValue);
if (method.isError()) {
- throw Error(method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : method.toString());
+ QString errorString = method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : method.toString();
+
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
const QJSValue result = method.call(arguments);
if (result.isError()) {
- throw Error(result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : result.toString());
+ QString errorString = result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : result.toString();
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
stack.removeLast();
@@ -651,20 +624,38 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths)
SETPROPERTY(desktopServices, TempLocation, QStandardPaths)
SETPROPERTY(desktopServices, HomeLocation, QStandardPaths)
- SETPROPERTY(desktopServices, DataLocation, 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
return object;
}
+#ifdef Q_OS_WIN
+QJSValue ScriptEngine::generateSettingsObject()
+{
+ QJSValue settingsObject = m_engine.newArray();
+ SETPROPERTY(settingsObject, NativeFormat, QSettings)
+ SETPROPERTY(settingsObject, IniFormat, QSettings)
+ SETPROPERTY(settingsObject, Registry32Format, QSettings)
+ SETPROPERTY(settingsObject, Registry64Format, QSettings)
+ SETPROPERTY(settingsObject, InvalidFormat, QSettings)
+ return settingsObject;
+}
+#endif
+
QJSValue ScriptEngine::generateQInstallerObject()
{
// register ::WizardPage enum in the script connection
diff --git a/src/libs/installer/scriptengine.h b/src/libs/installer/scriptengine.h
index a0c64c0e4..0b43465cb 100644
--- a/src/libs/installer/scriptengine.h
+++ b/src/libs/installer/scriptengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,7 +48,7 @@ public:
explicit ScriptEngine(PackageManagerCore *core = 0);
QJSValue globalObject() const { return m_engine.globalObject(); }
- QJSValue newQObject(QObject *object);
+ QJSValue newQObject(QObject *object, bool qtScriptCompat = true);
QJSValue newArray(uint length = 0);
QJSValue evaluate(const QString &program, const QString &fileName = QString(),
int lineNumber = 1);
@@ -69,11 +69,15 @@ private:
QJSValue generateQInstallerObject();
QJSValue generateWizardButtonsObject();
QJSValue generateDesktopServicesObject();
+#ifdef Q_OS_WIN
+ QJSValue generateSettingsObject();
+#endif
private:
QJSEngine m_engine;
QHash<QString, QStringList> m_callstack;
GuiProxy *m_guiProxy;
+ PackageManagerCore *m_core;
};
}
diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h
index a0936fe75..101d4f303 100644
--- a/src/libs/installer/scriptengine_p.h
+++ b/src/libs/installer/scriptengine_p.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.
@@ -43,36 +43,6 @@ class PackageManagerCore;
class PackageManagerGui;
class ScriptEngine;
-class ConsoleProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(ConsoleProxy)
-
-public:
- ConsoleProxy() {}
-
-public slots :
- void log(const QString &log) { qCDebug(QInstaller::lcInstallerInstallLog).noquote() << log; }
-};
-
-class InstallerProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(InstallerProxy)
-
-public:
- InstallerProxy(ScriptEngine *engine, PackageManagerCore *core)
- : m_engine(engine), m_core(core) {}
-
-public slots:
- QJSValue components(const QString &regexp = QString()) const;
- QJSValue componentByName(const QString &componentName);
-
-private:
- ScriptEngine *m_engine;
- PackageManagerCore *m_core;
-};
-
class QFileDialogProxy : public QObject
{
Q_OBJECT
@@ -146,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);
@@ -178,8 +149,6 @@ private:
} // namespace QInstaller
-Q_DECLARE_METATYPE(QInstaller::ConsoleProxy*)
-Q_DECLARE_METATYPE(QInstaller::InstallerProxy*)
Q_DECLARE_METATYPE(QInstaller::QFileDialogProxy*)
Q_DECLARE_METATYPE(QInstaller::QDesktopServicesProxy*)
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
index b0c6b9d31..e18f63689 100644
--- a/src/libs/installer/settings.cpp
+++ b/src/libs/installer/settings.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,8 +34,11 @@
#include "globals.h"
#include "fileutils.h"
+#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QStringList>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QUuid>
#include <QtGui/QFontMetrics>
#include <QtWidgets/QApplication>
@@ -76,6 +79,8 @@ static const QLatin1String scFtpProxy("FtpProxy");
static const QLatin1String scHttpProxy("HttpProxy");
static const QLatin1String scProxyType("ProxyType");
+static const QLatin1String scLocalCachePath("LocalCachePath");
+
const char scControlScript[] = "ControlScript";
template <typename T>
@@ -313,13 +318,13 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
elementList << scName << scVersion << scTitle << scPublisher << scProductUrl
<< scTargetDir << scAdminTargetDir
<< scInstallerApplicationIcon << scInstallerWindowIcon
- << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap
+ << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap << scAliasDefinitionsFile
<< scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scMaintenanceToolAlias
- << scRemoveTargetDir
+ << scRemoveTargetDir << scLocalCacheDir << scPersistentLocalCache
<< scRunProgram << scRunProgramArguments << scRunProgramDescription
<< scDependsOnLocalInstallerBinary
- << scAllowSpaceInPath << scAllowNonAsciiCharacters << scDisableAuthorizationFallback
- << scDisableCommandLineInterface
+ << scAllowSpaceInPath << scAllowNonAsciiCharacters << scAllowRepositoriesForOfflineInstaller
+ << scDisableAuthorizationFallback << scDisableCommandLineInterface
<< scWizardStyle << scStyleSheet << scTitleColor
<< scWizardDefaultWidth << scWizardDefaultHeight << scWizardMinimumWidth << scWizardMinimumHeight
<< scWizardShowPageList << scProductImages
@@ -386,8 +391,6 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
s.d->m_data.value(QLatin1String("UninstallerName"), QLatin1String("maintenancetool"))
.toString());
}
- if (s.d->m_data.value(scMaintenanceToolAlias).toString().isEmpty())
- s.d->m_data.replace(scMaintenanceToolAlias, s.d->m_data.value(scMaintenanceToolName));
if (s.d->m_data.value(scTargetConfigurationFile).toString().isEmpty())
s.d->m_data.replace(scTargetConfigurationFile, QLatin1String("components.xml"));
@@ -482,11 +485,11 @@ static int lengthToInt(const QVariant &variant)
QString length = variant.toString().trimmed();
if (length.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) {
length.chop(2);
- return qRound(length.toDouble() * QApplication::fontMetrics().height());
+ return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).height());
}
if (length.endsWith(QLatin1String("ex"), Qt::CaseInsensitive)) {
length.chop(2);
- return qRound(length.toDouble() * QApplication::fontMetrics().xHeight());
+ return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).xHeight());
}
if (length.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
length.chop(2);
@@ -536,6 +539,11 @@ void Settings::setProductImages(const QMap<QString, QVariant> &images)
d->m_data.insert(scProductImages, QVariant::fromValue(images));
}
+QString Settings::aliasDefinitionsFile() const
+{
+ return d->absolutePathFromKey(scAliasDefinitionsFile);
+}
+
QString Settings::installerApplicationIcon() const
{
return d->absolutePathFromKey(scInstallerApplicationIcon, systemIconSuffix());
@@ -641,6 +649,11 @@ bool Settings::allowNonAsciiCharacters() const
return d->m_data.value(scAllowNonAsciiCharacters, false).toBool();
}
+bool Settings::allowRepositoriesForOfflineInstaller() const
+{
+ return d->m_data.value(scAllowRepositoriesForOfflineInstaller, true).toBool();
+}
+
bool Settings::disableAuthorizationFallback() const
{
return d->m_data.value(scDisableAuthorizationFallback, false).toBool();
@@ -742,7 +755,7 @@ Settings::Update Settings::updateRepositoryCategories(const RepoHash &updates)
return update ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
}
-static bool apply(const RepoHash &updates, QHash<QUrl, Repository> *reposToUpdate)
+static bool apply(const RepoHash &updates, QMultiHash<QUrl, Repository> *reposToUpdate)
{
bool update = false;
QList<QPair<Repository, Repository> > values = updates.values(QLatin1String("replace"));
@@ -780,7 +793,7 @@ Settings::Update Settings::updateDefaultRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> defaultRepos;
+ QMultiHash <QUrl, Repository> defaultRepos;
foreach (const QVariant &variant, d->m_data.values(scRepositories)) {
const Repository repository = variant.value<Repository>();
defaultRepos.insert(repository.url(), repository);
@@ -834,7 +847,7 @@ Settings::Update Settings::updateUserRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> reposToUpdate;
+ QMultiHash <QUrl, Repository> reposToUpdate;
foreach (const QVariant &variant, d->m_data.values(scUserRepositories)) {
const Repository repository = variant.value<Repository>();
reposToUpdate.insert(repository.url(), repository);
@@ -874,6 +887,40 @@ void Settings::setRepositorySettingsPageVisible(bool visible)
d->m_data.replace(scRepositorySettingsPageVisible, visible);
}
+bool Settings::persistentLocalCache() const
+{
+ return d->m_data.value(scPersistentLocalCache, true).toBool();
+}
+
+void Settings::setPersistentLocalCache(bool enable)
+{
+ d->m_data.replace(scPersistentLocalCache, enable);
+}
+
+QString Settings::localCacheDir() const
+{
+ const QString fallback = QLatin1String("qt-installer-framework") + QDir::separator()
+ + QUuid::createUuidV3(QUuid(), applicationName()).toString(QUuid::WithoutBraces);
+ return d->m_data.value(scLocalCacheDir, fallback).toString();
+}
+
+void Settings::setLocalCacheDir(const QString &dir)
+{
+ d->m_data.replace(scLocalCacheDir, dir);
+}
+
+QString Settings::localCachePath() const
+{
+ const QString fallback = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)
+ + QDir::separator() + localCacheDir();
+ return d->m_data.value(scLocalCachePath, fallback).toString();
+}
+
+void Settings::setLocalCachePath(const QString &path)
+{
+ d->m_data.replace(scLocalCachePath, path);
+}
+
Settings::ProxyType Settings::proxyType() const
{
return Settings::ProxyType(d->m_data.value(scProxyType, Settings::NoProxy).toInt());
@@ -956,7 +1003,7 @@ void Settings::setSaveDefaultRepositories(bool save)
QString Settings::repositoryCategoryDisplayName() const
{
QString displayName = d->m_data.value(QLatin1String(scRepositoryCategoryDisplayName)).toString();
- return displayName.isEmpty() ? tr("Select Categories") : displayName;
+ return displayName.isEmpty() ? tr("Categories") : displayName;
}
void Settings::setRepositoryCategoryDisplayName(const QString& name)
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index c34ae2ee9..85b59869c 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,7 +42,7 @@
namespace QInstaller {
class Repository;
-typedef QHash<QString, QPair<Repository, Repository> > RepoHash;
+typedef QMultiHash<QString, QPair<Repository, Repository> > RepoHash;
class INSTALLER_EXPORT Settings
{
@@ -95,6 +95,8 @@ public:
QMap<QString, QVariant> productImages() const;
void setProductImages(const QMap<QString, QVariant> &images);
+ QString aliasDefinitionsFile() const;
+
QString applicationName() const;
QString version() const;
@@ -143,6 +145,8 @@ public:
bool allowSpaceInPath() const;
bool allowNonAsciiCharacters() const;
+ bool allowRepositoriesForOfflineInstaller() const;
+
bool disableAuthorizationFallback() const;
bool disableCommandLineInterface() const;
@@ -153,6 +157,15 @@ public:
bool repositorySettingsPageVisible() const;
void setRepositorySettingsPageVisible(bool visible);
+ bool persistentLocalCache() const;
+ void setPersistentLocalCache(bool enable);
+
+ QString localCacheDir() const;
+ void setLocalCacheDir(const QString &dir);
+
+ QString localCachePath() const;
+ void setLocalCachePath(const QString &path);
+
Settings::ProxyType proxyType() const;
void setProxyType(Settings::ProxyType type);
diff --git a/src/libs/installer/settingsoperation.cpp b/src/libs/installer/settingsoperation.cpp
index c5fe8384c..a74161178 100644
--- a/src/libs/installer/settingsoperation.cpp
+++ b/src/libs/installer/settingsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -46,6 +46,7 @@ SettingsOperation::SettingsOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
setName(QLatin1String("Settings"));
+ setRequiresUnreplacedVariables(true);
}
void SettingsOperation::backup()
@@ -104,10 +105,17 @@ bool SettingsOperation::performOperation()
if (!checkArguments())
return false;
- const QString path = argumentKeyValue(QLatin1String("path"));
+ QString path = argumentKeyValue(QLatin1String("path"));
const QString method = argumentKeyValue(QLatin1String("method"));
const QString key = argumentKeyValue(QLatin1String("key"));
- const QString aValue = argumentKeyValue(QLatin1String("value"));
+ QString aValue = argumentKeyValue(QLatin1String("value"));
+
+ if (requiresUnreplacedVariables()) {
+ if (PackageManagerCore *const core = packageManager()) {
+ path = core->replaceVariables(path);
+ aValue = core->replaceVariables(aValue);
+ }
+ }
// use MkdirOperation to get the path so it can remove it with MkdirOperation::undoOperation later
KDUpdater::MkdirOperation mkDirOperation;
@@ -120,7 +128,7 @@ bool SettingsOperation::performOperation()
}
setValue(QLatin1String("createddir"), mkDirOperation.value(QLatin1String("createddir")));
- QSettingsWrapper settings(path, QSettingsWrapper::IniFormat);
+ QSettingsWrapper settings(path, QSettings::IniFormat);
if (method == QLatin1String("set"))
settings.setValue(key, aValue);
else if (method == QLatin1String("remove"))
@@ -152,17 +160,28 @@ bool SettingsOperation::undoOperation()
{
if (!checkArguments())
return false;
- const QString path = argumentKeyValue(QLatin1String("path"));
+ QString path = argumentKeyValue(QLatin1String("path"));
const QString method = argumentKeyValue(QLatin1String("method"));
const QString key = argumentKeyValue(QLatin1String("key"));
- const QString aValue = argumentKeyValue(QLatin1String("value"));
+ QString aValue = argumentKeyValue(QLatin1String("value"));
if (method.startsWith(QLatin1String("remove")))
return true;
+ if (requiresUnreplacedVariables()) {
+ if (PackageManagerCore *const core = packageManager()) {
+ path = core->replaceVariables(path);
+ aValue = core->replaceVariables(aValue);
+ // Check is different settings file is wanted to be used.
+ // Old settings file name should be set to variable <variable_name>_OLD,
+ // and new path is then read from <variable_name> variable.
+ variableReplacement(&path);
+ }
+ }
+
bool cleanUp = false;
{ // kill the scope to kill settings object, else remove file will not work
- QSettingsWrapper settings(path, QSettingsWrapper::IniFormat);
+ QSettingsWrapper settings(path, QSettings::IniFormat);
if (method == QLatin1String("set")) {
settings.remove(key);
} else if (method == QLatin1String("add_array_value")) {
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 12ffbcdd7..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.
*/
@@ -150,10 +150,10 @@ QList<VolumeInfo> mountedVolumes()
bool pathIsOnLocalDevice(const QString &path)
{
- if (!QFileInfo(path).exists())
+ if (!QFileInfo::exists(path))
return false;
- if (path.startsWith(QLatin1String("\\\\")))
+ if (path.startsWith(QLatin1String("//")))
return false;
QDir dir(path);
diff --git a/src/libs/installer/systeminfo.cpp b/src/libs/installer/systeminfo.cpp
index 6a1976c4a..aed02c569 100644
--- a/src/libs/installer/systeminfo.cpp
+++ b/src/libs/installer/systeminfo.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,6 +55,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent)
\list
\li "i386"
\li "x86_64"
+ \li "arm64"
\endlist
\note This function depends on what the OS will report and may not detect the actual CPU
@@ -62,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 QSysInfo::currentCpuArchitecture() buildCpuArchitecture()
*/
QString SystemInfo::currentCpuArchitecture() const
{
@@ -70,6 +71,29 @@ QString SystemInfo::currentCpuArchitecture() const
}
/*!
+ \property SystemInfo::buildCpuArchitecture
+
+ The architecture of the CPU that the application was compiled for, in text format.
+
+ Possible values include:
+ \list
+ \li "i386"
+ \li "x86_64"
+ \li "arm64"
+ \endlist
+
+ \note Note that this may not match the actual CPU that the application is running on if
+ 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() currentCpuArchitecture()
+*/
+QString SystemInfo::buildCpuArchitecture() const
+{
+ return QSysInfo::buildCpuArchitecture();
+}
+
+/*!
\property SystemInfo::kernelType
The type of the operating system kernel the installer was compiled for. It is also the
@@ -124,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/installer/systeminfo.h b/src/libs/installer/systeminfo.h
index c5451605e..a1393397a 100644
--- a/src/libs/installer/systeminfo.h
+++ b/src/libs/installer/systeminfo.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,6 +39,7 @@ class SystemInfo : public QObject
Q_DISABLE_COPY(SystemInfo)
Q_PROPERTY(QString currentCpuArchitecture READ currentCpuArchitecture CONSTANT)
+ Q_PROPERTY(QString buildCpuArchitecture READ buildCpuArchitecture CONSTANT)
Q_PROPERTY(QString kernelType READ kernelType CONSTANT)
Q_PROPERTY(QString kernelVersion READ kernelVersion CONSTANT)
Q_PROPERTY(QString productType READ productType CONSTANT)
@@ -49,7 +50,7 @@ public:
explicit SystemInfo(QObject *parent = 0);
QString currentCpuArchitecture() const;
-
+ QString buildCpuArchitecture() const;
QString kernelType() const;
QString kernelVersion() const;
QString productType() const;
diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp
index af03df8c5..de753f71a 100644
--- a/src/libs/installer/uninstallercalculator.cpp
+++ b/src/libs/installer/uninstallercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,8 +32,6 @@
#include "packagemanagercore.h"
#include "globals.h"
-#include <QDebug>
-
namespace QInstaller {
/*!
@@ -42,57 +40,73 @@ namespace QInstaller {
\internal
*/
-UninstallerCalculator::UninstallerCalculator(const QList<Component *> &installedComponents
- , PackageManagerCore *core
+UninstallerCalculator::UninstallerCalculator(PackageManagerCore *core
, const AutoDependencyHash &autoDependencyComponentHash
, const LocalDependencyHash &localDependencyComponentHash
, const QStringList &localVirtualComponents)
- : m_installedComponents(installedComponents)
- , m_core(core)
+ : CalculatorBase(core)
, m_autoDependencyComponentHash(autoDependencyComponentHash)
, m_localDependencyComponentHash(localDependencyComponentHash)
, m_localVirtualComponents(localVirtualComponents)
{
}
-QSet<Component *> UninstallerCalculator::componentsToUninstall() const
+UninstallerCalculator::~UninstallerCalculator()
{
- return m_componentsToUninstall;
}
-void UninstallerCalculator::appendComponentToUninstall(Component *component, const bool reverse)
+bool UninstallerCalculator::solveComponent(Component *component, const QString &version)
{
+ Q_UNUSED(version)
+
if (!component)
- return;
+ return true;
if (!component->isInstalled())
- return;
+ return true;
if (m_localDependencyComponentHash.contains(component->name())) {
const QStringList &dependencies = PackageManagerCore::parseNames(m_localDependencyComponentHash.value(component->name()));
for (const QString &dependencyComponent : dependencies) {
Component *depComponent = m_core->componentByName(dependencyComponent);
- if (depComponent && depComponent->isInstalled() && !m_componentsToUninstall.contains(depComponent)) {
- appendComponentToUninstall(depComponent, reverse);
- insertUninstallReason(depComponent, UninstallerCalculator::Dependent, component->name());
- } else if (reverse) {
- appendComponentToUninstall(depComponent, true);
- }
+ if (!depComponent || !depComponent->isInstalled())
+ continue;
+
+ if (m_resolvedComponents.contains(depComponent)
+ || m_core->orderedComponentsToInstall().contains(depComponent)) {
+ // Component is already selected for uninstall or update
+ continue;
+ }
+
+ if (depComponent->isEssential() || depComponent->forcedInstallation()) {
+ const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
+ "Impossible dependency resolution detected. Forced install component \"%1\" would be uninstalled "
+ "because its dependency \"%2\" is marked for uninstallation with reason: \"%3\".")
+ .arg(depComponent->name(), component->name(), resolutionText(component));
+
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+ return false;
+ }
+ // Resolve also all cascading dependencies
+ if (!solveComponent(depComponent))
+ return false;
+
+ insertResolution(depComponent, Resolution::Dependent, component->name());
}
}
- if (reverse) {
- m_componentsToUninstall.remove(component);
- } else {
- m_componentsToUninstall.insert(component);
- }
+
+ m_resolvedComponents.append(component);
+ return true;
}
-void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components, const bool reverse)
+bool UninstallerCalculator::solve(const QList<Component*> &components)
{
- if (components.isEmpty())
- return;
- foreach (Component *component, components)
- appendComponentToUninstall(component, reverse);
+ foreach (Component *component, components) {
+ if (!solveComponent(component))
+ return false;
+ }
+
QList<Component*> autoDependOnList;
// All regular dependees are resolved. Now we are looking for auto depend on components.
for (Component *component : components) {
@@ -104,120 +118,83 @@ void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*>
Component *autoDepComponent = m_core->componentByName(autoDependencyComponent);
if (autoDepComponent && autoDepComponent->isInstalled()) {
// A component requested auto uninstallation, keep it to resolve their dependencies as well.
- if (reverse) {
- autoDependOnList.append(autoDepComponent);
- } else if (!m_componentsToUninstall.contains(autoDepComponent)) {
- insertUninstallReason(autoDepComponent, UninstallerCalculator::AutoDependent, component->name());
+ if (!m_resolvedComponents.contains(autoDepComponent)) {
+ insertResolution(autoDepComponent, Resolution::Automatic, component->name());
autoDepComponent->setInstallAction(ComponentModelHelper::AutodependUninstallation);
autoDependOnList.append(autoDepComponent);
}
}
}
}
+
if (!autoDependOnList.isEmpty())
- appendComponentsToUninstall(autoDependOnList, reverse);
+ solve(autoDependOnList);
else
- appendVirtualComponentsToUninstall(reverse);
-}
+ appendVirtualComponentsToUninstall();
-void UninstallerCalculator::removeComponentsFromUnInstall(const QList<Component*> &components)
-{
- appendComponentsToUninstall(components, true);
-}
-
-void UninstallerCalculator::insertUninstallReason(Component *component, const UninstallReasonType uninstallReason,
- const QString &referencedComponentName)
-{
- // keep the first reason
- if (m_toUninstallComponentIdReasonHash.contains(component->name()))
- return;
- m_toUninstallComponentIdReasonHash.insert(component->name(),
- qMakePair(uninstallReason, referencedComponentName));
+ return true;
}
-QString UninstallerCalculator::uninstallReason(Component *component) const
+QString UninstallerCalculator::resolutionText(Component *component) const
{
- UninstallerCalculator::UninstallReasonType reason = uninstallReasonType(component);
+ Resolution reason = resolutionType(component);
switch (reason) {
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("UninstallerCalculator",
"Deselected Components:");
- case Replaced:
+ case Resolution::Replaced:
return QCoreApplication::translate("UninstallerCalculator", "Components replaced "
- "by \"%1\":").arg(uninstallReasonReferencedComponent(component));
- case VirtualDependent:
+ "by \"%1\":").arg(referencedComponent(component));
+ case Resolution::VirtualDependent:
return QCoreApplication::translate("UninstallerCalculator",
"Removing virtual components without existing dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("UninstallerCalculator", "Components "
- "dependency \"%1\" removed:").arg(uninstallReasonReferencedComponent(component));
- case AutoDependent:
+ "dependency \"%1\" removed:").arg(referencedComponent(component));
+ case Resolution::Automatic:
return QCoreApplication::translate("UninstallerCalculator", "Components "
- "autodependency \"%1\" removed:").arg(uninstallReasonReferencedComponent(component));
+ "autodependency \"%1\" removed:").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid uninstall resolution detected!");
}
return QString();
}
-UninstallerCalculator::UninstallReasonType UninstallerCalculator::uninstallReasonType(Component *c) const
-{
- return m_toUninstallComponentIdReasonHash.value(c->name()).first;
-}
-
-QString UninstallerCalculator::uninstallReasonReferencedComponent(Component *component) const
-{
- return m_toUninstallComponentIdReasonHash.value(component->name()).second;
-}
-
-void UninstallerCalculator::appendVirtualComponentsToUninstall(const bool reverse)
+void UninstallerCalculator::appendVirtualComponentsToUninstall()
{
QList<Component*> unneededVirtualList;
-
// Check for virtual components without dependees
- if (reverse) {
- for (Component *reverseFromUninstall : qAsConst(m_virtualComponentsForReverse)) {
- if (m_componentsToUninstall.contains(reverseFromUninstall)) {
- bool required = false;
- // Check if installed or about to be updated -packages are dependant on the package
- const QList<Component*> installDependants = m_core->installDependants(reverseFromUninstall);
- for (Component *dependant : installDependants) {
- if (dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) {
- required = true;
- break;
- }
- }
- if (required) {
- unneededVirtualList.append(reverseFromUninstall);
- }
- }
- }
- } else {
- for (const QString &componentName : qAsConst(m_localVirtualComponents)) {
- Component *virtualComponent = m_core->componentByName(componentName);
- if (virtualComponent->isInstalled() && !m_componentsToUninstall.contains(virtualComponent)) {
- // Components with auto dependencies were handled in the previous step
- if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation())
- continue;
-
- bool required = false;
- // Check if installed or about to be updated -packages are dependant on the package
- const QList<Component*> installDependants = m_core->installDependants(virtualComponent);
- for (Component *dependant : installDependants) {
- if (dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) {
- required = true;
- break;
- }
- }
- if (!required) {
- unneededVirtualList.append(virtualComponent);
- m_virtualComponentsForReverse.append(virtualComponent);
- insertUninstallReason(virtualComponent, UninstallerCalculator::VirtualDependent);
- }
- }
+ for (const QString &componentName : qAsConst(m_localVirtualComponents)) {
+ Component *virtualComponent = m_core->componentByName(componentName, m_core->components(PackageManagerCore::ComponentType::All));
+ if (!virtualComponent)
+ continue;
+
+ if (virtualComponent->isInstalled() && !m_resolvedComponents.contains(virtualComponent)) {
+ // Components with auto dependencies were handled in the previous step
+ if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation())
+ continue;
+
+ if (!isRequiredVirtualPackage(virtualComponent)) {
+ unneededVirtualList.append(virtualComponent);
+ insertResolution(virtualComponent, Resolution::VirtualDependent);
+ }
}
}
if (!unneededVirtualList.isEmpty())
- appendComponentsToUninstall(unneededVirtualList, reverse);
+ solve(unneededVirtualList);
+}
+
+bool UninstallerCalculator::isRequiredVirtualPackage(Component *component)
+{
+ const QStringList localInstallDependents = m_core->localDependenciesToComponent(component);
+ for (const QString &dependent : localInstallDependents) {
+ Component *comp = m_core->componentByName(dependent);
+ if (!m_resolvedComponents.contains(comp)) {
+ return true;
+ }
+ }
+ return m_core->isDependencyForRequestedComponent(component);
}
} // namespace QInstaller
diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h
index 76e32c6a6..24979a9bb 100644
--- a/src/libs/installer/uninstallercalculator.h
+++ b/src/libs/installer/uninstallercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "installer_global.h"
#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -41,49 +42,30 @@ namespace QInstaller {
class Component;
class PackageManagerCore;
-class INSTALLER_EXPORT UninstallerCalculator
+class INSTALLER_EXPORT UninstallerCalculator : public CalculatorBase
{
public:
- enum UninstallReasonType
- {
- Selected, // "Deselected Component(s)"
- Replaced, // "Component(s) replaced by other components"
- VirtualDependent, // "No dependencies to virtual component"
- Dependent, // "Removed as dependency component is removed"
- AutoDependent // "Removed as autodependency component is removed"
- };
-
- UninstallerCalculator(const QList<Component *> &installedComponents, PackageManagerCore *core,
+ UninstallerCalculator(PackageManagerCore *core,
const AutoDependencyHash &autoDependencyComponentHash,
const LocalDependencyHash &localDependencyComponentHash,
const QStringList &localVirtualComponents);
+ ~UninstallerCalculator();
- QSet<Component*> componentsToUninstall() const;
-
- void appendComponentsToUninstall(const QList<Component*> &components, const bool reverse = false);
- void removeComponentsFromUnInstall(const QList<Component*> &components);
- void insertUninstallReason(Component *component,
- const UninstallReasonType uninstallReason,
- const QString &referencedComponentName = QString());
- QString uninstallReason(Component *component) const;
- UninstallerCalculator::UninstallReasonType uninstallReasonType(Component *c) const;
- QString uninstallReasonReferencedComponent(Component *component) const;
+ bool solve(const QList<Component*> &components) override;
+ QString resolutionText(Component *component) const override;
private:
- void appendComponentToUninstall(Component *component, const bool reverse);
- void appendVirtualComponentsToUninstall(const bool reverse);
+ bool solveComponent(Component *component, const QString &version = QString()) override;
+
+ bool isRequiredVirtualPackage(Component *component);
+ void appendVirtualComponentsToUninstall();
- QList<Component *> m_installedComponents;
- QSet<Component *> m_componentsToUninstall;
- PackageManagerCore *m_core;
- QHash<QString, QPair<UninstallReasonType, QString> > m_toUninstallComponentIdReasonHash;
+private:
AutoDependencyHash m_autoDependencyComponentHash;
LocalDependencyHash m_localDependencyComponentHash;
QStringList m_localVirtualComponents;
- QList<Component *> m_virtualComponentsForReverse;
};
-}
-
+} // namespace QInstaller
#endif // UNINSTALLERCALCULATOR_H
diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp
index 5294d00f6..9b64ade37 100644
--- a/src/libs/installer/utils.cpp
+++ b/src/libs/installer/utils.cpp
@@ -57,7 +57,7 @@
*/
void QInstaller::uiDetachedWait(int ms)
{
- QTime timer;
+ QElapsedTimer timer;
timer.start();
do {
QCoreApplication::processEvents(QEventLoop::AllEvents, ms);
@@ -139,7 +139,7 @@ QStringList QInstaller::localeCandidates(const QString &locale_)
at least one mutually exclusive pair of options set. Otherwise returns an empty
\c QStringList. The options considered mutual are provided with \a options.
*/
-QStringList QInstaller::checkMutualOptions(CommandLineParser &parser, const QStringList &options)
+QStringList QInstaller::checkMutualOptions(const CommandLineParser &parser, const QStringList &options)
{
QStringList mutual;
foreach (const QString &option, options) {
@@ -171,7 +171,7 @@ QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algo
const qint64 numRead = device->read(buffer.data(), buffer.size());
if (numRead <= 0)
return hash.result();
- hash.addData(buffer.constData(), numRead);
+ hash.addData(buffer.left(numRead));
}
return QByteArray(); // never reached
}
@@ -492,7 +492,7 @@ bool QInstaller::checkPrivilege(const wchar_t *privilege)
bool QInstaller::developerModeEnabled()
{
QSettingsWrapper appModelUnlock(QLatin1String("HKLM\\SOFTWARE\\Microsoft\\Windows\\"
- "CurrentVersion\\AppModelUnlock"), QSettingsWrapper::NativeFormat);
+ "CurrentVersion\\AppModelUnlock"), QSettings::NativeFormat);
return appModelUnlock.value(QLatin1String("AllowDevelopmentWithoutDevLicense"), false).toBool();
}
diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h
index 2bf997835..068490cc2 100644
--- a/src/libs/installer/utils.h
+++ b/src/libs/installer/utils.h
@@ -69,7 +69,7 @@ namespace QInstaller {
QStringList INSTALLER_EXPORT localeCandidates(const QString &locale);
- QStringList INSTALLER_EXPORT checkMutualOptions(CommandLineParser &parser, const QStringList &options);
+ QStringList INSTALLER_EXPORT checkMutualOptions(const CommandLineParser &parser, const QStringList &options);
INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string);
}
diff --git a/src/libs/kdtools/filedownloader.cpp b/src/libs/kdtools/filedownloader.cpp
index 2510dc4c8..4d4002b7a 100644
--- a/src/libs/kdtools/filedownloader.cpp
+++ b/src/libs/kdtools/filedownloader.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -657,14 +658,6 @@ void KDUpdater::FileDownloader::addCheckSumData(const QByteArray &data)
}
/*!
- Adds the \a length of characters of \a data to the cryptographic hash of the downloaded file.
-*/
-void KDUpdater::FileDownloader::addCheckSumData(const char *data, int length)
-{
- d->m_hash.addData(data, length);
-}
-
-/*!
Resets SHA-1 checksum data of the downloaded file.
*/
void KDUpdater::FileDownloader::resetCheckSumData()
@@ -920,7 +913,7 @@ void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event)
toWrite -= numWritten;
}
addSample(numRead);
- addCheckSumData(buffer.data(), numRead);
+ addCheckSumData(buffer.left(numRead));
if (numRead > 0) {
setProgress(d->source->pos(), d->source->size());
emit downloadProgress(calcProgress(d->source->pos(), d->source->size()));
@@ -1112,7 +1105,7 @@ void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent *event)
const qint64 numRead = d->destFile.read(buffer.data(), buffer.size());
addSample(numRead);
- addCheckSumData(buffer.data(), numRead);
+ addCheckSumData(buffer.left(numRead));
if (numRead > 0) {
setProgress(d->destFile.pos(), d->destFile.size());
@@ -1191,9 +1184,8 @@ struct KDUpdater::HttpDownloader::Private
disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished);
disconnect(http, &QNetworkReply::downloadProgress,
q, &HttpDownloader::httpReadProgress);
- void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
- disconnect(http, errorSignal, q, &HttpDownloader::httpError);
+ disconnect(http, &QNetworkReply::errorOccurred, q, &HttpDownloader::httpError);
http->deleteLater();
}
http = 0;
@@ -1219,8 +1211,14 @@ KDUpdater::HttpDownloader::HttpDownloader(QObject *parent)
#endif
connect(&d->manager, &QNetworkAccessManager::authenticationRequired,
this, &HttpDownloader::onAuthenticationRequired);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
connect(&d->manager, &QNetworkAccessManager::networkAccessibleChanged,
this, &HttpDownloader::onNetworkAccessibleChanged);
+#else
+ auto netInfo = QNetworkInformation::instance();
+ connect(netInfo, &QNetworkInformation::reachabilityChanged,
+ this, &HttpDownloader::onReachabilityChanged);
+#endif
}
@@ -1313,7 +1311,7 @@ void KDUpdater::HttpDownloader::httpReadyRead()
written += numWritten;
}
addSample(written);
- addCheckSumData(buffer.data(), read);
+ addCheckSumData(buffer.left(read));
updateBytesDownloadedBeforeResume(written);
}
}
@@ -1475,29 +1473,47 @@ void KDUpdater::HttpDownloader::startDownload(const QUrl &url)
d->m_authenticationCount = 0;
d->manager.setProxyFactory(proxyFactory());
clearBytesDownloadedBeforeResume();
- d->http = d->manager.get(QNetworkRequest(url));
+
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
+
+ d->http = d->manager.get(request);
connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead);
connect(d->http, &QNetworkReply::downloadProgress,
this, &HttpDownloader::httpReadProgress);
connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished);
- void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
- connect(d->http, errorSignal, this, &HttpDownloader::httpError);
+ connect(d->http, &QNetworkReply::errorOccurred, this, &HttpDownloader::httpError);
+ bool fileOpened = false;
if (d->destFileName.isEmpty()) {
QTemporaryFile *file = new QTemporaryFile(this);
- file->open();
+ fileOpened = file->open();
d->destination = file;
} else {
d->destination = new QFile(d->destFileName, this);
- d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ fileOpened = d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
}
-
- if (!d->destination->isOpen()) {
- const QString error = d->destination->errorString();
- const QString fileName = d->destination->fileName();
- d->shutDown();
- setDownloadAborted(tr("Cannot download %1. Cannot create file \"%2\": %3").arg(
- url.toString(), fileName, error));
+ if (!fileOpened) {
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Failed to open file " << d->destFileName
+ << ": "<<d->destination->errorString() << ". Trying again.";
+ QFileInfo fileInfo;
+ fileInfo.setFile(d->destination->fileName());
+ if (!QDir().mkpath(fileInfo.absolutePath())) {
+ setDownloadAborted(tr("Cannot download %1. Cannot create directory for \"%2\"").arg(
+ url.toString(), fileInfo.filePath()));
+ } else {
+ fileOpened = d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ if (fileOpened)
+ return;
+ if (d->destination->exists())
+ qCWarning(QInstaller::lcInstallerInstallLog) << "File exists but installer is unable to open it.";
+ else
+ qCWarning(QInstaller::lcInstallerInstallLog) << "File does not exist.";
+ setDownloadAborted(tr("Cannot download %1. Cannot create file \"%2\": %3").arg(
+ url.toString(), d->destination->fileName(), d->destination->errorString()));
+ d->shutDown();
+ }
}
}
@@ -1517,8 +1533,7 @@ void KDUpdater::HttpDownloader::resumeDownload()
connect(d->http, &QNetworkReply::downloadProgress,
this, &HttpDownloader::httpReadProgress);
connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished);
- void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
- connect(d->http, errorSignal, this, &HttpDownloader::httpError);
+ connect(d->http, &QNetworkReply::errorOccurred, this, &HttpDownloader::httpError);
runDownloadSpeedTimer();
runDownloadDeadlineTimer();
}
@@ -1560,6 +1575,7 @@ void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, Q
}
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
{
if (accessible == QNetworkAccessManager::NotAccessible) {
@@ -1574,6 +1590,22 @@ void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager
}
}
}
+#else
+void KDUpdater::HttpDownloader::onReachabilityChanged(QNetworkInformation::Reachability newReachability)
+{
+ if (newReachability == QNetworkInformation::Reachability::Online) {
+ if (isDownloadPaused()) {
+ setDownloadPaused(false);
+ resumeDownload();
+ }
+ } else {
+ d->shutDown(false);
+ setDownloadPaused(true);
+ setDownloadResumed(false);
+ stopDownloadDeadlineTimer();
+ }
+}
+#endif
#ifndef QT_NO_SSL
@@ -1611,7 +1643,7 @@ void KDUpdater::HttpDownloader::onSslErrors(QNetworkReply* reply, const QList<QS
"the error may be temporary and you can try again.")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
- msgBox.setButtonText(QMessageBox::Yes, tr("Try again"));
+ msgBox.addButton(tr("Try again"), QMessageBox::YesRole);
msgBox.setDefaultButton(QMessageBox::Cancel);
if (msgBox.exec() == QMessageBox::Cancel) {
diff --git a/src/libs/kdtools/filedownloader.h b/src/libs/kdtools/filedownloader.h
index f02a4cc85..e71f7d62f 100644
--- a/src/libs/kdtools/filedownloader.h
+++ b/src/libs/kdtools/filedownloader.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,6 +59,8 @@ public:
QByteArray assumedSha1Sum() const;
void setAssumedSha1Sum(const QByteArray &sha1);
+ void setCheckSha1Sum(const bool checkSha1Sum);
+ bool checkSha1Sum() const;
QString scheme() const;
void setScheme(const QString &scheme);
@@ -140,7 +143,6 @@ protected:
void emitEstimatedDownloadTime();
void addCheckSumData(const QByteArray &data);
- void addCheckSumData(const char *data, int length);
void resetCheckSumData();
private Q_SLOTS:
diff --git a/src/libs/kdtools/filedownloader_p.h b/src/libs/kdtools/filedownloader_p.h
index ec47e31ca..23eff08d7 100644
--- a/src/libs/kdtools/filedownloader_p.h
+++ b/src/libs/kdtools/filedownloader_p.h
@@ -35,6 +35,10 @@
#include <QtNetwork/QNetworkReply>
#include <QNetworkAccessManager>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QNetworkInformation>
+#endif
+
// these classes are not a part of the public API
namespace KDUpdater {
@@ -130,7 +134,11 @@ private Q_SLOTS:
void httpDone(bool error);
void httpReqFinished();
void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
+#else
+ void onReachabilityChanged(QNetworkInformation::Reachability newReachability);
+#endif
#ifndef QT_NO_SSL
void onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors);
#endif
diff --git a/src/libs/kdtools/kdsysinfo_win.cpp b/src/libs/kdtools/kdsysinfo_win.cpp
index 465029252..d423ef01e 100644
--- a/src/libs/kdtools/kdsysinfo_win.cpp
+++ b/src/libs/kdtools/kdsysinfo_win.cpp
@@ -66,7 +66,13 @@ QList<ProcessInfo> runningProcesses()
QStringList deviceList;
const DWORD bufferSize = 1024;
char buffer[bufferSize + 1] = { 0 };
- if (QSysInfo::windowsVersion() <= QSysInfo::WV_5_2) {
+
+ // Qt6 does not support Win before 10
+ bool winVerLessEqual5_2 = false;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ winVerLessEqual5_2 = QSysInfo::windowsVersion() <= QSysInfo::WV_5_2;
+#endif
+ if (winVerLessEqual5_2) {
const DWORD size = GetLogicalDriveStringsA(bufferSize, buffer);
deviceList = QString::fromLatin1(buffer, size).split(QLatin1Char(char(0)), Qt::SkipEmptyParts);
}
@@ -85,7 +91,7 @@ QList<ProcessInfo> runningProcesses()
processStruct.dwSize = sizeof(PROCESSENTRY32);
bool foundProcess = Process32First(snapshot, &processStruct);
while (foundProcess) {
- HANDLE procHandle = OpenProcess(QSysInfo::windowsVersion() > QSysInfo::WV_5_2
+ HANDLE procHandle = OpenProcess(!winVerLessEqual5_2
? KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, false, processStruct
.th32ProcessID);
@@ -93,7 +99,7 @@ QList<ProcessInfo> runningProcesses()
QString executablePath;
DWORD bufferSize = 1024;
- if (QSysInfo::windowsVersion() > QSysInfo::WV_5_2) {
+ if (!winVerLessEqual5_2) {
succ = pQueryFullProcessImageNamePtr(procHandle, 0, buffer, &bufferSize);
executablePath = QString::fromLatin1(buffer);
} else if (pGetProcessImageFileNamePtr) {
diff --git a/src/libs/kdtools/localpackagehub.cpp b/src/libs/kdtools/localpackagehub.cpp
index bd6d879e3..c7b627c51 100644
--- a/src/libs/kdtools/localpackagehub.cpp
+++ b/src/libs/kdtools/localpackagehub.cpp
@@ -313,6 +313,7 @@ void LocalPackageHub::refresh()
\a title,
\a treeName,
\a description,
+ \a sortingPriority,
\a dependencies,
\a autoDependencies,
\a forcedInstallation,
diff --git a/src/libs/kdtools/lockfile_win.cpp b/src/libs/kdtools/lockfile_win.cpp
index 20961cc4e..7fc808d39 100644
--- a/src/libs/kdtools/lockfile_win.cpp
+++ b/src/libs/kdtools/lockfile_win.cpp
@@ -45,7 +45,7 @@ bool LockFile::Private::lock()
errorString.clear();
handle = CreateFile(filename.toStdWString().data(), GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, NULL, QFileInfo(filename).exists() ? OPEN_EXISTING : CREATE_NEW,
+ FILE_SHARE_READ, NULL, QFileInfo::exists(filename) ? OPEN_EXISTING : CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle == INVALID_HANDLE_VALUE) {
diff --git a/src/libs/kdtools/sysinfo_x11.cpp b/src/libs/kdtools/sysinfo_x11.cpp
index a59fd150b..24ef099ca 100644
--- a/src/libs/kdtools/sysinfo_x11.cpp
+++ b/src/libs/kdtools/sysinfo_x11.cpp
@@ -40,7 +40,7 @@
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
namespace KDUpdater {
@@ -124,9 +124,9 @@ QList<ProcessInfo> runningProcesses()
QList<ProcessInfo> processes;
QDir procDir(QLatin1String("/proc"));
const QFileInfoList procCont = procDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);
- QRegExp validator(QLatin1String("[0-9]+"));
- Q_FOREACH (const QFileInfo &info, procCont) {
- if (validator.exactMatch(info.fileName())) {
+ static const QRegularExpression validator(QLatin1String("^[0-9]+$"));
+ for (const QFileInfo &info : procCont) {
+ if (validator.match(info.fileName()).hasMatch()) {
const QString linkPath = QDir(info.absoluteFilePath()).absoluteFilePath(QLatin1String("exe"));
const QFileInfo linkInfo(linkPath);
if (linkInfo.exists()) {
diff --git a/src/libs/kdtools/updatefinder.cpp b/src/libs/kdtools/updatefinder.cpp
index b1070c742..dbe7825df 100644
--- a/src/libs/kdtools/updatefinder.cpp
+++ b/src/libs/kdtools/updatefinder.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,7 +39,8 @@
#include <QCoreApplication>
#include <QFileInfo>
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QFutureWatcher>
using namespace KDUpdater;
using namespace QInstaller;
@@ -55,72 +56,107 @@ using namespace QInstaller;
objects.
*/
-//
-// Private
-//
-class UpdateFinder::Private
+
+static int computeProgressPercentage(int min, int max, int percent)
{
-public:
- enum struct Resolution {
- AddPackage,
- KeepExisting,
- RemoveExisting
- };
-
- explicit Private(UpdateFinder *qq)
- : q(qq)
- , cancel(false)
- , downloadCompleteCount(0)
- , m_downloadsToComplete(0)
- {}
-
- ~Private()
- {
- clear();
- }
+ return min + qint64(max-min) * percent / 100;
+}
- struct Data {
- Data()
- : downloader(0) {}
- explicit Data(const PackageSource &i, FileDownloader *d = 0)
- : info(i), downloader(d) {}
+static int computePercent(int done, int total)
+{
+ return total ? done * Q_INT64_C(100) / total : 0 ;
+}
- PackageSource info;
- FileDownloader *downloader;
- };
- UpdateFinder *q;
- QHash<QString, Update *> updates;
+/*!
+ Constructs an update finder.
+*/
+UpdateFinder::UpdateFinder()
+ : Task(QLatin1String("UpdateFinder"), Stoppable)
+ , m_cancel(false)
+ , m_downloadCompleteCount(0)
+ , m_downloadsToComplete(0)
+ , m_updatesXmlTasks(0)
+ , m_updatesXmlTasksToComplete(0)
+{
+}
- // Temporary structure that notes down information about updates.
- bool cancel;
- int downloadCompleteCount;
- int m_downloadsToComplete;
- QHash<UpdatesInfo *, Data> m_updatesInfoList;
+/*!
+ Destructor
+*/
+UpdateFinder::~UpdateFinder()
+{
+ clear();
+}
- void clear();
- void computeUpdates();
- void cancelComputeUpdates();
- bool downloadUpdateXMLFiles();
- bool computeApplicableUpdates();
+/*!
+ Returns a list of KDUpdater::Update objects.
+*/
+QList<Update *> UpdateFinder::updates() const
+{
+ return m_updates.values();
+}
- QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo);
- void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList);
- Resolution checkPriorityAndVersion(const PackageSource &source, const QVariantHash &data) const;
- void slotDownloadDone();
+/*!
+ Sets the information about installed local packages \a hub.
+*/
+void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub)
+{
+ m_localPackageHub = std::move(hub);
+}
- QSet<PackageSource> packageSources;
- std::weak_ptr<LocalPackageHub> m_localPackageHub;
-};
+/*!
+ Sets the package \a sources information when searching for applicable packages.
+*/
+void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources)
+{
+ m_packageSources = sources;
+}
+/*!
+ \internal
-static int computeProgressPercentage(int min, int max, int percent)
+ Implemented from KDUpdater::Task::doRun().
+*/
+void UpdateFinder::doRun()
{
- return min + qint64(max-min) * percent / 100;
+ computeUpdates();
}
-static int computePercent(int done, int total)
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doStop()
{
- return total ? done * Q_INT64_C(100) / total : 0 ;
+ cancelComputeUpdates();
+
+ // Wait until the cancel has actually happened, and then return.
+ // Thinking of using QMutex for this. Frank/Till any suggestions?
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doPause()
+{
+ // Not a pausable task
+ return false;
+}
+
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doResume()
+{
+ // Not a pausable task, hence it is not resumable as well
+ return false;
}
/*!
@@ -128,20 +164,22 @@ static int computePercent(int done, int total)
Releases all internal resources consumed while downloading and computing updates.
*/
-void UpdateFinder::Private::clear()
+void UpdateFinder::clear()
{
- qDeleteAll(updates);
- updates.clear();
+ qDeleteAll(m_updates);
+ m_updates.clear();
const QList<Data> values = m_updatesInfoList.values();
foreach (const Data &data, values)
delete data.downloader;
- qDeleteAll(m_updatesInfoList.keys());
+ qDeleteAll(m_updatesInfoList.keyBegin(), m_updatesInfoList.keyEnd());
m_updatesInfoList.clear();
- downloadCompleteCount = 0;
+ m_downloadCompleteCount = 0;
m_downloadsToComplete = 0;
+ qDeleteAll(m_xmlFileTasks);
+ m_xmlFileTasks.clear();
}
/*!
@@ -164,51 +202,59 @@ void UpdateFinder::Private::clear()
\note Each time this function is called, all the previously computed updates are discarded
and its resources are freed.
*/
-void UpdateFinder::Private::computeUpdates()
+void UpdateFinder::computeUpdates()
{
// Computing updates is done in two stages
// 1. Downloading Update XML files from all the update sources
- // 2. Matching updates with Package XML and figuring out available updates
-
+ // 2. Parse attributes from Update XML documents to UpdateInfoList
+ // 3. Remove all updates with invalid content
+ // 4. Matching updates with Package XML and figuring out available updates
clear();
- cancel = false;
+ m_cancel = false;
// First do some quick sanity checks on the packages info
std::shared_ptr<LocalPackageHub> packages = m_localPackageHub.lock();
if (!packages) {
- q->reportError(tr("Cannot access the package information of this application."));
+ reportError(tr("Cannot access the package information of this application."));
return;
}
if (!packages->isValid()) {
- q->reportError(packages->errorString());
+ reportError(packages->errorString());
return;
}
// Now do some quick sanity checks on the package sources.
- if (packageSources.count() <= 0) {
- q->reportError(tr("No package sources set for this application."));
+ if (m_packageSources.count() <= 0) {
+ reportError(tr("No package sources set for this application."));
return;
}
// Now we can start...
+ if (!downloadUpdateXMLFiles() || m_cancel) {
+ clear();
+ return;
+ }
- // Step 1: 0 - 49 percent
- if (!downloadUpdateXMLFiles() || cancel) {
+ if (!parseUpdateXMLFiles() || m_cancel) {
clear();
return;
}
- // Step 2: 50 - 100 percent
- if (!computeApplicableUpdates() || cancel) {
+ if (!removeInvalidObjects() || m_cancel) {
+ clear();
+ return;
+ }
+
+ if (!computeApplicableUpdates() || m_cancel) {
clear();
return;
}
// All done
- q->reportProgress(100, tr("%n update(s) found.", "", updates.count()));
- q->reportDone();
+ reportProgress(100, tr("%n update(s) found.", "", m_updates.count()));
+ reportDone();
}
/*!
@@ -218,9 +264,9 @@ void UpdateFinder::Private::computeUpdates()
\sa computeUpdates()
*/
-void UpdateFinder::Private::cancelComputeUpdates()
+void UpdateFinder::cancelComputeUpdates()
{
- cancel = true;
+ m_cancel = true;
}
/*!
@@ -239,32 +285,32 @@ void UpdateFinder::Private::cancelComputeUpdates()
The function gets into an event loop until all the downloads are complete.
*/
-bool UpdateFinder::Private::downloadUpdateXMLFiles()
+bool UpdateFinder::downloadUpdateXMLFiles()
{
// create UpdatesInfo for each update source
- foreach (const PackageSource &info, packageSources) {
+ foreach (const PackageSource &info, m_packageSources) {
const QUrl url = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString());
if (url.scheme() != QLatin1String("resource") && url.scheme() != QLatin1String("file")) {
// create FileDownloader (except for local files and resources)
- FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), q);
+ FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), this);
if (!downloader)
break;
downloader->setUrl(url);
downloader->setAutoRemoveDownloadedFile(true);
- connect(downloader, SIGNAL(downloadCanceled()), q, SLOT(slotDownloadDone()));
- connect(downloader, SIGNAL(downloadCompleted()), q, SLOT(slotDownloadDone()));
- connect(downloader, SIGNAL(downloadAborted(QString)), q, SLOT(slotDownloadDone()));
- m_updatesInfoList.insert(new UpdatesInfo, Data(info, downloader));
+ connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadCompleted()), this, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(slotDownloadDone()));
+ m_updatesInfoList.insert(new UpdatesInfo(info.postLoadComponentScript), Data(info, downloader));
} else {
- UpdatesInfo *updatesInfo = new UpdatesInfo;
+ UpdatesInfo *updatesInfo = new UpdatesInfo(info.postLoadComponentScript);
updatesInfo->setFileName(QInstaller::pathFromUrl(url));
m_updatesInfoList.insert(updatesInfo, Data(info));
}
}
// Trigger download of Updates.xml file
- downloadCompleteCount = 0;
+ m_downloadCompleteCount = 0;
m_downloadsToComplete = 0;
foreach (const Data &data, m_updatesInfoList) {
if (data.downloader) {
@@ -274,39 +320,53 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
}
// Wait until all downloaders have completed their downloads.
- while (true) {
- QCoreApplication::processEvents();
- if (cancel)
- return false;
-
- if (downloadCompleteCount == m_downloadsToComplete)
- break;
-
- q->reportProgress(computePercent(downloadCompleteCount, m_downloadsToComplete),
- tr("Downloading Updates.xml from update sources."));
- }
+ return waitForJobToFinish(m_downloadCompleteCount, m_downloadsToComplete);
+}
+/*!
+ \internal
+*/
+bool UpdateFinder::parseUpdateXMLFiles()
+{
// Setup the update info objects with the files from download.
- foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) {
+ m_updatesXmlTasks = 0;
+ m_updatesXmlTasksToComplete = 0;
+ QList<UpdatesInfo *> keys = m_updatesInfoList.keys();
+ for (UpdatesInfo *updatesInfo : qAsConst(keys)) {
const Data data = m_updatesInfoList.value(updatesInfo);
if (data.downloader) {
if (!data.downloader->isDownloaded()) {
- q->reportError(tr("Cannot download package source %1 from \"%2\".").arg(data
- .downloader->url().fileName(), data.info.url.toString()));
+ reportError(tr("Cannot download package source %1 from \"%2\".").arg(data.
+ downloader->url().fileName(), data.info.url.toString()));
} else {
updatesInfo->setFileName(data.downloader->downloadedFileName());
}
}
+ if (!updatesInfo->fileName().isEmpty()) {
+ ParseXmlFilesTask *const task = new ParseXmlFilesTask(updatesInfo);
+ m_xmlFileTasks.append(task);
+ QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
+ m_updatesXmlTasksToComplete++;
+ connect(watcher, &QFutureWatcherBase::finished, this, &UpdateFinder::parseUpdatesXmlTaskFinished);
+ watcher->setFuture(QtConcurrent::run(&ParseXmlFilesTask::doTask, task));
+ }
}
- // Remove all invalid update info objects.
+ // Wait until all updates.xml files are parsed
+ return waitForJobToFinish(m_updatesXmlTasks, m_updatesXmlTasksToComplete);
+}
+/*!
+ \internal
+*/
+bool UpdateFinder::removeInvalidObjects()
+{
QMutableHashIterator<UpdatesInfo *, Data> it(m_updatesInfoList);
while (it.hasNext()) {
UpdatesInfo *info = it.next().key();
if (info->isValid())
continue;
- q->reportError(info->errorString());
+ reportError(info->errorString());
delete info;
it.remove();
}
@@ -314,7 +374,7 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
if (m_updatesInfoList.isEmpty())
return false;
- q->reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
+ reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
return true;
}
@@ -326,36 +386,40 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
KDUpdater::PackagesInfo. Thereby figures out whether an update is applicable for
this application or not.
*/
-bool UpdateFinder::Private::computeApplicableUpdates()
+bool UpdateFinder::computeApplicableUpdates()
{
int i = 0;
- foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) {
+ QList<UpdatesInfo *> keys = m_updatesInfoList.keys();
+ for (UpdatesInfo *updatesInfo : qAsConst(keys)) {
// Fetch updates applicable to this application.
QList<UpdateInfo> updates = applicableUpdates(updatesInfo);
if (!updates.count())
continue;
- if (cancel)
+ if (m_cancel)
return false;
const PackageSource updateSource = m_updatesInfoList.value(updatesInfo).info;
// Create Update objects for updates that have a valid
// UpdateFile
createUpdateObjects(updateSource, updates);
- if (cancel)
+ if (m_cancel)
return false;
// Report progress
- q->reportProgress(computeProgressPercentage(51, 100, computePercent(i,
+ reportProgress(computeProgressPercentage(51, 100, computePercent(i,
m_updatesInfoList.count())), tr("Computing applicable updates."));
++i;
}
- q->reportProgress(99, tr("Application updates computed."));
+ reportProgress(99, tr("Application updates computed."));
return true;
}
-QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesInfo)
+/*!
+ \internal
+*/
+QList<UpdateInfo> UpdateFinder::applicableUpdates(UpdatesInfo *updatesInfo)
{
const QList<UpdateInfo> dummy;
if (!updatesInfo || updatesInfo->updateInfoCount() == 0)
@@ -384,7 +448,10 @@ QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesI
return updatesInfo->updatesInfo();
}
-void UpdateFinder::Private::createUpdateObjects(const PackageSource &source,
+/*!
+ \internal
+*/
+void UpdateFinder::createUpdateObjects(const PackageSource &source,
const QList<UpdateInfo> &updateInfoList)
{
foreach (const UpdateInfo &info, updateInfoList) {
@@ -394,23 +461,24 @@ void UpdateFinder::Private::createUpdateObjects(const PackageSource &source,
const QString name = info.data.value(QLatin1String("Name")).toString();
if (value == Resolution::RemoveExisting)
- delete updates.take(name);
+ delete m_updates.take(name);
// Create and register the update
- updates.insert(name, new Update(source, info));
+ m_updates.insert(name, new Update(source, info));
}
}
-/*
+/*!
+ \internal
If a package of the same name exists, always use the one with the higher
version. If the new package has the same version but a higher
priority, use the new new package, otherwise keep the already existing package.
*/
-UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion(
+UpdateFinder::Resolution UpdateFinder::checkPriorityAndVersion(
const PackageSource &source, const QVariantHash &newPackage) const
{
const QString name = newPackage.value(QLatin1String("Name")).toString();
- if (Update *existingPackage = updates.value(name)) {
+ if (Update *existingPackage = m_updates.value(name)) {
// Bingo, package was previously found elsewhere.
const int match = compareVersion(newPackage.value(QLatin1String("Version")).toString(),
@@ -442,108 +510,51 @@ UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion
return Resolution::AddPackage;
}
-//
-// UpdateFinder
-//
-
-/*!
- Constructs an update finder.
-*/
-UpdateFinder::UpdateFinder()
- : Task(QLatin1String("UpdateFinder"), Stoppable),
- d(new Private(this))
-{
-}
-
-/*!
- Destructor
-*/
-UpdateFinder::~UpdateFinder()
-{
- delete d;
-}
-
-/*!
- Returns a list of KDUpdater::Update objects.
-*/
-QList<Update *> UpdateFinder::updates() const
-{
- return d->updates.values();
-}
-
-/*!
- Sets the information about installed local packages \a hub.
-*/
-void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub)
-{
- d->m_localPackageHub = std::move(hub);
-}
-
-/*!
- Sets the package \a sources information when searching for applicable packages.
-*/
-void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources)
-{
- d->packageSources = sources;
-}
-
-/*!
- \internal
-
- Implemented from KDUpdater::Task::doRun().
-*/
-void UpdateFinder::doRun()
-{
- d->computeUpdates();
-}
-
/*!
\internal
-
- Implemented from KDUpdater::Task::doStop().
*/
-bool UpdateFinder::doStop()
+bool UpdateFinder::waitForJobToFinish(const int &currentCount, const int &totalsCount)
{
- d->cancelComputeUpdates();
+ while (true) {
+ QCoreApplication::processEvents();
+ if (m_cancel)
+ return false;
- // Wait until the cancel has actually happened, and then return.
- // Thinking of using QMutex for this. Frank/Till any suggestions?
+ if (currentCount == totalsCount)
+ break;
+ reportProgress(computePercent(currentCount, totalsCount),
+ tr("Downloading Updates.xml from update sources."));
+ }
return true;
}
-
/*!
\internal
-
- Implemented from KDUpdater::Task::doStop().
*/
-bool UpdateFinder::doPause()
+void UpdateFinder::parseUpdatesXmlTaskFinished()
{
- // Not a pausable task
- return false;
-}
+ ++m_updatesXmlTasks;
-/*!
- \internal
+ int pc = computePercent(m_updatesXmlTasks, m_updatesXmlTasksToComplete);
+ pc = computeProgressPercentage(0, 45, pc);
+ reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
- Implemented from KDUpdater::Task::doStop().
-*/
-bool UpdateFinder::doResume()
-{
- // Not a pausable task, hence it is not resumable as well
- return false;
+ QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender());
+ watcher->waitForFinished();
+ watcher->deleteLater();
}
+
/*!
\internal
*/
-void UpdateFinder::Private::slotDownloadDone()
+void UpdateFinder::slotDownloadDone()
{
- ++downloadCompleteCount;
+ ++m_downloadCompleteCount;
- int pc = computePercent(downloadCompleteCount, m_downloadsToComplete);
+ int pc = computePercent(m_downloadCompleteCount, m_downloadsToComplete);
pc = computeProgressPercentage(0, 45, pc);
- q->reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
+ reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
}
@@ -589,8 +600,9 @@ int KDUpdater::compareVersion(const QString &v1, const QString &v2)
return 0;
// Split version components across ".", "-" or "_"
- QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-|_")));
- QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-|_")));
+ static const QRegularExpression regex(QLatin1String( "\\.|-|_"));
+ QStringList v1_comps = v1.split(regex);
+ QStringList v2_comps = v2.split(regex);
// Check each component of the version
int index = 0;
diff --git a/src/libs/kdtools/updatefinder.h b/src/libs/kdtools/updatefinder.h
index 5a3f50f62..626a700fd 100644
--- a/src/libs/kdtools/updatefinder.h
+++ b/src/libs/kdtools/updatefinder.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,19 +32,68 @@
#include "task.h"
#include "packagesource.h"
+#include "filedownloader.h"
+#include "updatesinfo_p.h"
+#include "abstracttask.h"
#include <memory>
+using namespace QInstaller;
namespace KDUpdater {
class LocalPackageHub;
class Update;
+class ParseXmlFilesTask : public AbstractTask<void>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ParseXmlFilesTask)
+
+public:
+ ParseXmlFilesTask(UpdatesInfo *info)
+ : m_info(info)
+ {}
+
+ void doTask(QFutureInterface<void> &fi) override
+ {
+ fi.reportStarted();
+ fi.setExpectedResultCount(1);
+
+ if (fi.isCanceled()) {
+ fi.reportFinished();
+ return; // ignore already canceled
+ }
+ m_info->parseFile();
+
+ fi.reportFinished();
+ }
+
+private:
+ UpdatesInfo *const m_info;
+};
+
+
class KDTOOLS_EXPORT UpdateFinder : public Task
{
Q_OBJECT
class Private;
+ struct Data {
+ Data()
+ : downloader(0) {}
+ explicit Data(const QInstaller::PackageSource &i, KDUpdater::FileDownloader *d = 0)
+ : info(i), downloader(d) {}
+
+ QInstaller::PackageSource info;
+ KDUpdater::FileDownloader *downloader;
+ };
+
+ enum struct Resolution {
+ AddPackage,
+ KeepExisting,
+ RemoveExisting
+ };
+
public:
UpdateFinder();
~UpdateFinder();
@@ -59,10 +108,35 @@ private:
bool doStop() override;
bool doPause() override;
bool doResume() override;
+ void clear();
+ void computeUpdates();
+ void cancelComputeUpdates();
+ bool downloadUpdateXMLFiles();
+ bool parseUpdateXMLFiles();
+ bool removeInvalidObjects();
+ bool computeApplicableUpdates();
+
+ QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo);
+ void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList);
+ Resolution checkPriorityAndVersion(const QInstaller::PackageSource &source, const QVariantHash &data) const;
+ bool waitForJobToFinish(const int &currentCount, const int &totalsCount);
+
+private slots:
+ void parseUpdatesXmlTaskFinished();
+ void slotDownloadDone();
private:
- Private *d;
- Q_PRIVATE_SLOT(d, void slotDownloadDone())
+ QSet<PackageSource> m_packageSources;
+ std::weak_ptr<LocalPackageHub> m_localPackageHub;
+ QHash<QString, Update *> m_updates;
+
+ bool m_cancel;
+ int m_downloadCompleteCount;
+ int m_downloadsToComplete;
+ QHash<UpdatesInfo *, Data> m_updatesInfoList;
+ int m_updatesXmlTasks;
+ int m_updatesXmlTasksToComplete;
+ QList<ParseXmlFilesTask*> m_xmlFileTasks;
};
} // namespace KDUpdater
diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp
index 35629809e..af89382a8 100644
--- a/src/libs/kdtools/updateoperation.cpp
+++ b/src/libs/kdtools/updateoperation.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -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"));
}
/*!
@@ -321,6 +316,41 @@ void UpdateOperation::setRequiresUnreplacedVariables(bool isRequired)
m_requiresUnreplacedVariables = isRequired;
}
+/*!
+ Replaces installer \c value \a variableValue with predefined variable.
+ If \c key is found for the \a variableValue and the \c key ends with string _OLD,
+ the initial \a variableValue is replaced with the \c value having a key
+ without _OLD ending. This way we can replace the hard coded values defined for operations,
+ if the value has for some reason changed. For example if we set following variables
+ in install script:
+ \badcode
+ installer.setValue("MY_OWN_EXECUTABLE", "C:/Qt/NewLocation/Tools.exe")
+ installer.setValue("MY_OWN_EXECUTABLE_OLD", "C:/Qt/OldLocation/Tools.exe")
+ \endcode
+ and we have moved the Tools.exe from OldLocation to NewLocation, the operation
+ continues to work and use the Tools.exe from NewLocation although original
+ installation has been made with Tools.exe in OldLocation.
+ Returns \c true if \a variableValue is replaced.
+*/
+bool UpdateOperation::variableReplacement(QString *variableValue)
+{
+ bool variableValueChanged = false;
+ const QString valueNormalized = QDir::cleanPath(*variableValue);
+ QString key = m_core->key(valueNormalized);
+ if (key.endsWith(QLatin1String("_OLD"))) {
+ key.chop(4);
+ if (m_core->containsValue(key)) {
+ key.prepend(QLatin1String("@"));
+ key.append(QLatin1String("@"));
+ *variableValue = m_core->replaceVariables(key);
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Running above operation with replaced value: " << valueNormalized
+ << "has been replaced with" << *variableValue;
+ variableValueChanged = true;
+ }
+ }
+ return variableValueChanged;
+}
+
struct StartsWith
{
explicit StartsWith(const QString &searchTerm)
@@ -542,14 +572,21 @@ QDomDocument UpdateOperation::toXml() const
value.setAttribute(QLatin1String("name"), it.key());
value.setAttribute(QLatin1String("type"), QLatin1String(variant.typeName()));
- if (variant.type() != QVariant::List && variant.type() != QVariant::StringList
- && variant.canConvert(QVariant::String)) {
- // it can convert to string? great!
- value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(),
- target, QLatin1String(QInstaller::scRelocatable))));
+ int variantType;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ variantType = variant.typeId();
+#else
+ variantType = variant.type();
+#endif
+
+ if (variantType != QMetaType::QStringList
+ && variant.canConvert<QString>()) {
+ // it can convert to string? great!
+ value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(),
+ target, QLatin1String(QInstaller::scRelocatable))));
} else {
// no? then we have to go the hard way...
- if (variant.type() == QVariant::StringList) {
+ if (variantType == QMetaType::QStringList) {
QStringList list = variant.toStringList();
for (int i = 0; i < list.count(); ++i) {
list[i] = QInstaller::replacePath(list.at(i), target,
@@ -616,25 +653,31 @@ bool UpdateOperation::fromXml(const QDomDocument &doc)
const QString type = v.attribute(QLatin1String("type"));
const QString value = v.text();
+ int variantType;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ const QMetaType t = QMetaType::fromName(type.toLatin1().data());
+ variantType = t.id();
+#else
const QVariant::Type t = QVariant::nameToType(type.toLatin1().data());
- QVariant var = qVariantFromValue(value);
- if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) {
- QDataStream stream(QByteArray::fromBase64( value.toLatin1()));
+ variantType = t;
+#endif
+ QVariant var = QVariant::fromValue(value);
+ if (variantType == QMetaType::QStringList || !var.canConvert(t)) {
+ QDataStream stream(QByteArray::fromBase64(value.toLatin1()));
stream >> var;
- if (t == QVariant::StringList) {
+ if (variantType == QMetaType::QStringList) {
QStringList list = var.toStringList();
for (int i = 0; i < list.count(); ++i) {
list[i] = QInstaller::replacePath(list.at(i),
- relocatable, target);
+ relocatable, target);
}
var = QVariant::fromValue(list);
}
- } else if (t == QVariant::String) {
- const QString str = QInstaller::replacePath(value,
- relocatable, target);
- var = QVariant::fromValue(str);
+ } else if (variantType == QMetaType::QString) {
+ const QString str = QInstaller::replacePath(value,
+ relocatable, target);
+ var = QVariant::fromValue(str);
}
-
m_values[name] = var;
}
diff --git a/src/libs/kdtools/updateoperation.h b/src/libs/kdtools/updateoperation.h
index a39c25747..e25846cd3 100644
--- a/src/libs/kdtools/updateoperation.h
+++ b/src/libs/kdtools/updateoperation.h
@@ -112,8 +112,9 @@ 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);
private:
QString m_name;
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/libs/kdtools/updatesinfo.cpp b/src/libs/kdtools/updatesinfo.cpp
index 545931a55..e82c1c1fb 100644
--- a/src/libs/kdtools/updatesinfo.cpp
+++ b/src/libs/kdtools/updatesinfo.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,18 +29,20 @@
#include "updatesinfo_p.h"
#include "utils.h"
+#include "constants.h"
-#include <QDomDocument>
#include <QFile>
#include <QLocale>
#include <QPair>
#include <QVector>
#include <QUrl>
+#include <QXmlStreamReader>
using namespace KDUpdater;
-UpdatesInfoData::UpdatesInfoData()
+UpdatesInfoData::UpdatesInfoData(const bool postLoadComponentScript)
: error(UpdatesInfo::NotYetReadError)
+ , m_postLoadComponentScript(postLoadComponentScript)
{
}
@@ -62,35 +65,26 @@ void UpdatesInfoData::parseFile(const QString &updateXmlFile)
return;
}
- QDomDocument doc;
- QString parseErrorMessage;
- int parseErrorLine, parseErrorColumn;
- if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) {
- error = UpdatesInfo::InvalidXmlError;
- errorMessage = tr("Parse error in %1 at %2, %3: %4").arg(updateXmlFile,
- QString::number(parseErrorLine), QString::number(parseErrorColumn), parseErrorMessage);
- return;
- }
-
- QDomElement rootE = doc.documentElement();
- if (rootE.tagName() != QLatin1String("Updates")) {
- setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(rootE.tagName()));
- return;
- }
-
- QDomNodeList childNodes = rootE.childNodes();
- for(int i = 0; i < childNodes.count(); i++) {
- const QDomElement childE = childNodes.at(i).toElement();
- if (childE.isNull())
- continue;
-
- if (childE.tagName() == QLatin1String("ApplicationName"))
- applicationName = childE.text();
- else if (childE.tagName() == QLatin1String("ApplicationVersion"))
- applicationVersion = childE.text();
- else if (childE.tagName() == QLatin1String("PackageUpdate")) {
- if (!parsePackageUpdateElement(childE))
- return; //error handled in subroutine
+ QXmlStreamReader reader(&file);
+ if (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Updates")) {
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("ApplicationName")) {
+ applicationName = reader.readElementText();
+ } else if (reader.name() == QLatin1String("ApplicationVersion")) {
+ applicationVersion = reader.readElementText();
+ } else if (reader.name() == QLatin1String("Checksum")) {
+ checkSha1CheckSum = (reader.readElementText());
+ } else if (reader.name() == QLatin1String("PackageUpdate")) {
+ if (!parsePackageUpdateElement(reader, checkSha1CheckSum))
+ return; //error handled in subroutine
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+ } else {
+ setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(reader.name()));
+ return;
}
}
@@ -108,74 +102,58 @@ void UpdatesInfoData::parseFile(const QString &updateXmlFile)
error = UpdatesInfo::NoError;
}
-bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
+bool UpdatesInfoData::parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum)
{
- if (updateE.isNull())
- return false;
-
UpdateInfo info;
- QMap<QString, QString> localizedDescriptions;
- for (int i = 0; i < updateE.childNodes().count(); i++) {
- QDomElement childE = updateE.childNodes().at(i).toElement();
- if (childE.isNull())
+ QHash<QString, QVariant> scriptHash;
+ while (reader.readNext()) {
+ const QString elementName = reader.name().toString();
+ if ((reader.name() == QLatin1String("PackageUpdate"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (elementName.isEmpty() || reader.tokenType() == QXmlStreamReader::EndElement)
continue;
-
- if (childE.tagName() == QLatin1String("ReleaseNotes")) {
- info.data[childE.tagName()] = QUrl(childE.text());
- } else if (childE.tagName() == QLatin1String("Licenses")) {
- QHash<QString, QVariant> licenseHash;
- const QDomNodeList licenseNodes = childE.childNodes();
- for (int index = 0; index < licenseNodes.count(); ++index) {
- const QDomNode licenseNode = licenseNodes.at(index);
- if (licenseNode.nodeName() == QLatin1String("License")) {
- QDomElement element = licenseNode.toElement();
- QVariantMap attributes;
- attributes.insert(QLatin1String("file"), element.attributeNode(QLatin1String("file")).value());
- if (!element.attributeNode(QLatin1String("priority")).isNull())
- attributes.insert(QLatin1String("priority"), element.attributeNode(QLatin1String("priority")).value());
- else
- attributes.insert(QLatin1String("priority"), QLatin1String("0"));
- licenseHash.insert(element.attributeNode(QLatin1String("name")).value(), attributes);
- }
- }
- if (!licenseHash.isEmpty())
- info.data.insert(QLatin1String("Licenses"), licenseHash);
- } else if (childE.tagName() == QLatin1String("TreeName")) {
- const bool moveChildren = QVariant(childE.attribute(QLatin1String("moveChildren"))).toBool();
- const QPair<QString, bool> treeNamePair(childE.text(), moveChildren);
+ if (elementName == QLatin1String("Licenses")) {
+ parseLicenses(reader, info.data);
+ } else if (elementName == QLatin1String("TreeName")) {
+ const QXmlStreamAttributes attr = reader.attributes();
+ const bool moveChildren = attr.value(QLatin1String("moveChildren")).toString().toLower() == QInstaller::scTrue ? true : false;
+ const QPair<QString, bool> treeNamePair(reader.readElementText(), moveChildren);
info.data.insert(QLatin1String("TreeName"), QVariant::fromValue(treeNamePair));
- } else if (childE.tagName() == QLatin1String("Version")) {
+ } else if (elementName == QLatin1String("Version")) {
+ const QXmlStreamAttributes attr = reader.attributes();
info.data.insert(QLatin1String("inheritVersionFrom"),
- childE.attribute(QLatin1String("inheritVersionFrom")));
- info.data[childE.tagName()] = childE.text();
- } else if (childE.tagName() == QLatin1String("DisplayName")) {
- processLocalizedTag(childE, info.data);
- } else if (childE.tagName() == QLatin1String("Description")) {
- if (!childE.hasAttribute(QLatin1String("xml:lang")))
- info.data[QLatin1String("Description")] = childE.text();
- QString languageAttribute = childE.attribute(QLatin1String("xml:lang"), QLatin1String("en"));
- localizedDescriptions.insert(languageAttribute.toLower(), childE.text());
- } else if (childE.tagName() == QLatin1String("UpdateFile")) {
- info.data[QLatin1String("CompressedSize")] = childE.attribute(QLatin1String("CompressedSize"));
- info.data[QLatin1String("UncompressedSize")] = childE.attribute(QLatin1String("UncompressedSize"));
- } else if (childE.tagName() == QLatin1String("Operations")) {
- const QDomNodeList operationNodes = childE.childNodes();
- QVariant operationListVariant = parseOperations(childE.childNodes());
- info.data.insert(QLatin1String("Operations"), operationListVariant);
+ attr.value(QLatin1String("inheritVersionFrom")).toString());
+ info.data[elementName] = reader.readElementText();
+ } else if (elementName == QLatin1String("DisplayName")
+ || elementName == QLatin1String("Description")) {
+ processLocalizedTag(reader, info.data);
+ } else if (elementName == QLatin1String("UpdateFile")) {
+ info.data[QLatin1String("CompressedSize")] = reader.attributes().value(QLatin1String("CompressedSize")).toString();
+ info.data[QLatin1String("UncompressedSize")] = reader.attributes().value(QLatin1String("UncompressedSize")).toString();
+ } else if (elementName == QLatin1String("Operations")) {
+ parseOperations(reader, info.data);
+ } else if (elementName == QLatin1String("Script")) {
+ const QXmlStreamAttributes attr = reader.attributes();
+ bool postLoad = false;
+ // postLoad can be set either to individual components or to whole repositories.
+ // If individual components has the postLoad attribute, it overwrites the repository value.
+ if (attr.hasAttribute(QLatin1String("postLoad")))
+ postLoad = attr.value(QLatin1String("postLoad")).toString().toLower() == QInstaller::scTrue ? true : false;
+ else if (m_postLoadComponentScript)
+ postLoad = true;
+
+ if (postLoad)
+ scriptHash.insert(QLatin1String("postLoadScript"), reader.readElementText());
+ else
+ scriptHash.insert(QLatin1String("installScript"), reader.readElementText());
} else {
- info.data[childE.tagName()] = childE.text();
- }
- }
-
- QStringList candidates;
- foreach (const QString &lang, QLocale().uiLanguages())
- candidates << QInstaller::localeCandidates(lang.toLower());
- foreach (const QString &candidate, candidates) {
- if (localizedDescriptions.contains(candidate)) {
- info.data[QLatin1String("Description")] = localizedDescriptions.value(candidate);
- break;
+ info.data[elementName] = reader.readElementText();
}
}
+ if (!scriptHash.isEmpty())
+ info.data.insert(QLatin1String("Script"), scriptHash);
if (!info.data.contains(QLatin1String("Name"))) {
setInvalidContentError(tr("PackageUpdate element without Name"));
@@ -189,54 +167,87 @@ bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
setInvalidContentError(tr("PackageUpdate element without ReleaseDate"));
return false;
}
-
+ info.data[QLatin1String("CheckSha1CheckSum")] = checkSha1CheckSum;
updateInfoList.append(info);
return true;
}
-void UpdatesInfoData::processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const
+void UpdatesInfoData::processLocalizedTag(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
{
- QString languageAttribute = childE.attribute(QLatin1String("xml:lang")).toLower();
- if (!info.contains(childE.tagName()) && (languageAttribute.isEmpty()))
- info[childE.tagName()] = childE.text();
+ const QString languageAttribute = reader.attributes().value(QLatin1String("xml:lang")).toString().toLower();
+ const QString elementName = reader.name().toString();
+ if (!info.contains(elementName) && (languageAttribute.isEmpty()))
+ info[elementName] = reader.readElementText();
+ if (languageAttribute.isEmpty())
+ return;
// overwrite default if we have a language specific description
if (QLocale().name().startsWith(languageAttribute, Qt::CaseInsensitive))
- info[childE.tagName()] = childE.text();
+ info[elementName] = reader.readElementText();
}
-QVariant UpdatesInfoData::parseOperations(const QDomNodeList &operationNodes)
+void UpdatesInfoData::parseOperations(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
{
- QVariant operationListVariant;
QList<QPair<QString, QVariant>> operationsList;
- for (int i = 0; i < operationNodes.count(); ++i) {
- const QDomNode operationNode = operationNodes.at(i);
- if (operationNode.nodeName() == QLatin1String("Operation")) {
- const QDomNodeList argumentNodes = operationNode.childNodes();
- QStringList attributes;
- for (int index = 0; index < argumentNodes.count(); ++index) {
- const QDomNode argumentNode = argumentNodes.at(index);
- if (argumentNode.nodeName() == QLatin1String("Argument")) {
- QDomElement argumentElement = argumentNode.toElement();
- attributes.append(argumentElement.text());
- }
+ while (reader.readNext()) {
+ const QString subElementName = reader.name().toString();
+ // End of parsing operations
+ if ((subElementName == QLatin1String("Operations"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName != QLatin1String("Operation") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ QStringList attributes;
+ const QXmlStreamAttributes attr = reader.attributes();
+ while (reader.readNext()) {
+ const QString subElementName2 = reader.name().toString();
+ // End of parsing single operation
+ if ((subElementName2 == QLatin1String("Operation"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
}
- QPair<QString, QVariant> pair;
- pair.first = operationNode.toElement().attributeNode(QLatin1String("name")).value();
- pair.second = attributes;
- operationsList.append(pair);
+ if (subElementName2 != QLatin1String("Argument") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ attributes.append(reader.readElementText());
}
+ QPair<QString, QVariant> pair;
+ pair.first = attr.value(QLatin1String("name")).toString();
+ pair.second = attributes;
+ operationsList.append(pair);
}
- operationListVariant.setValue(operationsList);
- return operationListVariant;
+ info.insert(QLatin1String("Operations"), QVariant::fromValue(operationsList));
}
-
+void UpdatesInfoData::parseLicenses(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
+{
+ QHash<QString, QVariant> licenseHash;
+ while (reader.readNext()) {
+ const QString subElementName = reader.name().toString();
+ // End of parsing Licenses
+ if ((subElementName == QLatin1String("Licenses"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName != QLatin1String("License") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ const QXmlStreamAttributes attr = reader.attributes();
+ QVariantMap attributes;
+ attributes.insert(QLatin1String("file"), attr.value(QLatin1String("file")).toString());
+ if (!attr.value(QLatin1String("priority")).isNull())
+ attributes.insert(QLatin1String("priority"), attr.value(QLatin1String("priority")).toString());
+ else
+ attributes.insert(QLatin1String("priority"), QLatin1String("0"));
+ licenseHash.insert(attr.value(QLatin1String("name")).toString(), attributes);
+ }
+ if (!licenseHash.isEmpty())
+ info.insert(QLatin1String("Licenses"), licenseHash);
+}
//
// UpdatesInfo
//
-UpdatesInfo::UpdatesInfo()
- : d(new UpdatesInfoData)
+UpdatesInfo::UpdatesInfo(const bool postLoadComponentScript)
+ : d(new UpdatesInfoData(postLoadComponentScript))
{
}
@@ -264,6 +275,10 @@ void UpdatesInfo::setFileName(const QString &updateXmlFile)
d->updateInfoList.clear();
d->updateXmlFile = updateXmlFile;
+}
+
+void UpdatesInfo::parseFile()
+{
d->parseFile(d->updateXmlFile);
}
@@ -282,6 +297,11 @@ QString UpdatesInfo::applicationVersion() const
return d->applicationVersion;
}
+QString UpdatesInfo::checkSha1CheckSum() const
+{
+ return d->checkSha1CheckSum;
+}
+
int UpdatesInfo::updateInfoCount() const
{
return d->updateInfoList.count();
diff --git a/src/libs/kdtools/updatesinfo_p.h b/src/libs/kdtools/updatesinfo_p.h
index 93e2fe8c6..a3768232a 100644
--- a/src/libs/kdtools/updatesinfo_p.h
+++ b/src/libs/kdtools/updatesinfo_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,7 +59,7 @@ public:
InvalidContentError
};
- UpdatesInfo();
+ UpdatesInfo(const bool postLoadComponentScript = false);
~UpdatesInfo();
bool isValid() const;
@@ -68,10 +69,13 @@ public:
QString fileName() const;
void setFileName(const QString &updateXmlFile);
+ void parseFile();
QString applicationName() const;
QString applicationVersion() const;
+ QString checkSha1CheckSum() const;
+
int updateInfoCount() const;
UpdateInfo updateInfo(int index) const;
QList<UpdateInfo> updatesInfo() const;
diff --git a/src/libs/kdtools/updatesinfodata_p.h b/src/libs/kdtools/updatesinfodata_p.h
index 07da6fcf0..c71a85193 100644
--- a/src/libs/kdtools/updatesinfodata_p.h
+++ b/src/libs/kdtools/updatesinfodata_p.h
@@ -32,8 +32,7 @@
#include <QCoreApplication>
#include <QSharedData>
-QT_FORWARD_DECLARE_CLASS(QDomElement)
-QT_FORWARD_DECLARE_CLASS(QDomNodeList)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
namespace KDUpdater {
@@ -44,7 +43,7 @@ struct UpdatesInfoData : public QSharedData
Q_DECLARE_TR_FUNCTIONS(KDUpdater::UpdatesInfoData)
public:
- UpdatesInfoData();
+ UpdatesInfoData(const bool postLoadComponentScript);
~UpdatesInfoData();
int error;
@@ -52,16 +51,19 @@ public:
QString updateXmlFile;
QString applicationName;
QString applicationVersion;
+ QString checkSha1CheckSum;
QList<UpdateInfo> updateInfoList;
+ bool m_postLoadComponentScript;
void parseFile(const QString &updateXmlFile);
- bool parsePackageUpdateElement(const QDomElement &updateE);
+ bool parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum);
void setInvalidContentError(const QString &detail);
private:
- void processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const;
- QVariant parseOperations(const QDomNodeList &operationNodes);
+ void processLocalizedTag(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
+ void parseOperations(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
+ void parseLicenses(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
};
} // namespace KDUpdater
diff --git a/src/sdk/commandlineinterface.cpp b/src/sdk/commandlineinterface.cpp
index 63deaf4d9..44f028fd5 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(
@@ -96,20 +98,15 @@ int CommandLineInterface::checkUpdates()
qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot check updates with installer.";
return EXIT_FAILURE;
}
- m_core->setUpdater();
- if (!m_core->fetchRemotePackagesTree()) {
- qCWarning(QInstaller::lcInstallerInstallLog) << m_core->error();
- return EXIT_FAILURE;
- }
-
- const QList<QInstaller::Component *> components =
- m_core->components(QInstaller::PackageManagerCore::ComponentType::Root);
- if (components.isEmpty()) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "There are currently no updates available.";
+ try {
+ if (m_core->searchAvailableUpdates() != QInstaller::PackageManagerCore::Success) {
+ return EXIT_FAILURE;
+ }
return EXIT_SUCCESS;
+ } catch (const QInstaller::Error &err) {
+ qCCritical(QInstaller::lcInstallerInstallLog) << err.message();
+ return EXIT_FAILURE;
}
- QInstaller::LoggingHandler::instance().printUpdateInformation(components);
- return EXIT_SUCCESS;
}
int CommandLineInterface::listInstalledPackages()
@@ -139,7 +136,19 @@ int CommandLineInterface::searchAvailablePackages()
QString regexp;
if (!m_positionalArguments.isEmpty())
regexp = m_positionalArguments.first();
- m_core->listAvailablePackages(regexp, parsePackageFilters());
+
+ if (m_parser.isSet(CommandLineOptions::scTypeLong)) {
+ // If type is specified, only list relevant contents
+ if (m_parser.value(CommandLineOptions::scTypeLong) == QLatin1String("package"))
+ m_core->listAvailablePackages(regexp, parsePackageFilters());
+ else if (m_parser.value(CommandLineOptions::scTypeLong) == QLatin1String("alias"))
+ m_core->listAvailableAliases(regexp);
+ } else {
+ // No type - we can try again with packages search if there were no matching aliases
+ if (!m_core->listAvailableAliases(regexp))
+ m_core->listAvailablePackages(regexp, parsePackageFilters());
+ }
+
return EXIT_SUCCESS;
}
@@ -248,6 +257,18 @@ int CommandLineInterface::createOfflineInstaller()
}
}
+int CommandLineInterface::clearLocalCache()
+{
+ if (!initialize())
+ return EXIT_FAILURE;
+
+ if (!m_core->clearLocalCache())
+ return EXIT_FAILURE;
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Cache cleared successfully!";
+ return EXIT_SUCCESS;
+}
+
bool CommandLineInterface::checkLicense()
{
const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance();
@@ -291,7 +312,7 @@ QHash<QString, QString> CommandLineInterface::parsePackageFilters()
const QString element = filter.left(i).trimmed();
const QString value = filter.mid(i + 1).trimmed();
- if ((i == -1) || (filter.count(QLatin1Char('=') > 1))
+ if ((i == -1) || (filter.count(QLatin1Char('=')) > 1)
|| element.isEmpty() || value.isEmpty()) {
qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Ignoring unknown entry "
<< filter << "in package filter arguments. Please use syntax \"element=regex,...\".";
diff --git a/src/sdk/commandlineinterface.h b/src/sdk/commandlineinterface.h
index 29bae74a4..aeaca780f 100644
--- a/src/sdk/commandlineinterface.h
+++ b/src/sdk/commandlineinterface.h
@@ -49,6 +49,7 @@ public:
int uninstallPackages();
int removeInstallation();
int createOfflineInstaller();
+ int clearLocalCache();
private:
bool initialize();
diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp
index 32df0b550..ac2c9d367 100644
--- a/src/sdk/installerbase.cpp
+++ b/src/sdk/installerbase.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.
@@ -97,18 +97,18 @@ int InstallerBase::run()
if (status != QInstaller::PackageManagerCore::Success)
return status;
+ m_core->saveGivenArguments(m_parser.optionNames());
+
#ifdef ENABLE_SQUISH
- int squishPort = 11233;
if (m_parser.isSet(CommandLineOptions::scSquishPortLong)) {
- squishPort = m_parser.value(CommandLineOptions::scSquishPortLong).toInt();
- }
- if (squishPort != 0) {
- if (Squish::allowAttaching(squishPort))
- qCDebug(QInstaller::lcDeveloperBuild) << "Attaching to squish port " << squishPort << " succeeded";
- else
- qCDebug(QInstaller::lcDeveloperBuild) << "Attaching to squish failed.";
- } else {
- qCWarning(QInstaller::lcDeveloperBuild) << "Invalid squish port number: " << squishPort;
+ const int maxSquishPortNumber = 65535;
+ int squishPort = m_parser.value(CommandLineOptions::scSquishPortLong).toInt();
+ if (squishPort <= 0 || squishPort > maxSquishPortNumber) {
+ qWarning().noquote() << "Invalid Squish port:" << squishPort;
+ } else {
+ bool attachSucceeded = Squish::allowAttaching(squishPort);
+ qCDebug(QInstaller::lcDeveloperBuild) << "Attach to squish port" << squishPort << "succeeded: "<<attachSucceeded;
+ }
}
#endif
const int result = QCoreApplication::instance()->exec();
diff --git a/src/sdk/main.cpp b/src/sdk/main.cpp
index a1e330299..abfc9dc5a 100644
--- a/src/sdk/main.cpp
+++ b/src/sdk/main.cpp
@@ -48,10 +48,13 @@
#include <QCommandLineParser>
#include <QDateTime>
#include <QNetworkProxyFactory>
+#include <QThread>
+#include <QThreadPool>
+#include <QDeadlineTimer>
#include <iostream>
-#if defined(Q_OS_MACOS) or defined(Q_OS_UNIX)
+#if defined(Q_OS_MACOS) || defined(Q_OS_UNIX)
# include <unistd.h>
# include <sys/types.h>
#endif
@@ -63,8 +66,47 @@
#define SHA "Installer Framework SHA1: " QUOTE(_GIT_SHA1_)
static const char PLACEHOLDER[32] = "MY_InstallerCreateDateTime_MY";
+#ifdef Q_OS_WIN
+static void cleanupUpdate(const CommandLineParser &parser, bool *exit)
+{
+ QString cleanupPath;
+ QString cleanupOption;
+ *exit = false;
+
+ if (parser.isSet(CommandLineOptions::scCleanupUpdate)) {
+ cleanupPath = parser.value(CommandLineOptions::scCleanupUpdate);
+ cleanupOption = CommandLineOptions::scCleanupUpdate;
+ } else if (parser.isSet(CommandLineOptions::scCleanupUpdateOnly)) {
+ cleanupPath = parser.value(CommandLineOptions::scCleanupUpdateOnly);
+ cleanupOption = CommandLineOptions::scCleanupUpdateOnly;
+ *exit = true;
+ }
+
+ if (cleanupOption.isEmpty())
+ return;
+
+ // Since Windows does not support that the maintenance tool deletes itself we
+ // remove the old executable here after update (as the new maintenance tool).
+ if (!cleanupPath.isEmpty()) {
+ QFile fileToRemove(cleanupPath);
+ // Give up after 120 seconds if the old process has not exited and released the file
+ QDeadlineTimer deadline(120000);
+ while (fileToRemove.exists() && !deadline.hasExpired()) {
+ if (fileToRemove.remove()) {
+ std::cout << "Removed leftover file: " << qPrintable(cleanupPath)
+ << " after update." << std::endl;
+ }
+ QThread::msleep(1000);
+ }
+ } else {
+ std::cout << "Invalid value for option " << qPrintable(cleanupOption);
+ }
+}
+#endif
+
int main(int argc, char *argv[])
{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (!qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR")
&& !qEnvironmentVariableIsSet("QT_SCALE_FACTOR")
&& !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) {
@@ -73,6 +115,7 @@ int main(int argc, char *argv[])
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
+#endif
// increase maximum numbers of file descriptors
#if defined(Q_OS_MACOS)
QCoreApplication::setSetuidAllowed(true);
@@ -82,6 +125,13 @@ int main(int argc, char *argv[])
setrlimit(RLIMIT_NOFILE, &rl);
#endif
+ // Try to avoid running into situations where the application would hang due to "nested" blocking
+ // usage of the global threadpool that has only one thread available, i.e. main thread invokes
+ // QtConcurrent::run(&myFunction) and myFunction() calls QtConcurrent::blockingFiltered()
+ // for a container.
+ if (QThread::idealThreadCount() == 1)
+ QThreadPool::globalInstance()->setMaxThreadCount(2);
+
// We need to start either a command line application or a GUI application. Since we
// fail doing so at least on Linux while parsing the argument using a core application
// object and later starting the GUI application, we now parse the arguments first.
@@ -159,6 +209,15 @@ int main(int argc, char *argv[])
return help ? EXIT_SUCCESS : EXIT_FAILURE;
}
+#ifdef Q_OS_WIN
+ {
+ bool exit = false;
+ cleanupUpdate(parser, &exit);
+ if (exit)
+ return EXIT_SUCCESS;
+ }
+#endif
+
if (parser.isSet(CommandLineOptions::scStartServerLong)) {
const QStringList arguments = parser.value(CommandLineOptions::scStartServerLong)
.split(QLatin1Char(','), Qt::SkipEmptyParts);
@@ -282,6 +341,9 @@ int main(int argc, char *argv[])
} else if (parser.positionalArguments().contains(CommandLineOptions::scCreateOfflineShort)
|| parser.positionalArguments().contains(CommandLineOptions::scCreateOfflineLong)) {
return CommandLineInterface(argc, argv).createOfflineInstaller();
+ } else if (parser.positionalArguments().contains(CommandLineOptions::scClearCacheShort)
+ || parser.positionalArguments().contains(CommandLineOptions::scClearCacheLong)) {
+ return CommandLineInterface(argc, argv).clearLocalCache();
}
if (QInstaller::LoggingHandler::instance().isVerbose()) {
std::cout << VERSION << std::endl << BUILDDATE << std::endl << SHA << std::endl;
diff --git a/src/sdk/sdk.pro b/src/sdk/sdk.pro
index 4a47fb825..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) {
@@ -104,7 +106,7 @@ HEADERS += \
installerbase.h \
aboutapplicationdialog.h
-SOURCES = \
+SOURCES += \
main.cpp \
installerbase.cpp \
tabcontroller.cpp \
diff --git a/src/sdk/sdkapp.h b/src/sdk/sdkapp.h
index 8a9423edc..eef0110ec 100644
--- a/src/sdk/sdkapp.h
+++ b/src/sdk/sdkapp.h
@@ -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.
@@ -60,6 +60,10 @@
#include <QMetaEnum>
#include <QTranslator>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QNetworkInformation>
+#endif
+
template<class T>
class SDKApp : public T
{
@@ -71,6 +75,9 @@ public:
, m_core(nullptr)
{
m_parser.parse(QCoreApplication::arguments());
+#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
+ QNetworkInformation::loadDefaultBackend();
+#endif
}
virtual ~SDKApp()
@@ -83,6 +90,8 @@ public:
{
try {
return T::notify(receiver, event);
+ } catch (QInstaller::Error &e) {
+ qFatal("Exception thrown: %s", qPrintable(e.message()));
} catch (std::exception &e) {
qFatal("Exception thrown: %s", e.what());
} catch (...) {
@@ -107,14 +116,9 @@ public:
}
binary.close();
#endif
- QString fileName = datFile(binaryFile());
- quint64 cookie = QInstaller::BinaryContent::MagicCookieDat;
- if (fileName.isEmpty()) {
- fileName = binaryFile();
- cookie = QInstaller::BinaryContent::MagicCookie;
- }
-
- binary.setFileName(fileName);
+ QString datFileName = datFile(binaryFile());
+ quint64 cookie = datFileName.isEmpty() ? QInstaller::BinaryContent::MagicCookie : QInstaller::BinaryContent::MagicCookieDat;
+ binary.setFileName(!datFileName.isEmpty() ? datFileName : binaryFile());
QInstaller::openForRead(&binary);
qint64 magicMarker;
@@ -161,7 +165,7 @@ public:
}
QLoggingCategory::setFilterRules(loggingRules);
qCDebug(QInstaller::lcInstallerInstallLog).noquote() << "Arguments:" <<
- QCoreApplication::arguments().join(QLatin1String(", "));
+ m_parser.arguments().join(QLatin1String(", "));
for (auto &optionName : m_parser.optionNames()) {
if (isCommandLineInterface)
@@ -183,12 +187,12 @@ public:
const QStringList arguments = m_parser.value(CommandLineOptions::scStartClientLong)
.split(QLatin1Char(','), Qt::SkipEmptyParts);
m_core = new QInstaller::PackageManagerCore(
- magicMarker, oldOperations,
+ magicMarker, oldOperations, datFileName,
arguments.value(0, QLatin1String(QInstaller::Protocol::DefaultSocket)),
arguments.value(1, QLatin1String(QInstaller::Protocol::DefaultAuthorizationKey)),
QInstaller::Protocol::Mode::Debug, userArgs, isCommandLineInterface);
} else {
- m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations,
+ m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations, datFileName,
QUuid::createUuid().toString(), QUuid::createUuid().toString(),
QInstaller::Protocol::Mode::Production, userArgs, isCommandLineInterface);
}
@@ -203,8 +207,9 @@ public:
const QStringList translations = m_core->settings().translations();
if (translations.isEmpty()) {
- foreach (const QLocale locale, QLocale().uiLanguages()) {
- QScopedPointer<QTranslator> qtTranslator(new QTranslator(QCoreApplication::instance()));
+ for (const QString &language : QLocale().uiLanguages()) {
+ const QLocale locale(language);
+ std::unique_ptr<QTranslator> qtTranslator(new QTranslator(QCoreApplication::instance()));
bool qtLoaded = qtTranslator->load(locale, QLatin1String("qt"),
QLatin1String("_"), newDirectory);
if (!qtLoaded)
@@ -213,26 +218,33 @@ public:
if (qtLoaded || locale.language() == QLocale::English) {
if (qtLoaded)
- QCoreApplication::instance()->installTranslator(qtTranslator.take());
+ QCoreApplication::instance()->installTranslator(qtTranslator.release());
- QScopedPointer<QTranslator> ifwTranslator(new QTranslator(QCoreApplication::instance()));
+ std::unique_ptr <QTranslator> ifwTranslator(new QTranslator(QCoreApplication::instance()));
bool ifwLoaded = ifwTranslator->load(locale, QLatin1String("ifw"), QLatin1String("_"), newDirectory);
if (!ifwLoaded)
ifwLoaded = ifwTranslator->load(locale, QLatin1String("ifw"), QLatin1String("_"), directory);
- if (ifwLoaded)
- QCoreApplication::instance()->installTranslator(ifwTranslator.take());
+ if (ifwLoaded) {
+ QCoreApplication::instance()->installTranslator(ifwTranslator.release());
+ } else {
+ qCWarning(QInstaller::lcDeveloperBuild) << "Could not load IFW translation for language"
+ << QLocale::languageToString(locale.language());
+ }
// To stop loading other translations it's sufficient that
// qt was loaded successfully or we hit English as system language
lang = locale;
break;
+ } else {
+ qCWarning(QInstaller::lcDeveloperBuild) << "Could not load Qt translation for language"
+ << QLocale::languageToString(locale.language());
}
}
} else {
foreach (const QString &translation, translations) {
- QScopedPointer<QTranslator> translator(new QTranslator(QCoreApplication::instance()));
+ std::unique_ptr<QTranslator> translator(new QTranslator(QCoreApplication::instance()));
if (translator->load(translation, QLatin1String(":/translations")))
- QCoreApplication::instance()->installTranslator(translator.take());
+ QCoreApplication::instance()->installTranslator(translator.release());
}
QLocale currentLocale(translations.at(0).section(QLatin1Char('_'), 1));
lang = currentLocale;
@@ -262,6 +274,16 @@ public:
KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory());
}
+ if (m_parser.isSet(CommandLineOptions::scLocalCachePathLong)) {
+ const QString cachePath = m_parser.value(CommandLineOptions::scLocalCachePathLong);
+ if (cachePath.isEmpty()) {
+ errorMessage = QObject::tr("Empty value for option 'cache-path'.");
+ return false;
+ }
+ m_core->settings().setLocalCachePath(cachePath);
+ }
+ m_core->resetLocalCache(true);
+
if (m_parser.isSet(CommandLineOptions::scShowVirtualComponentsLong))
QInstaller::PackageManagerCore::setVirtualComponentsVisible(true);
@@ -406,7 +428,7 @@ public:
}
m_core->setValue(QInstaller::scUILanguage, lang.name());
- emit m_core->defaultTranslationsLoadedForLanguage(lang.language());
+ emit m_core->defaultTranslationsLoadedForLanguage(lang);
ProductKeyCheck::instance()->addPackagesFromXml(QLatin1String(":/metadata/Updates.xml"));
return true;
@@ -460,13 +482,39 @@ public:
if (magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) {
QFileInfo fi(binaryFile);
QString bundlePath;
+ QString datFileName;
if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath))
fi.setFile(bundlePath);
#ifdef Q_OS_MACOS
- return fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat"));
+ datFileName = fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat"));
#else
- return fi.absoluteDir().filePath(qApp->applicationName() + QLatin1String(".dat"));
+ datFileName = fi.absoluteDir().filePath(qApp->applicationName() + QLatin1String(".dat"));
#endif
+ // When running maintenance tool, datFile name should be the same as the application name.
+ // In case we have updated maintenance tool in previous maintenance tool run, the datFile
+ // name may not match if the maintenance tool name has changed. In that case try to
+ // look for the dat file from the root folder of the install.
+ if (!QFileInfo::exists(datFileName)) {
+ QFileInfo fi(datFileName);
+ QDirIterator it(fi.absolutePath(),
+ QStringList() << QLatin1String("*.dat"),
+ QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
+ while (it.hasNext()) {
+ try {
+ QFile f(it.next());
+ f.open(QIODevice::ReadOnly);
+ if (f.fileName().endsWith(QLatin1String("installer.dat")))
+ continue;
+ QInstaller::BinaryContent::findMagicCookie(&f, magicMarker);
+ datFileName = f.fileName();
+ break;
+ } catch (const QInstaller::Error &error) {
+ Q_UNUSED(error)
+ continue;
+ }
+ }
+ }
+ return datFileName;
}
return QString();
}
@@ -508,7 +556,7 @@ public:
foreach (const QString &argument, positionalArguments) {
if (argument.contains(QLatin1Char('='))) {
const QString name = argument.section(QLatin1Char('='), 0, 0);
- const QString value = argument.section(QLatin1Char('='), 1, 1);
+ const QString value = argument.section(QLatin1Char('='), 1);
params.insert(name, value);
}
}
@@ -557,7 +605,7 @@ public:
QString controlScript = QString();
if (m_parser.isSet(CommandLineOptions::scScriptLong)) {
controlScript = m_parser.value(CommandLineOptions::scScriptLong);
- if (!QFileInfo(controlScript).exists())
+ if (!QFileInfo::exists(controlScript))
qCDebug(QInstaller::lcInstallerInstallLog) << "Script file does not exist.";
} else if (!m_core->settings().controlScript().isEmpty()) {
diff --git a/src/sdk/settingsdialog.cpp b/src/sdk/settingsdialog.cpp
index c3159ac36..0a16377b3 100644
--- a/src/sdk/settingsdialog.cpp
+++ b/src/sdk/settingsdialog.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -194,6 +194,7 @@ SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent)
, m_ui(new Ui::SettingsDialog)
, m_core(core)
, m_showPasswords(false)
+ , m_cacheCleared(false)
{
m_ui->setupUi(this);
setupRepositoriesTreeWidget();
@@ -238,6 +239,23 @@ SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent)
this, &SettingsDialog::selectAll);
connect(m_ui->m_deselectAll, &QAbstractButton::clicked,
this, &SettingsDialog::deselectAll);
+
+ connect(m_ui->m_clearPushButton, &QAbstractButton::clicked,
+ this, &SettingsDialog::clearLocalCacheClicked);
+ 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());
m_ui->m_useTmpRepositories->setEnabled(settings.hasReplacementRepos());
@@ -248,6 +266,16 @@ SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent)
m_ui->m_repositories->setParent(this);
m_ui->m_repositories->setVisible(settings.repositorySettingsPageVisible());
}
+
+ m_ui->m_cachePathLineEdit->setText(settings.localCachePath());
+ m_ui->m_clearPushButton->setEnabled(m_core->isValidCache());
+ showClearCacheProgress(false);
+}
+
+void SettingsDialog::showClearCacheProgress(bool show)
+{
+ m_ui->m_clearCacheProgressLabel->setVisible(show);
+ m_ui->m_clearCacheProgressBar->setVisible(show);
}
void SettingsDialog::accept()
@@ -290,6 +318,14 @@ void SettingsDialog::accept()
settingsChanged |= (settings.httpProxy() != newSettings.httpProxy());
}
+ // need to fetch metadata again
+ settingsChanged |= m_cacheCleared;
+ m_cacheCleared = false;
+
+ // update cache path
+ newSettings.setLocalCachePath(m_ui->m_cachePathLineEdit->text());
+ settingsChanged |= (settings.localCachePath() != newSettings.localCachePath());
+
if (settingsChanged)
emit networkSettingsChanged(newSettings);
diff --git a/src/sdk/settingsdialog.h b/src/sdk/settingsdialog.h
index 97bdd0467..5f5c017a8 100644
--- a/src/sdk/settingsdialog.h
+++ b/src/sdk/settingsdialog.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -104,11 +104,14 @@ class SettingsDialog : public QDialog
public:
explicit SettingsDialog(QInstaller::PackageManagerCore *core, QWidget *parent = 0);
+ void showClearCacheProgress(bool show);
+
public slots:
void accept();
signals:
void networkSettingsChanged(const QInstaller::Settings &settings);
+ void clearLocalCacheClicked();
private slots:
void addRepository();
@@ -131,6 +134,7 @@ private:
QInstaller::PackageManagerCore *m_core;
bool m_showPasswords;
+ bool m_cacheCleared;
QList<QTreeWidgetItem*> m_rootItems;
};
diff --git a/src/sdk/settingsdialog.ui b/src/sdk/settingsdialog.ui
index 6645e6460..90580877d 100644
--- a/src/sdk/settingsdialog.ui
+++ b/src/sdk/settingsdialog.ui
@@ -274,6 +274,102 @@
</item>
</layout>
</widget>
+ <widget class="QWidget" name="m_localCache">
+ <attribute name="title">
+ <string>Local cache</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QLabel" name="m_cacheDescriptionLabel">
+ <property name="text">
+ <string>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="m_cachePathLabel">
+ <property name="text">
+ <string>Path for cache:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="m_cachePathLineEdit"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_clearCacheProgressLabel">
+ <property name="text">
+ <string>Clearing cache...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="m_clearCacheProgressBar">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximum">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>-1</number>
+ </property>
+ <property name="format">
+ <string notr="true">%p%</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_clearPushButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Deletes the contents of the cache directory</string>
+ </property>
+ <property name="text">
+ <string>Clear cache</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
<item>
diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp
index 56ab524aa..8c15243f8 100644
--- a/src/sdk/tabcontroller.cpp
+++ b/src/sdk/tabcontroller.cpp
@@ -37,6 +37,8 @@
#include <productkeycheck.h>
#include <QtCore/QTimer>
+#include <QtWidgets/QMessageBox>
+#include <QtConcurrent>
using namespace QInstaller;
@@ -128,7 +130,7 @@ int TabController::init()
if (page) {
page->setMessage(QString());
page->setErrorMessage(QString());
- page->onCoreNetworkSettingsChanged();
+ page->resetFetchedState();
}
d->m_gui->restart();
@@ -154,6 +156,8 @@ void TabController::restartWizard()
d->m_core->settings().setDefaultRepositories(d->m_settings.defaultRepositories());
d->m_core->settings().setTemporaryRepositories(d->m_settings.temporaryRepositories(),
d->m_settings.hasReplacementRepos());
+ d->m_core->settings().setLocalCachePath(d->m_settings.localCachePath());
+
d->m_core->networkSettingsChanged();
}
@@ -169,8 +173,11 @@ void TabController::restartWizard()
void TabController::onSettingsButtonClicked()
{
SettingsDialog dialog(d->m_core);
+ dialog.adjustSize();
connect(&dialog, &SettingsDialog::networkSettingsChanged,
this, &TabController::onNetworkSettingsChanged);
+ connect(&dialog, &SettingsDialog::clearLocalCacheClicked,
+ this, &TabController::onClearCacheClicked);
dialog.exec();
if (d->m_networkSettingsChanged) {
@@ -191,11 +198,59 @@ void TabController::onAboutApplicationClicked()
dialog.exec();
}
+void TabController::onClearCacheClicked()
+{
+ SettingsDialog *settingsDialog = static_cast<SettingsDialog *>(sender());
+ settingsDialog->setEnabled(false);
+ settingsDialog->showClearCacheProgress(true);
+
+ QString errorMessage;
+ bool success = true;
+
+ // Clearing might take some time, run in a separate thread
+ QEventLoop loop;
+ QFutureWatcher<bool> futureWatcher;
+
+ connect(&futureWatcher, &QFutureWatcher<bool>::finished, this, [&]() {
+ success = futureWatcher.future().result();
+ if (loop.isRunning())
+ loop.quit();
+ });
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ futureWatcher.setFuture(QtConcurrent::run(&PackageManagerCore::clearLocalCache,
+ d->m_core, &errorMessage));
+#else
+ futureWatcher.setFuture(QtConcurrent::run(d->m_core,
+ &PackageManagerCore::clearLocalCache, &errorMessage));
+#endif
+
+ if (!futureWatcher.isFinished())
+ loop.exec();
+
+ settingsDialog->setEnabled(true);
+ settingsDialog->showClearCacheProgress(false);
+
+ QMessageBox msgBox(settingsDialog);
+ msgBox.setWindowModality(Qt::WindowModal);
+ msgBox.setStandardButtons(QMessageBox::Close);
+
+ msgBox.setIcon(success
+ ? QMessageBox::Information
+ : QMessageBox::Critical);
+
+ msgBox.setText(success
+ ? tr("Cache cleared successfully!")
+ : errorMessage);
+
+ msgBox.exec();
+}
+
void TabController::onCurrentIdChanged(int newId)
{
if (d->m_gui) {
if (PackageManagerPage *page = qobject_cast<PackageManagerPage *>(d->m_gui->page(newId)))
- d->m_gui->showSettingsButton(page->settingsButtonRequested());
+ d->m_gui->requestSettingsButtonByInstaller(page->settingsButtonRequested());
}
}
diff --git a/src/sdk/tabcontroller.h b/src/sdk/tabcontroller.h
index a92e724b4..236ff9fb8 100644
--- a/src/sdk/tabcontroller.h
+++ b/src/sdk/tabcontroller.h
@@ -61,6 +61,7 @@ private Q_SLOTS:
void restartWizard();
void onSettingsButtonClicked();
void onAboutApplicationClicked();
+ void onClearCacheClicked();
void onCurrentIdChanged(int newId);
void onNetworkSettingsChanged(const QInstaller::Settings &settings);
diff --git a/src/sdk/translations/ifw_ar.ts b/src/sdk/translations/ifw_ar.ts
index 91067b054..ee995f3d5 100644
--- a/src/sdk/translations/ifw_ar.ts
+++ b/src/sdk/translations/ifw_ar.ts
@@ -212,6 +212,30 @@
<source>User defined repositories</source>
<translation>المستودعات المعرفة من قبل المستخدم</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>ذاكرة التخزين المؤقت المحلية</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>يتم تخزين معلومات التعريف من المستودعات البعيدة مؤقتًا على القرص لتحسين أوقات التحميل. يمكنك تحديد دليل آخر لتخزين ذاكرة التخزين المؤقت أو مسح محتويات ذاكرة التخزين المؤقت الحالية.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>مسار ذاكرة التخزين المؤقت:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>حذف محتويات دليل ذاكرة التخزين المؤقت</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>مسح ذاكرة التخزين المؤقت</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>جاري مسح ذاكرة التخزين المؤقت ...</translation>
+ </message>
</context>
<context>
<name>QObject</name>
@@ -271,6 +295,14 @@
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation>الرجاء التأكد من أن المستخدم الحالي لديه حق قراءة الملف &amp;quot;%1&amp;quot; أو حاول تشغيل%2 كمسؤول.</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>قيمة غير صالحة لـ &apos;max-concurrent-operations&apos;</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>قيمة فارغة للخيار &apos;cache-path&apos;.</translation>
+ </message>
</context>
<context>
<name>QInstaller</name>
@@ -382,6 +414,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>محتوى غير صالح في &quot;%1&quot;.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>يمكن حل هذا عن طريق إعادة تشغيل التطبيق بعد مسح ذاكرة التخزين المؤقت من:</translation>
+ </message>
</context>
<context>
<name>BinaryLayout</name>
@@ -442,18 +478,6 @@
<translation>لا يمكن للمكونات أن تحتوي على أطفال في وضع التحديث.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>لا يمكن فتح ملف واجهة المستخدم المطلوب &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>لا يمكن تحميل ملف واجهة المتسخدم المطلوب &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>لا يمكن فتح ملف الرخصة المطلوب &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>خطأ</translation>
</message>
@@ -473,6 +497,26 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>كان هناك خطأ أثناء تحميل المكون المحدد. هذا المكون لا يمكن تثبيته.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>لا يمكن فتح ملف واجهة المستخدم المطلوب &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>لا يمكن تحميل ملف واجهة المتسخدم المطلوب &quot;%1&quot;: %2.%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>لا يمكن فتح ملف الرخصة المطلوب &quot;%1&quot;: %2.%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -520,62 +564,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>الاف&amp;تراضي</translation>
+ <source>Default</source>
+ <translation>الافتراضي</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>حدد المكونات الافتراضية في العرض الشجري.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;أعد الضبط</translation>
+ <source>Reset</source>
+ <translation>أعد الضبط</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>إعادة تعيين جميع المكونات إلى حالة التحديد الأصلية في طريقة العرض الشجري.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>&amp;حدّد الكل</translation>
+ <source>Select All</source>
+ <translation>حدّد الكل</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>قم بتحديد جميع المكونات في عرض الشجري.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>&amp;ألغِ تحديد الكل</translation>
+ <source>Deselect All</source>
+ <translation>ألغِ تحديد الكل</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>قم بإلغاء تحديد المكونات الافتراضية في عرض الشجري.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;تصفح ملفات QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>حدد ملف حزمة دعم لوحة Qt لتثبيت محتوى إضافي غير متاح مباشرة في المستودعات التي عبر الإنترنت.</translation>
</message>
@@ -608,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>
@@ -619,6 +639,26 @@
<source>Search</source>
<translation>بحث</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>تصفح ملفات &amp;QBSP</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>اختيار</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>خطأ</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>إنشاء المثبت دون اتصال</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>إنشاء برنامج تثبيت دون اتصال من المكونات المحددة، بدلاً من التثبيت الآن.</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentSelectionPagePrivate</name>
@@ -631,7 +671,7 @@
<translation>خطأ</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation>معلومات المكون</translation>
</message>
</context>
@@ -650,12 +690,8 @@
<translation>لا يمكن حفظ إخراج &quot;%1&quot; إلى قيمة مفتاح تثبيت فارغة.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>الملف &quot;%1&quot; غير موجود أو ليس قابلاً للتنفيذ.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>تشغيل &quot;%1&quot; تسبب في إنهيار.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation>فشل تشغيل الأمر: &quot;%1&quot;: %2</translation>
</message>
</context>
<context>
@@ -835,14 +871,6 @@
<translation>خطأ في التنزيل</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>فشل التحقق من التجزئة أثناء التنزيل. هذا خطأ مؤقت، يرجى إعادة المحاولة.</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>لا يمكن التحقق من التحزئة</translation>
- </message>
- <message>
<source>Cannot download archive %1: %2</source>
<translation>لا يمكن تنزيل الأرشيف %1: %2</translation>
</message>
@@ -932,6 +960,29 @@ Error while loading %2</source>
<source>Total: </source>
<translation>المجموع: </translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>تم تجاوز عدد مرات إعادة المحاولة (%1).
+</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>فشل التحقق من التجزئة أثناء التنزيل. هذا خطأ مؤقت، يرجى إعادة المحاولة.
+
+المتوقع: %1
+تم التنزيل: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>لا يمكن التحقق من التجزئة
+المتوقع: %1
+تم التنزيل: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1074,6 +1125,22 @@ Error while loading %2</source>
<source>Extracting &quot;%1&quot;</source>
<translation>استخراج &quot;%1&quot;</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>أرشيف غير مدعوم &quot;%1&quot;: لم يتم تسجيل أي معالج للاحقة الملف &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الأرشيف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>حدث خطأ أثناء قراءة محتويات الأرشيف &quot;%1&quot;: %2.</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>إزالة الملفات المستخرجة من &quot;%1&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::FakeStopProcessForUpdateOperation</name>
@@ -1127,6 +1194,18 @@ Error while loading %2</source>
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>لا يمكن العثور على الاعتمادية المفقودة &quot;%1&quot; لـ &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>تم الكشف عن تحليل تبعية مستحيلة. سيتم إلغاء تثبيت مكون التثبيت الإجباري &quot;%1&quot; بسبب أن التبعية &quot;%2&quot; تم تعليمها لإلغاء التثبيت بسبب: &quot;%3&quot;.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>المكونات المحددة بواسطة الاسم المستعار &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>تم اكتشاف التكرار، الاسم المستعار للمكون &quot;%1&quot; تم الاضافة مسبقا.</translation>
+ </message>
</context>
<context>
<name>QInstaller::InstallIconsOperation</name>
@@ -1286,10 +1365,6 @@ Error while loading %2</source>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>لا يمكن كتابة ملف الرخصة &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>لم يُعثر على ملفات ترخيص لحذفها.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1313,10 +1388,6 @@ Error while loading %2</source>
<translation>محرك نواة مدير الحزم مفقود.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>جارٍ تحضير تنزيل ملفات التعريف...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>جارٍ فك ضغط المستودعات المضغوطة. قد يستغرق هذا بعص الوقت...</translation>
</message>
@@ -1353,14 +1424,6 @@ Error while loading %2</source>
<translation>اكتُشف عدم تطابق تدقيق المجموع لـ &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>جارٍ الحصول على معلومات التعريف من المستودع البعيد... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>جارٍ الحصول على معلومات التعريف من المستودع البعيد... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>حدث خطأ أثناء إستخراج الأرشيف &quot;%1&quot;: %2</translation>
</message>
@@ -1372,6 +1435,45 @@ Error while loading %2</source>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>أرشيف غير مدعوم &quot;%1&quot;: لم يتم تسجيل أي معالج للاحقة الملف &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>جاري إحضار معلومات التحديث الأخيرة...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>جاري تحديث ذاكرة التخزين المؤقت المحلية بـ%n من العناصر الجديدة...</numerusform>
+ <numerusform>جاري تحديث ذاكرة التخزين المؤقت المحلية بـ%n من العناصر الجديدة...</numerusform>
+ <numerusform>جاري تحديث ذاكرة التخزين المؤقت المحلية بـ%n من العناصر الجديدة...</numerusform>
+ <numerusform>جاري تحديث ذاكرة التخزين المؤقت المحلية بـ%n من العناصر الجديدة...</numerusform>
+ <numerusform>جاري تحديث ذاكرة التخزين المؤقت المحلية بـ%n من العناصر الجديدة...</numerusform>
+ <numerusform>جاري تحديث ذاكرة التخزين المؤقت المحلية بـ%n من العناصر الجديدة...</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>قد يؤدي مسح دليل ذاكرة التخزين المؤقت وإعادة تشغيل التطبيق إلى حل هذه المشكلة.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>حدث خطأ غير معروف أثناء تحديث ذاكرة التخزين المؤقت.</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف المستخرج &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>الحصول على المعلومات من المستودع البعيد...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>جارٍ الحصول على معلومات التعريف من المستودع البعيد...</translation>
+ </message>
</context>
<context>
<name>QInstaller::FileTaskObserver</name>
@@ -1447,10 +1549,8 @@ Error while loading %2</source>
<translation>خطأ في كتابة أداة الصيانة</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-جارٍ تنزيل الحزم...</translation>
+ <source>Downloading packages...</source>
+ <translation>جارٍ تنزيل الحزم...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1551,10 +1651,6 @@ Do you want to continue?</source>
<translation>لم يُعثر على حزم مثبتة.</translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation>المكون ذو المعرف %1 موجود بالفعل.</translation>
- </message>
- <message>
<source>Application running in Uninstaller mode.</source>
<translation>يعمل التطبيق في وضع إزالة التثبيت.</translation>
</message>
@@ -1567,42 +1663,24 @@ Do you want to continue?</source>
<translation>لا يمكن الحصول على كافة الاعتماديات.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>المكونات على وشك الحذف.</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>المكون غير موجود.</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>المكون غير موجود.
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>تم تثبيت المكون فقط كتبعية تلقائية لـ %2.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>تم تثبيت المكون فقط كتبعية تلقائية لـ %2.
-</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>المكون غير قابل للتحقق مما يعني أنه يجب عليك تحديد أحد المكونات الفرعية.
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>المكون غير قابل للتحقق مما يعني أنه يجب عليك تحديد أحد المكونات الفرعية.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>المكون %1 مثبت بالفعل
-</translation>
- </message>
- <message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>المكون افتراضي.
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>المكون %1 مثبت بالفعل</translation>
</message>
<message>
- <source>Running processes found.</source>
- <translation>وجدت العمليات الجارية.</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>المكون افتراضي.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1625,10 +1703,6 @@ Do you want to continue?</source>
<translation>لا توجد مساحة كافية لتخزين جميع المكونات المحددة! بينما المساحة المطلوبة هي %2 على الأقل، المساحة المتاحة هي %1.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>لا توجد مساحة كافية لتخزين الملفات المؤقتة!. بينما المساحة المطلوبة هي %2 على الأقل، المساحة المتاحة هي %1.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>يبدو أن وحدة التخزين التي حددتها للتثبيت بها مساحة كافية للتثبيت، ولكن سيكون هناك أقل من 1% من مساحة وحدة التخزين المتاحة بعد ذلك.</translation>
</message>
@@ -1645,14 +1719,40 @@ Do you want to continue?</source>
<translation>غير صالح</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>المكون تابع لمكون ظاهري %2.
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>المكون تابع لمكون ظاهري %2.</translation>
</message>
<message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
- <translation type="unfinished"></translation>
+ <translation>قد يتجاوز الحجم المقدر للمثبّت 1% حد الحجم القابل للتنفيذ المعتمد وهو %2. قد لا يكون التطبيق قادرًا على التشغيل.</translation>
+ </message>
+ <message>
+ <source>Components about to be removed:</source>
+ <translation>المكونات التالية على وشك الإزالة:</translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>لا يمكن تثبيت المكون%1. حدثت مشكلة أثناء تحميل هذا المكون، لذلك تم وضع علامة &quot;غير مستقر&quot; عليه ولا يمكن اختياره.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>لا توجد مساحة كافية على القرص لتخزين الملفات المؤقتة! يتوفر%1 ، بينما الحد الأدنى المطلوب هو%2. يمكنك تحديد موقع آخر للملفات المؤقتة عن طريق تعديل مسار ذاكرة التخزين المؤقت المحلية من إعدادات المثبت.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>لا يمكن تحديد المكونات المطلوب إزالتها.</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>لا يمكن تحديد الاسم المستعار %1. حدثت مشكلة أثناء تحميل هذا الاسم المستعار، لذا تم وضع علامة عليه كغير مستقر ولا يمكن تحديده.</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>لا يمكن تحديد %1. تم وضع علامة على الاسم المستعار افتراضيًا، مما يعني أنه لا يمكن تحديده يدويًا.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>سيستخدم المثبت الذي تم إنشاؤه %1 من مساحة القرص.</translation>
</message>
</context>
<context>
@@ -1762,16 +1862,12 @@ Do you want to continue?</source>
<translation>إنشاء أداة الصيانة</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-انتهى التثبيت!</translation>
+ <source>Installation finished!</source>
+ <translation>انتهى التثبيت!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-أُلغي التثبيت!</translation>
+ <source>Installation aborted!</source>
+ <translation>أُلغي التثبيت!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1782,16 +1878,12 @@ Installation aborted!</source>
<translation>جارٍ حذف المكونات الغير محددة...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-انتهى التحديث!</translation>
+ <source>Update finished!</source>
+ <translation>انتهى التحديث!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-أُلغي التحديث!</translation>
+ <source>Update aborted!</source>
+ <translation>أُلغي التحديث!</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1834,8 +1926,7 @@ Update aborted!</source>
<translation>تم إحباط الجيل غير المتصل!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
+ <source>Installing component %1</source>
<translation>لا يمكن تثبيت المكون %1</translation>
</message>
<message>
@@ -1883,10 +1974,6 @@ Installing component %1</source>
<translation>لا يمكن الحصول على بيانات التعريف: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>لا تمكن إضافة معلومات مصدر التحديث المؤقت.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>لا يمكن العثور على أي معلومات مصدر تحديث.</translation>
</message>
@@ -1900,6 +1987,50 @@ Installing component %1</source>
%1</source>
<translation>يجب إيقاف هذه العمليات للمتابعة:%1</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation>جاري التحضير لتفريغ المكونات ...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation>اكتملت%1 من%2 عملية.</translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation>جاري تفريغ المكونات ...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation>تم التراجع عن1% من2% عملية.</translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation>تم إكمال التراجع.</translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation>تم تثبيت1% من2% مكونات.</translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation>تم تثبيت جميع المكونات.</translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>جاري تحميل البرامج النصية للمكونات...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>الاسم المستعار يعلن عن اسم يتعارض مع مكون موجود &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>لم يتم التمكن من تحديد الأسماء المستعارة للمكونات</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>تم اكتشاف تبعية دورية بين الأسماء المستعارة &quot;%1&quot; و &quot;%2&quot;.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -1957,11 +2088,11 @@ Please copy the installer to a local drive</source>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>الإعداد - %1</translation>
+ <source>Welcome</source>
+ <translation>مَرْحَبًا</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>مرحباً بك في إعداد %1.</translation>
</message>
<message>
@@ -1989,10 +2120,6 @@ Please copy the installer to a local drive</source>
<translation>لا تحديثات متوفرة.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> إدارة الحزم المحلية هي المتاحة فقط.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>&amp;اخرج</translation>
</message>
@@ -2079,7 +2206,7 @@ Please copy the installer to a local drive</source>
<translation>جاهز لإزالة التثبيت</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>الإعداد جاهز الآن لبدأ حذف %1 من حاسوبك. .&lt;br&gt;&lt;font color=&quot;red&quot;&gt;مجلد البرنامج %2 سيُحذف تماماً&lt;/font&gt; مع كل المحتوى فيه!</translation>
</message>
<message>
@@ -2091,7 +2218,7 @@ Please copy the installer to a local drive</source>
<translation>جاهز لتحديث الحزم</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>الإعداد جاهز لبدأ تحديث تثبيتك.</translation>
</message>
<message>
@@ -2103,13 +2230,25 @@ Please copy the installer to a local drive</source>
<translation>جاهز للتثبيت</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>الإعداد جاهز الآن لبدأ تثبيت %1 على حاسوبك.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>جاهز للتحديث</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>إنشاء المثبت دون اتصال</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>جاهز لإنشاء برنامج التثبيت دون اتصال</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>جميع المعلومات المطلوبة متاحة الآن لإنشاء برنامج تثبيت دون اتصال للمكونات المحددة.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PerformInstallationPage</name>
@@ -2149,11 +2288,23 @@ Please copy the installer to a local drive</source>
<source>Uninstalling</source>
<translation>إلغاء التثبيت</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>&amp; إنشاء برنامج التثبيت دون اتصال</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>إنشاء برنامج تثبيت دون اتصال لـ %1</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>إنشاء المثبت دون اتصال</translation>
+ </message>
</context>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>إكمال معالج %1</translation>
</message>
<message>
@@ -2161,7 +2312,7 @@ Please copy the installer to a local drive</source>
<translation>مكتمل</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>اضغط %1 للخروج من معالج %2.</translation>
</message>
<message>
@@ -2173,14 +2324,14 @@ Please copy the installer to a local drive</source>
<translation>شغل %1 الآن.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>فشل معالج %1.</translation>
</message>
</context>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>إكمال معالج إعداد %1</translation>
</message>
</context>
@@ -2303,7 +2454,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>لا يمكن فتح ملف الإعدادات %1 للقراءة: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation>حدد الاقسام</translation>
</message>
</context>
@@ -2511,6 +2662,10 @@ or accept the elevation of access rights if being asked.</source>
<source>Try again</source>
<translation>حاول مرة آخرى</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>لا يمكن تحميل %1. لا يمكن إنشاء دليل لـ &quot;%2&quot;</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -2786,10 +2941,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>لا يمكن قراءة &quot;%1&apos;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>خطأ في فرز %1 في %2، %3:%4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>عنصر الجذر %1 غير متوقع، يجب أن يكون &quot;Updates&quot;.</translation>
</message>
@@ -2839,20 +2990,178 @@ or accept the elevation of access rights if being asked.</source>
<translation>لا يمكن فتح الأرشيف &quot;%1&quot; للقراءة: %2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
- <translation>حدث خطأ أثناء قراءة محتويات الأرشيف &quot;%1&quot;: %2.</translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>حدث خطأ أثناء إستخراج الأرشيف &quot;%1&quot;: %2</translation>
</message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation>لا يمكن التحضير للملف &quot;%1&quot;.</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>لا يمكن فتح الأرشيف للقراءة: 1%</translation>
</message>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation>تم إلغاء استخراج الارشيف &quot;%1&quot;.</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>لا يمكن قراءة الإدخال 1%.</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>حدث خطأ أثناء إستخراج الأرشيف &quot;%1&quot;: %2</translation>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>لا يمكن كتابة الإدخال &quot;%1&quot; إلى القرص: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>لا يمكن فتح الأرشيف للقراءة: 1%</translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation>لا يمكن قراءة الإدخال 1%.</translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>لا يمكن كتابة الإدخال &quot;%1&quot; إلى القرص: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للكتابة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>لا يمكن فتح الملف &quot;%1&quot; للقراءة: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation>لا يمكن كتابة الإدخال لـ &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation>إلغاء اختيار المكونات:</translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation>تم استبدال المكونات بـ &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation>إزالة المكونات الافتراضية الغير محتوية على تبعيات:</translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation>تم إزالة تبعيات المكونات &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation>تم إزالة الاعتماد الذاتي لالمكونات &quot;%1&quot;:</translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation>حول مثبّت %1</translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation>حول أداة الصيانة %1</translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>لا يمكن تهيئة ذاكرة التخزين المؤقت بمسار فارغ.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>لا يمكن إنشاء دليل &quot;%1&quot; لذاكرة التخزين المؤقت.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>لا يمكن تهيئة ذاكرة التخزين المؤقت: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>لا يمكن مسح ذاكرة التخزين المؤقت غير الصالحة.</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>لا يمكن إزالة ملف البيان: %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>خطأ أثناء مسح ذاكرة التخزين المؤقت: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>لا يمكن استرداد العناصر من ذاكرة التخزين المؤقت غير الصالحة.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>لا يمكن استرداد العنصر من ذاكرة التخزين المؤقت غير الصالحة.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>لا يمكن تسجيل العنصر في ذاكرة التخزين المؤقت غير الصالحة.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>لا يمكن تسجيل عنصر فارغ.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>لا يمكن تسجيل عنصر غير صالح مع المجموع الاختباري %1</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>لا يمكن تسجيل العنصر بالمجموع الاختباري %1. عنصر بنفس المجموع الاختباري موجود بالفعل في ذاكرة التخزين المؤقت.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>خطأ أثناء نسخ العنصر إلى المسار &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>لا يمكن إزالة العنصر من ذاكرة التخزين المؤقت غير الصالحة.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>لا يمكن إزالة العنصر المحدد بواسطة المجموع الاختباري %1: لا يوجد مثل هذا العنصر.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>خطأ أثناء إزالة الدليل &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>خطأ أثناء إبطال ذاكرة التخزين المؤقت: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>لا يمكن فتح ملف البيان: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>لا يمكن كتابة محتويات لملف البيان: %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>لا يمكن مزامنة ذاكرة التخزين المؤقت غير الصالحة.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>تم اختبار وضع تسجيل غير معروف!</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>تم مسح ذاكرة التخزين المؤقت بنجاح!</translation>
</message>
</context>
</TS>
diff --git a/src/sdk/translations/ifw_ca.ts b/src/sdk/translations/ifw_ca.ts
index 768c39063..ddd7efc13 100644
--- a/src/sdk/translations/ifw_ca.ts
+++ b/src/sdk/translations/ifw_ca.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>No s&apos;ha pogut trobar la dependència «%1» que falta per a «%2».</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -306,6 +318,10 @@
<source>Try again</source>
<translation>Torna a intentar-ho</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -489,10 +505,6 @@
<translation>No s&apos;ha pogut llegir «%1»</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Error d&apos;anàlisi en %1 a %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Element arrel %1 inesperat, hauria de ser «Updates».</translation>
</message>
@@ -744,6 +756,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -752,18 +768,6 @@
<translation>Els components no poden tenir elements secundaris en el mode actualitzador.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>No s&apos;ha pogut obrir el fitxer UI «%1» sol·licitat: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>No s&apos;ha pogut carregar el fitxer UI «%1» sol·licitat: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>No s&apos;ha pogut obrir el fitxer de llicència «%1» sol·licitat: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -784,8 +788,24 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>There was an error loading the selected component. This component cannot be updated.</source>
- <translation type="unfinished"></translation>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>No s&apos;ha pogut obrir el fitxer UI «%1» sol·licitat: %2. %3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>No s&apos;ha pogut carregar el fitxer UI «%1» sol·licitat: %2. %3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>No s&apos;ha pogut obrir el fitxer de llicència «%1» sol·licitat: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
</context>
<context>
@@ -834,42 +854,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>Predetermin&amp;at</translation>
+ <source>Default</source>
+ <translation>Predeterminat</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Restableix</translation>
+ <source>Reset</source>
+ <translation>Restableix</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Selecciona-ho tot</translation>
+ <source>Select All</source>
+ <translation>Selecciona-ho tot</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Desselecciona-ho tot</translation>
+ <source>Deselect All</source>
+ <translation>Desselecciona-ho tot</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>Explora els fitxers Q&amp;BSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation type="unfinished"></translation>
</message>
@@ -898,35 +914,39 @@
<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>
<translation>Cal que actualitzeu els components obligatoris abans de poder seleccionar altres components per actualitzar-los.</translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation type="unfinished">Alt+A</translation>
+ <source>Filter the enabled repository categories</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
+ <source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation type="unfinished">Alt+S</translation>
+ <source>Browse &amp;QBSP files</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation type="unfinished">Alt+D</translation>
+ <source>Select</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
+ <source>Error</source>
+ <translation type="unfinished">Error</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -941,8 +961,8 @@
<translation>Error</translation>
</message>
<message>
- <source>Component Information</source>
- <translation type="unfinished"></translation>
+ <source>Information</source>
+ <translation>Informació</translation>
</message>
</context>
<context>
@@ -960,12 +980,8 @@
<translation>No s&apos;ha pogut desar la sortida de «%1» a un valor de clau buit de l&apos;instal·lador.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>El fitxer «%1» no existeix o no és un binari executable.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>L&apos;execució de «%1» ha resultat en una fallada.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1102,6 +1118,14 @@
<source>Cannot remove directory &quot;%1&quot;: %2</source>
<translation>No s&apos;ha pogut eliminar el directori «%1»: %2</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create archive &quot;%1&quot;: %2</source>
+ <translation>No s&apos;ha pogut crear l&apos;arxiu «%1»: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::CreateShortcutOperation</name>
@@ -1166,6 +1190,62 @@ Error en descarregar %2</translation>
<source>Cannot find component for %1.</source>
<translation>No s&apos;ha pogut trobar el component per a %1.</translation>
</message>
+ <message>
+ <source>%1 of %2</source>
+ <translation type="unfinished">%1 de %2</translation>
+ </message>
+ <message>
+ <source>%1 downloaded.</source>
+ <translation>S&apos;ha descarregat %1.</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s), </source>
+ <translation>
+ <numerusform>%n dia, </numerusform>
+ <numerusform>%n dies, </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s), </source>
+ <translation>
+ <numerusform>%n hora, </numerusform>
+ <numerusform>%n hores, </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation>
+ <numerusform>%n minut</numerusform>
+ <numerusform>%n minuts</numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation>
+ <numerusform>%n segon</numerusform>
+ <numerusform>%n segons</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source> - %1%2%3%4 remaining.</source>
+ <translation> - resta %1%2%3%4.</translation>
+ </message>
+ <message>
+ <source> - unknown time remaining.</source>
+ <translation> - temps restant desconegut.</translation>
+ </message>
+ <message>
+ <source>Archive: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Total: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1240,20 +1320,21 @@ Error en descarregar %2</translation>
<source>Extracting &quot;%1&quot;</source>
<translation>S&apos;està extraient «%1»</translation>
</message>
-</context>
-<context>
- <name>QInstaller::ExtractArchiveOperation::Runnable</name>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
<message>
<source>Cannot open archive &quot;%1&quot; for reading: %2</source>
<translation>No s&apos;ha pogut obrir el fitxer «%1» per a lectura: %2</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Error en extreure l&apos;arxiu «%1»: %2</translation>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Unknown exception caught while extracting &quot;%1&quot;.</source>
- <translation>S&apos;ha produït una excepció desconeguda en extreure «%1».</translation>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1325,7 +1406,7 @@ Error en descarregar %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>S&apos;ha completat l&apos;Assistent de %1</translation>
</message>
<message>
@@ -1333,7 +1414,7 @@ Error en descarregar %2</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Feu clic a %1 per a sortir de l&apos;Assistent de %2.</translation>
</message>
<message>
@@ -1345,7 +1426,7 @@ Error en descarregar %2</translation>
<translation>Executa %1 ara.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Ha fallat l&apos;Assistent de %1.</translation>
</message>
</context>
@@ -1386,15 +1467,19 @@ Error en descarregar %2</translation>
<source>Cannot create directory &quot;%1&quot;: %2</source>
<translation>No s&apos;ha pogut crear el directori «%1»: %2</translation>
</message>
+ <message>
+ <source>Cannot prepare to backup file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Programa d&apos;instal·lació: %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Us donem la benvinguda a l&apos;Assistent de configuració de %1.</translation>
</message>
<message>
@@ -1422,13 +1507,13 @@ Error en descarregar %2</translation>
<translation>No hi ha disponible cap actualització.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Només està disponible la gestió dels paquets locals.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Su&amp;rt</translation>
</message>
+ <message>
+ <source>There is an important update available. Please select &apos;%1&apos; first</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::LicenseAgreementPage</name>
@@ -1472,10 +1557,6 @@ Error en descarregar %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>No s&apos;ha pogut escriure en el fitxer de llicència «%1».</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>No s&apos;han trobat fitxers de llicència per a suprimir.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1499,10 +1580,6 @@ Error en descarregar %2</translation>
<translation>Manca el motor del nucli del gestor de paquets.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>S&apos;està preparant la descàrrega de la informació de les metadades...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>S&apos;estan desempaquetant els repositoris comprimits. Aquesta operació pot trigar una estona...</translation>
</message>
@@ -1535,29 +1612,56 @@ Error en descarregar %2</translation>
<translation>S&apos;ha detectat una discrepància en la suma de verificació per a «%1».</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>S&apos;està recuperant la informació de les metadades des del repositori remot... %1/%2 </translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Error en extreure l&apos;arxiu «%1»: %2</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>S&apos;està recuperant la informació de les metadades des del repositori remot... </translation>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>No s&apos;ha pogut obrir el fitxer «%1» per a lectura: %2</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Error en extreure l&apos;arxiu «%1»: %2</translation>
+ <source>Metadata download canceled.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Unknown exception caught while extracting archive &quot;%1&quot;.</source>
- <translation>S&apos;ha produït una excepció desconeguda en extreure l&apos;arxiu «%1».</translation>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot; for reading: %2</source>
- <translation>No s&apos;ha pogut obrir el fitxer «%1» per a lectura: %2</translation>
+ <source>Fetching latest update information...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
</message>
<message>
- <source>Metadata download canceled.</source>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>No s&apos;ha pogut obrir el fitxer «%1» per a escriptura: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>S&apos;està recuperant la informació des de repositoris remots...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>S&apos;està recuperant la informació de les metadades des del repositori remot...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1566,10 +1670,8 @@ Error en descarregar %2</translation>
<translation>Error en escriure a l&apos;Eina de manteniment</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-S&apos;estan descarregant els paquets...</translation>
+ <source>Downloading packages...</source>
+ <translation>S&apos;estan descarregant els paquets...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1674,31 +1776,19 @@ Voleu continuar?</translation>
<translation>No s&apos;han pogut resoldre totes les dependències.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Components que estan a punt d&apos;eliminar-se.</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
+ <source>Component %1 already installed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
+ <source>Cannot install %1. Component is virtual.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Running processes found.</source>
+ <source>Cannot install %1. Component not found.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1730,10 +1820,6 @@ Voleu continuar?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation type="unfinished"></translation>
</message>
@@ -1742,13 +1828,11 @@ Voleu continuar?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1760,11 +1844,35 @@ Voleu continuar?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
+ <source>Invalid</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Invalid</source>
+ <source>Components about to be removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1883,16 +1991,12 @@ Voleu continuar?</translation>
<translation>S&apos;està creant l&apos;Eina de manteniment</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-S&apos;ha finalitzat la instal·lació!</translation>
+ <source>Installation finished!</source>
+ <translation>S&apos;ha finalitzat la instal·lació!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-S&apos;ha interromput la instal·lació!</translation>
+ <source>Installation aborted!</source>
+ <translation>S&apos;ha interromput la instal·lació!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1903,16 +2007,12 @@ S&apos;ha interromput la instal·lació!</translation>
<translation>S&apos;estan eliminant els components desseleccionats...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Ha finalitzat l&apos;actualització!</translation>
+ <source>Update finished!</source>
+ <translation>Ha finalitzat l&apos;actualització!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-S&apos;ha interromput l&apos;actualització!</translation>
+ <source>Update aborted!</source>
+ <translation>S&apos;ha interromput l&apos;actualització!</translation>
</message>
<message>
<source>Cannot create target directory for installer.</source>
@@ -1947,10 +2047,8 @@ S&apos;ha interromput l&apos;actualització!</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-S&apos;instal·la el component %1</translation>
+ <source>Installing component %1</source>
+ <translation>S&apos;instal·la el component %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1983,10 +2081,6 @@ S&apos;instal·la el component %1</translation>
<translation>No s&apos;ha pogut recuperar la informació de les metadades: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>No s&apos;ha pogut afegir la informació temporal de la font d&apos;actualització.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>No s&apos;ha pogut trobar cap informació de la font d&apos;actualització.</translation>
</message>
@@ -2015,6 +2109,50 @@ S&apos;instal·la el component %1</translation>
%1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2118,6 +2256,18 @@ Copieu l&apos;instal·lador en una unitat local</translation>
<source>Uninstalling</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2161,7 +2311,7 @@ Copieu l&apos;instal·lador en una unitat local</translation>
<translation>Llest per a desinstal·lar</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>El programa d&apos;instal·lació està preparat per a començar a eliminar %1 de l&apos;ordinador.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;El directori del programa %2 se suprimirà completament&lt;/font&gt;, incloent tot el contingut d&apos;aquest directori.</translation>
</message>
<message>
@@ -2173,7 +2323,7 @@ Copieu l&apos;instal·lador en una unitat local</translation>
<translation>Llest per a actualitzar els paquets</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>El programa d&apos;instal·lació està preparat per a començar a actualitzar la vostra instal·lació.</translation>
</message>
<message>
@@ -2185,13 +2335,25 @@ Copieu l&apos;instal·lador en una unitat local</translation>
<translation>Llest per a instal·lar</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>El programa d&apos;instal·lació està preparat per a començar a instal·lar %1 en el vostre ordinador.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2248,7 +2410,7 @@ Copieu l&apos;instal·lador en una unitat local</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>S&apos;està completant l&apos;Assistent d&apos;instal·lació de %1</translation>
</message>
</context>
@@ -2471,6 +2633,14 @@ Copieu l&apos;instal·lador en una unitat local</translation>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2515,8 +2685,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>No s&apos;ha pogut obrir el fitxer d&apos;ajustaments %1 per a lectura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Selecciona les categories dels paquets</translation>
+ <source>Categories</source>
+ <translation>Categories</translation>
</message>
</context>
<context>
@@ -2653,6 +2823,30 @@ or accept the elevation of access rights if being asked.</source>
<source>The server&apos;s URL that contains a valid repository.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2710,4 +2904,189 @@ or accept the elevation of access rights if being asked.</source>
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation::Worker</name>
+ <message>
+ <source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>No s&apos;ha pogut obrir el fitxer «%1» per a lectura: %2</translation>
+ </message>
+ <message>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Error en extreure l&apos;arxiu «%1»: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>No s&apos;ha pogut obrir el fitxer «%1» per a escriptura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>No s&apos;ha pogut obrir el fitxer «%1» per a lectura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_da.ts b/src/sdk/translations/ifw_da.ts
index 1bb383ba1..a6325fb01 100644
--- a/src/sdk/translations/ifw_da.ts
+++ b/src/sdk/translations/ifw_da.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Kan ikke finde manglende afhængighed &quot;%1&quot; til &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -306,6 +318,10 @@
<source>Try again</source>
<translation>Prøv igen</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -489,10 +505,6 @@
<translation>Kan ikke læse &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Parse-fejl i %1 ved %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Rod-elementet %1 uventet, skulle være &quot;Updates&quot;.</translation>
</message>
@@ -744,6 +756,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -752,18 +768,6 @@
<translation>Komponenter må ikke have børn i opdateringstilstand.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Kan ikke åbne den anmodede UI-fil &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Kan ikke indlæse den anmodede UI-fil &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Kan ikke åbne den anmodede licensfil &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Fejl</translation>
</message>
@@ -784,8 +788,28 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>There was an error loading the selected component. This component cannot be updated.</source>
- <translation type="unfinished"></translation>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Kan ikke åbne den anmodede UI-fil &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Kan ikke indlæse den anmodede UI-fil &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Kan ikke åbne den anmodede licensfil &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
</context>
<context>
@@ -834,32 +858,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;Standard</translation>
+ <source>Default</source>
+ <translation>Standard</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Nulstil</translation>
+ <source>Reset</source>
+ <translation>Nulstil</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Vælg alle</translation>
+ <source>Select All</source>
+ <translation>Vælg alle</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Fravælg alle</translation>
+ <source>Deselect All</source>
+ <translation>Fravælg alle</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -870,12 +894,8 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Gennemse QBSP-filer</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>
@@ -906,29 +926,33 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
+ <source>Filter the enabled repository categories</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
+ <source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation type="unfinished">Alt+V</translation>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation type="unfinished">Alt+F</translation>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
+ <source>Browse &amp;QBSP files</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Select</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation type="unfinished">Fejl</translation>
+ </message>
</context>
<context>
<name>QInstaller::ConsumeOutputOperation</name>
@@ -945,12 +969,8 @@
<translation>Kan ikke gemme outputtet fra &quot;%1&quot; til en tom installer-nøgleværdi.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>Filen &quot;%1&quot; findes ikke eller er ikke en eksekverbar binær.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>Kørsel af &quot;%1&quot; resulterede i at det holdt op med at virke.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1087,6 +1107,14 @@
<source>Cannot remove directory &quot;%1&quot;: %2</source>
<translation>Kan ikke fjerne mappen &quot;%1&quot;: %2</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create archive &quot;%1&quot;: %2</source>
+ <translation>Kan ikke oprette arkivet &quot;%1&quot;: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::CreateShortcutOperation</name>
@@ -1151,6 +1179,62 @@ Fejl under indlæsning af %2</translation>
<source>Cannot find component for %1.</source>
<translation>Kan ikke finde komponent til %1.</translation>
</message>
+ <message>
+ <source>%1 of %2</source>
+ <translation>%1 af %2</translation>
+ </message>
+ <message>
+ <source>%1 downloaded.</source>
+ <translation>%1 downloadet.</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s), </source>
+ <translation>
+ <numerusform>%n dag, </numerusform>
+ <numerusform>%n dage, </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s), </source>
+ <translation>
+ <numerusform>%n time, </numerusform>
+ <numerusform>%n timer, </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation>
+ <numerusform>%n minut</numerusform>
+ <numerusform>%n minutter</numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation>
+ <numerusform>%n sekund</numerusform>
+ <numerusform>%n sekunder</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source> - %1%2%3%4 remaining.</source>
+ <translation> - %1%2%3%4 tilbage.</translation>
+ </message>
+ <message>
+ <source> - unknown time remaining.</source>
+ <translation> - ukendt tid tilbage.</translation>
+ </message>
+ <message>
+ <source>Archive: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Total: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1220,21 +1304,6 @@ Fejl under indlæsning af %2</translation>
</message>
</context>
<context>
- <name>QInstaller::ExtractArchiveOperation::Runnable</name>
- <message>
- <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
- <translation>Kan ikke åbne arkivet &quot;%1&quot; til læsning: %2</translation>
- </message>
- <message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Fejl under udpakning af arkivet &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Unknown exception caught while extracting &quot;%1&quot;.</source>
- <translation>Ukendt undtagelse fanget under udpakning af &quot;%1&quot;.</translation>
- </message>
-</context>
-<context>
<name>QInstaller::FakeStopProcessForUpdateOperation</name>
<message>
<source>Cannot get package manager core.</source>
@@ -1303,7 +1372,7 @@ Fejl under indlæsning af %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Fuldfører %1-assistenten</translation>
</message>
<message>
@@ -1311,7 +1380,7 @@ Fejl under indlæsning af %2</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Klik på %1 for at afslutte %2-assistenten.</translation>
</message>
<message>
@@ -1323,7 +1392,7 @@ Fejl under indlæsning af %2</translation>
<translation>Kør %1 nu.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>%1-assistenten mislykkedes.</translation>
</message>
</context>
@@ -1364,15 +1433,19 @@ Fejl under indlæsning af %2</translation>
<source>Cannot create directory &quot;%1&quot;: %2</source>
<translation>Kan ikke oprette mappen &quot;%1&quot;: %2</translation>
</message>
+ <message>
+ <source>Cannot prepare to backup file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Opsætning - %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Velkommen til opsætningsassistenten for %1.</translation>
</message>
<message>
@@ -1388,10 +1461,6 @@ Fejl under indlæsning af %2</translation>
<translation>Ingen tilgængelige opdateringer.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Kun lokal pakkehåndtering tilgængeligt.</translation>
- </message>
- <message>
<source>&amp;Add or remove components</source>
<translation>&amp;Tilføj eller fjern komponenter</translation>
</message>
@@ -1407,6 +1476,10 @@ Fejl under indlæsning af %2</translation>
<source>&amp;Quit</source>
<translation>&amp;Afslut</translation>
</message>
+ <message>
+ <source>There is an important update available. Please select &apos;%1&apos; first</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::LicenseAgreementPage</name>
@@ -1450,10 +1523,6 @@ Fejl under indlæsning af %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Kan ikke skrive licensfilen &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Ingen licensfil fundet til sletning.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1477,10 +1546,6 @@ Fejl under indlæsning af %2</translation>
<translation>Manglende pakkehåndterings-kernemotor.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Forbedreder download af metainformation...</translation>
- </message>
- <message>
<source>Unknown exception during extracting.</source>
<translation>Ukendt undtagelse under udpakning.</translation>
</message>
@@ -1509,10 +1574,6 @@ Fejl under indlæsning af %2</translation>
<translation>Fejl under udpakning af arkivet &quot;%1&quot;: %2</translation>
</message>
<message>
- <source>Unknown exception caught while extracting archive &quot;%1&quot;.</source>
- <translation>Ukendt undtagelse fanget under udpakning af arkivet &quot;%1&quot;.</translation>
- </message>
- <message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>Kan ikke åbne filen &quot;%1&quot; til læsning: %2</translation>
</message>
@@ -1525,17 +1586,48 @@ Fejl under indlæsning af %2</translation>
<translation>Tjeksum uoverensstemmelse registreret for &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Henter metainformation fra fjern-repository... %1/%2 </translation>
+ <source>Metadata download canceled.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Henter metainformation fra fjern-repository... </translation>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Metadata download canceled.</source>
+ <source>Fetching latest update information...</source>
<translation type="unfinished"></translation>
</message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Kan ikke åbne filen &quot;%1&quot; til skrivning: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Henter information fra fjern-repository...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Henter metainformation fra fjern-repository...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1544,10 +1636,8 @@ Fejl under indlæsning af %2</translation>
<translation>Fejl ved skrivning af vedligeholdelsesværktøj</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Downloader pakker...</translation>
+ <source>Downloading packages...</source>
+ <translation>Downloader pakker...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1652,31 +1742,19 @@ Vil du fortsætte?</translation>
<translation>Kan ikke løse alle afhængigheder.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Komponenter som er ved at blive fjernet.</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Component %1 already installed
-</source>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
+ <source>Component %1 already installed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
+ <source>Cannot install %1. Component is virtual.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Running processes found.</source>
+ <source>Cannot install %1. Component not found.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1708,10 +1786,6 @@ Vil du fortsætte?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation type="unfinished"></translation>
</message>
@@ -1720,13 +1794,11 @@ Vil du fortsætte?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1738,11 +1810,35 @@ Vil du fortsætte?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
+ <source>Invalid</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Invalid</source>
+ <source>Components about to be removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1861,16 +1957,12 @@ Vil du fortsætte?</translation>
<translation>Opretter vedligeholdelsesværktøj</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Installation færdig!</translation>
+ <source>Installation finished!</source>
+ <translation>Installation færdig!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Installation afbrudt!</translation>
+ <source>Installation aborted!</source>
+ <translation>Installation afbrudt!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1881,16 +1973,12 @@ Installation afbrudt!</translation>
<translation>Fjerner fravalgte komponenter...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Opdatering færdig!</translation>
+ <source>Update finished!</source>
+ <translation>Opdatering færdig!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Opdatering afbrudt!</translation>
+ <source>Update aborted!</source>
+ <translation>Opdatering afbrudt!</translation>
</message>
<message>
<source>Cannot create target directory for installer.</source>
@@ -1925,10 +2013,8 @@ Opdatering afbrudt!</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Installerer komponenten %1</translation>
+ <source>Installing component %1</source>
+ <translation>Installerer komponenten %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1961,10 +2047,6 @@ Installerer komponenten %1</translation>
<translation>Kan ikke hente metainformation: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Kan ikke tilføje kildeinformation for midlertidig opdatering.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Kan ikke finde nogen kildeinformation for opdatering.</translation>
</message>
@@ -1993,6 +2075,50 @@ Installerer komponenten %1</translation>
%1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2096,6 +2222,18 @@ Kopiér venligst installeren til et lokalt drev</translation>
<source>Uninstalling</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2139,7 +2277,7 @@ Kopiér venligst installeren til et lokalt drev</translation>
<translation>Klar til afinstallation</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Opsætningen er nu klar til at begynde fjernelsen af %1 fra din computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;Programmappen %2 slettes fuldstændigt&lt;/font&gt;, inklusiv alt indhold i mappen!</translation>
</message>
<message>
@@ -2151,7 +2289,7 @@ Kopiér venligst installeren til et lokalt drev</translation>
<translation>Klar til opdateringspakker</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>Opsætningen er nu klar til at begynde opdateringen af din installation.</translation>
</message>
<message>
@@ -2163,13 +2301,25 @@ Kopiér venligst installeren til et lokalt drev</translation>
<translation>Klar til installation</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Opsætningen er nu klar til at begynde installationen af %1 på din computer.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2226,7 +2376,7 @@ Kopiér venligst installeren til et lokalt drev</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Fuldfører opsætningsassistenten for %1</translation>
</message>
</context>
@@ -2449,6 +2599,14 @@ Kopiér venligst installeren til et lokalt drev</translation>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2492,7 +2650,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Kan ikke åbne indstillingsfilen %1 til læsning: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2620,16 +2778,40 @@ or accept the elevation of access rights if being asked.</source>
</message>
<message>
<source>Select All</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">Vælg alle</translation>
</message>
<message>
<source>Deselect All</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">Fravælg alle</translation>
</message>
<message>
<source>The server&apos;s URL that contains a valid repository.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2691,7 +2873,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Fejl</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2701,6 +2883,22 @@ or accept the elevation of access rights if being asked.</source>
<source>Extracting &quot;%1&quot;</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Kan ikke åbne arkivet &quot;%1&quot; til læsning: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2709,4 +2907,189 @@ or accept the elevation of access rights if being asked.</source>
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation::Worker</name>
+ <message>
+ <source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Kan ikke åbne arkivet &quot;%1&quot; til læsning: %2</translation>
+ </message>
+ <message>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Fejl under udpakning af arkivet &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Kan ikke åbne filen &quot;%1&quot; til skrivning: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Kan ikke åbne filen &quot;%1&quot; til læsning: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_de.ts b/src/sdk/translations/ifw_de.ts
index 7d1ca4f4b..ad80c6624 100644
--- a/src/sdk/translations/ifw_de.ts
+++ b/src/sdk/translations/ifw_de.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Konnte fehlende Abhängigkeit &apos;%1&apos; für &apos;%2&apos; nicht finden.</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>Die Abhängigkeiten sind nicht auflösbar. Die zur forcierten Installation vorgesehene Komponente &quot;%1&quot; würde deinstalliert werden, weil sie von Komponente &quot;%2&quot; abhängt, welche auf Grund von &quot;%3&quot; zur Deinstallation vorgesehen ist.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>Durch den Alias &quot;%1&quot; ausgewählte Komponenten</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>Es wurde eine Rekursion festegestellt; der Alias &quot;%1&quot; wurde bereits hinzugefügt.</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -306,6 +318,10 @@
<source>Try again</source>
<translation>Erneut versuchen</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>Kann %1 nicht herunterladen. Es kann kein Verzeichnis für &quot;%2&quot; erstellt werden</translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -489,10 +505,6 @@
<translation>Konnte Datei &quot;%1&quot; nicht lesen</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Ungültiges XML in Datei %1, Zeile %2, Spalte %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Unerwartetes Wurzelelement %1, erwartet wird &quot;UpdateSources&quot;.</translation>
</message>
@@ -744,6 +756,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>Ungültiger Inhalt in &quot;%1&quot;.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>Das kann durch Neustart der Anwendung behoben werden; nach Löschen des Caches von:</translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -752,18 +768,6 @@
<translation>Komponenten können im Updater-Modus keine Kinder haben.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Konnte angeforderte UI-Datei &apos;%1&apos; nicht öffnen: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Konnte angeforderte UI-Datei &apos;%1&apos; nicht laden: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Konnte angeforderte Lizenzdatei &apos;%1&apos; nicht öffnen: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Fehler</translation>
</message>
@@ -783,6 +787,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>Es ist ein Fehler beim Laden der ausgewählten Komponente aufgetreten. Diese Komponente kann nicht installiert werden.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Konnte angeforderte UI-Datei &quot;%1&quot; nicht öffnen: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Konnte angeforderte UI-Datei &quot;%1&quot; nicht laden: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Konnte angeforderte Lizenzdatei &quot;%1&quot; nicht öffnen: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -830,52 +858,32 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>St&amp;andard</translation>
+ <source>Default</source>
+ <translation>Standard</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Wählt die Standardkomponenten in der Baumansicht aus.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+Z</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;Zurücksetzen</translation>
+ <source>Reset</source>
+ <translation>Zurücksetzen</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Setzt alle Komponenten in der Baumansicht auf die ursprüngliche Auswahl zurück.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>Alle au&amp;swählen</translation>
+ <source>Select All</source>
+ <translation>Alle auswählen</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Wählt alle Komponenten in der Baumansicht aus.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+B</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>Alle a&amp;bwählen</translation>
+ <source>Deselect All</source>
+ <translation>Alle abwählen</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
@@ -890,10 +898,6 @@
<translation>Datei öffnen</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>Durchsuche QBSP Dateien</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Wählen Sie eine Qt Board Support Package-Datei, um zusätzliche Inhalte zu installieren, die nicht direkt in den Online-Repositories verfügbar sind.</translation>
</message>
@@ -922,13 +926,33 @@
<translation>Bitte wählen Sie die Komponenten aus, die Sie entfernen möchten.</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>Bitte wählen Sie die Komponenten aus, die Sie installieren möchten. Wählen Sie die Komponenten ab, die Sie entfernen möchten.</translation>
- </message>
- <message>
<source>Mandatory components need to be updated first before you can select other components to update.</source>
<translation>Obligatorische Komponenten müssen zuerst aktualisiert werden, bevor andere Komponenten zur Aktualisierung ausgewählt werden können.</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>Durchsuche QBSP Dateien</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>Auswählen</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Fehler</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Offline-Installer Erstellen</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>Erstellt einen Offline-Installer von den ausgewählten Komponenten anstatt zu installieren</translation>
+ </message>
+ <message>
+ <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>Bitte wählen Sie die Komponenten aus, die Sie installieren möchten. Wählen Sie die Komponenten ab, die Sie entfernen möchten.</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentSelectionPagePrivate</name>
@@ -941,8 +965,8 @@
<translation>Fehler</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Komponenteninformationen</translation>
+ <source>Information</source>
+ <translation>Informationen</translation>
</message>
</context>
<context>
@@ -1141,14 +1165,6 @@
<translation>Fehler beim Herunterladen</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>Prüfsumme ungültig beim Herunterladen. Dies ist ein kurzzeitiger Fehler, bitte erneut versuchen.</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>Prüfsumme konnte nicht geprüft werden</translation>
- </message>
- <message>
<source>Cannot download archive %1: %2</source>
<translation>Konnte Archiv %1 nicht herunterladen: %2</translation>
</message>
@@ -1222,6 +1238,28 @@ Fehler beim Laden von %2</translation>
<source>Archive: </source>
<translation>Archiv: </translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>Anzahl der Wiederholungen (%1) überschritten</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>Prüfsumme ungültig beim Herunterladen. Dies ist ein kurzzeitiger Fehler, bitte erneut versuchen.
+
+Erwartet: %1
+Heruntergeladen: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>Prüfsumme konnte nicht geprüft werden
+Erwartet: %1
+Heruntergeladen: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1382,7 +1420,7 @@ Fehler beim Laden von %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Den %1-Assistent abschließen</translation>
</message>
<message>
@@ -1390,7 +1428,7 @@ Fehler beim Laden von %2</translation>
<translation>Abschließen</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Klicken Sie %1, um den %2 Assistenten zu beenden.</translation>
</message>
<message>
@@ -1402,7 +1440,7 @@ Fehler beim Laden von %2</translation>
<translation>Starte jetzt %1.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Der %1-Assistent ist fehlgeschlagen.</translation>
</message>
</context>
@@ -1451,11 +1489,11 @@ Fehler beim Laden von %2</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Einrichten - %1</translation>
+ <source>Welcome</source>
+ <translation>Willkommen</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Willkommen zum %1-Einrichtungsassistenten.</translation>
</message>
<message>
@@ -1483,10 +1521,6 @@ Fehler beim Laden von %2</translation>
<translation>Keine Aktualisierungen verfügbar.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Nur lokale Paketverwaltung verfügbar.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Beenden</translation>
</message>
@@ -1538,10 +1572,6 @@ Fehler beim Laden von %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Konnte Lizenzdatei &quot;%1&quot; nicht schreiben.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Keine Lizenzdateien zum Löschen gefunden.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1565,10 +1595,6 @@ Fehler beim Laden von %2</translation>
<translation>Fehlende Paketmanager-Kernkomponente.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Herunterladen der Metainformationen wird vorbereitet ...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Entpacken des komprimierten Repository, Das könnte eine Weile dauern...</translation>
</message>
@@ -1605,14 +1631,6 @@ Fehler beim Laden von %2</translation>
<translation>Checksummen stimmen nicht überein &apos;%1&apos;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Metainformationen werden vom Installationsserver empfangen... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Metainformationen werden vom Installationsserver empfangen ... </translation>
- </message>
- <message>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>Nicht unterstütztes Archivformat: &quot;%1&quot;: Für die Dateiendung &quot;%2&quot; ist keine Behandlungsroutine registriert.</translation>
</message>
@@ -1624,6 +1642,41 @@ Fehler beim Laden von %2</translation>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>Kann die Datei &quot;%1&quot; nicht zum Lesen öffnen: %2</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>Hole neue Update-Information...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>%n Element im lokalen Cache erneuert...</numerusform>
+ <numerusform>%n Elemente im lokalen Cache erneuert...</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>Das Löschen des Cache-Verzeichnisses and Neustarten der Anwendung kann das beheben.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>Unbekannte Ausnahmebedingung beim Aktualisieren des Caches</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>Die extrahierte Datei &quot;%1&quot; konnte nicht zum Lesen geöffnet werden: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Die Datei &quot;%1&quot; konnte nicht zum Schreiben geöffnet werden: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Informationen werden vom Installationsserver empfangen...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Metainformationen werden vom Installationsserver empfangen...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1632,10 +1685,8 @@ Fehler beim Laden von %2</translation>
<translation>Fehler beim Schreiben des Verwaltungswerkzeugs</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Pakete werden heruntergeladen ...</translation>
+ <source>Downloading packages...</source>
+ <translation>Pakete werden heruntergeladen ...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1748,38 +1799,24 @@ Möchten Sie trotzdem fortsetzen?</translation>
<translation>Konnte nicht alle Abhängigkeiten auflösen.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>Komponente %1 kann nicht installiert werden. Die Komponente wird nur als automatische Abhängigkeit von %2 installiert.
-</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>Komponente %1 kann nicht installiert werden. Die Komponente kann nicht ausgewählt werden, d.&#xa0;h., Sie müssen eine der Unterkomponenten auswählen.
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>Komponente %1 kann nicht installiert werden. Die Komponente wird nur als automatische Abhängigkeit von %2 installiert.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>Komponente %1 ist bereits installiert
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>Komponente %1 kann nicht installiert werden. Die Komponente kann nicht ausgewählt werden, d.&#xa0;h., Sie müssen eine der Unterkomponenten auswählen.</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>%1 kann nicht installiert werden. Die Komponente ist virtuell.
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>Komponente %1 ist bereits installiert</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>%1 kann nicht installiert werden. Komponente nicht gefunden.
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>%1 kann nicht installiert werden. Die Komponente ist virtuell.</translation>
</message>
<message>
- <source>Running processes found.</source>
- <translation>Laufende Prozesse gefunden.</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>%1 kann nicht installiert werden. Komponente nicht gefunden.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1802,10 +1839,6 @@ Möchten Sie trotzdem fortsetzen?</translation>
<translation>Nicht genügend Festplattenplatz für alle ausgewählten Komponenten! Verfügbarer Platz: %1, mindestens benötigt: %2.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>Nicht genügend Festplattenplatz für temporäre Dateien! Verfügbarer Platz: %1, mindestens benötigt: %2.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>Das für die Installation ausgewählte Laufwerk scheint über genügend Speicherplatz für die Installation zu verfügen, aber anschließend ist weniger als 1% des Speicherplatzes verfügbar.</translation>
</message>
@@ -1822,10 +1855,8 @@ Möchten Sie trotzdem fortsetzen?</translation>
<translation>ungültig</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>%1 kann nicht installiert werden. Die Komponente ist Nachkomme einer virtuellen Komponente %2.
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>%1 kann nicht installiert werden. Die Komponente ist Nachkomme einer virtuellen Komponente %2.</translation>
</message>
<message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
@@ -1836,10 +1867,28 @@ Möchten Sie trotzdem fortsetzen?</translation>
<translation>Zu löschende Komponenten:</translation>
</message>
<message>
- <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.
-</source>
- <translation>Komponente %1 kann nicht installiert werden. Es gab ein Problem beim Laden, daher wird sie als instabil gekennzeichnet und kann nicht ausgewählt werden.
-</translation>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>Komponente %1 kann nicht installiert werden. Es gab ein Problem beim Laden, daher wird sie als instabil gekennzeichnet und kann nicht ausgewählt werden.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>Es gibt nicht ausreichend Plattenplatz, um die temporären Dateien zu speichern! %1 sind verfügbar, aber das erforderliche Minimum ist %2. Sie können einen anderen Speicherort für die temporären Dateien auswählen, indem Sie den lokalen Cache-Pfad in den Installationseinstellungen ändern.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>Die zu deinstallierenden Komponenten konnten nicht aufgelöst werden.</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>Der Alias %1 kann nicht ausgewählt werden. Beim Laden trat ein Problem auf; er wurde daher als instabil gekennzeichnet und kann nicht ausgewählt werden.</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>Der Alias %1 kann nicht ausgewählt werden. Er ist als virtuell gekennzeichnet und kann nicht daher nicht manuell ausgewählt werden.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>Der erstellte Installer wird %1 Festplattenplatz verwenden.</translation>
</message>
</context>
<context>
@@ -1957,16 +2006,12 @@ Möchten Sie trotzdem fortsetzen?</translation>
<translation>Temporäre Datei &quot;%1&quot; konnte nicht entfernt werden: %2</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Installation abgeschlossen!</translation>
+ <source>Installation finished!</source>
+ <translation>Installation abgeschlossen!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Installation abgebrochen!</translation>
+ <source>Installation aborted!</source>
+ <translation>Installation abgebrochen!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1977,16 +2022,12 @@ Installation abgebrochen!</translation>
<translation>Abgewählte Komponenten werden entfernt ...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Aktualisierung beendet!</translation>
+ <source>Update finished!</source>
+ <translation>Aktualisierung beendet!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Aktualisierung abgebrochen!</translation>
+ <source>Update aborted!</source>
+ <translation>Aktualisierung abgebrochen!</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -2029,10 +2070,8 @@ Aktualisierung abgebrochen!</translation>
<translation>Offline-Generierung abgebrochen!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Komponente %1 wird installiert</translation>
+ <source>Installing component %1</source>
+ <translation>Komponente %1 wird installiert</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2079,10 +2118,6 @@ Komponente %1 wird installiert</translation>
<translation>Konnte die Metainformationen nicht empfangen: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Konnte Informationen nicht zu temporären Aktualisierungsquellen hinzufügen.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Konnte keine Informationen zu Aktualisierungsquellen finden.</translation>
</message>
@@ -2091,20 +2126,16 @@ Komponente %1 wird installiert</translation>
<translation>Zyklische Abhängigkeit zwischen Komponenten entdeckt: &apos;%1&apos; und &apos;%2&apos;.</translation>
</message>
<message>
- <source>
-Preparing to unpack components...</source>
- <translation>
-Bereite Auspacken der Komponenten vor...</translation>
+ <source>Preparing to unpack components...</source>
+ <translation>Bereite Auspacken der Komponenten vor...</translation>
</message>
<message>
<source>%1 of %2 operations completed.</source>
<translation>%1 von %2 Schritten fertiggestellt.</translation>
</message>
<message>
- <source>
-Unpacking components...</source>
- <translation>
-Packe Komponenten aus...</translation>
+ <source>Unpacking components...</source>
+ <translation>Packe Komponenten aus...</translation>
</message>
<message>
<source>%1 of %2 operations rolled back.</source>
@@ -2122,6 +2153,22 @@ Packe Komponenten aus...</translation>
<source>All components installed.</source>
<translation>Alle Komponenten installiert.</translation>
</message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>Lade Komponenten-Skripte...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>Der Alias verwendet einen Namen, der mit der bereits existierenden Komponte &quot;%1&quot; kollidiert</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>Unaufgelöste Komponenten-Aliasse</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>Es wurde eine zyklische Abhängigkeit zwischen den Aliassen &quot;%1&quot; und &quot;%2&quot; festgestellt.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2225,6 +2272,18 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
<source>Uninstalling</source>
<translation>Wird deinstalliert</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>Offline-Installer &amp;Erstellen</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>Erstelle Offline-Installer für %1</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>Erstelle Offline-Installer</translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2275,7 +2334,7 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
<translation>Bereit zum Deinstallieren</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Das Einrichtungsprogramm ist jetzt bereit, %1 von Ihrem Computer zu entfernen. &lt;br&gt;&lt;font color=&quot;red&quot;&gt;Das Programmverzeichnis %2 wird vollständig gelöscht&lt;/font&gt;, inklusive allen Inhalten in diesem Verzeichnis!</translation>
</message>
<message>
@@ -2287,7 +2346,7 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
<translation>Bereit zum Aktualisieren der Pakete</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>Das Einrichtungsprogramm ist jetzt bereit, Ihre Installation zu aktualisieren.</translation>
</message>
<message>
@@ -2299,13 +2358,25 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
<translation>Bereit zum Installieren</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Das Einrichtungsprogramm ist jetzt bereit, %1 auf Ihrem Computer zu installieren.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Bereit für Aktualisierung</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Offline-Installer Erstellen</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>Bereit zum Erstellen des Offline-Installers</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>Die erforderlichen Informationen zum Erstellen eines Offline-Installers für die ausgewählten Komponenten stehen bereit.</translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2362,7 +2433,7 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Der %1-Assistent wird abgeschlossen</translation>
</message>
</context>
@@ -2590,6 +2661,10 @@ Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation>
<source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
<translation>Ungültiger Wert für &apos;max-concurrent-operations&apos;.</translation>
</message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>Leerer Wert für Option &apos;Cache-Pfad&apos;.</translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2633,8 +2708,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Konnte Einstellungsdatei %1 nicht zum Lesen öffnen: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Kategorien auswählen</translation>
+ <source>Categories</source>
+ <translation>Kategorien</translation>
</message>
</context>
<context>
@@ -2771,6 +2846,30 @@ or accept the elevation of access rights if being asked.</source>
<source>Deselect All</source>
<translation>Alle abwählen</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>Lokaler Cache</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>Die Meta-Information der entfernten Repositories wird auf der Festplatte zwischengespeichert, um die Ladezeiten zu verbessern. Sie können ein anderes Verzeichnis für diesen Cache festlegen oder den Inhalt des aktuellen Caches löschen.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>Pfad für Cache:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>Löscht den Inhalt des Cache-Verzeichnisses</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>Cache löschen</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>Lösche Cache...</translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2870,10 +2969,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>Konnte Datei &quot;%1&quot; nicht zum Schreiben öffnen: %2</translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot;for reading: %2</source>
- <translation>Konnte Datei &quot;%1&quot; nicht zum Lesen öffnen: %2</translation>
- </message>
- <message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>Konnte Datei &quot;%1&quot; nicht zum Lesen öffnen: %2</translation>
</message>
@@ -2916,4 +3011,98 @@ or accept the elevation of access rights if being asked.</source>
<translation>Über %1 Verwaltungswerkzeug</translation>
</message>
</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>Der Cache kann nicht mit einem leeren Pfad initialisiert werden.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>Das Verzeichnis %1 für den Cache konnte nicht angelegt werden.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>Cache kann nicht initialisiert werden: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>Ungültiger Cache kann nicht gelöscht werden.</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>Manifest-Datei kann nicht gelöscht werden: %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>Fehler beim Löschen des Caches: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>Es können keine Elemente vom ungültigem Cache geholt werden.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>Es kann kein Element vom ungültigem Cache geholt werden.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>Es kann kein Element im ungültigem Cache registriert werden.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>Leeres Element kann nicht registriert werden.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>Ungültiges Element mit Prüfsumme %1 kann nicht registriert werden.</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>Das Element mit der Prüfsumme %1 kann nicht registriert werden. Es existiert bereits ein Element mit der gleichen Prüfsumme im Cache.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>Fehler beim Kopieren eines Elements in das Verzeichnis &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>Es kann kein Element aus dem ungültigem Cache entfernt werden.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>Das Element mit der Prüfsumme %1 kann nicht gelöscht werden: Das Element existiert nicht.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>Fehler beim Löschen des Verzeichnisses &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>Fehler beim Markieren des Caches als ungültig: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>Manifest-Datei kann nicht geöffnet werden: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>Der Inhalt der Manifest-Datei konnte nicht geschrieben werden: %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>Ungültiger Cache kann nicht synchronisiert werden.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>Unbekannter Registrierungsmodus ausgewählt!</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>Cache wurde erfolgreich gelöscht!</translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_es.ts b/src/sdk/translations/ifw_es.ts
index 0985078f2..69369064f 100644
--- a/src/sdk/translations/ifw_es.ts
+++ b/src/sdk/translations/ifw_es.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>No se puede encontrar la dependencia &quot;%1&quot; que falta para &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>Resolución de dependencia imposible detectada. El componente de instalación forzada &quot;%1&quot; se desinstalaría porque su dependencia &quot;%2&quot; está marcada para desinstalación con el motivo: &quot;%3&quot;.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>Compoentes seleccionados por el alias &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>Recursión detectada, el alias del componente &quot;%1&quot; ya se agregó.</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -306,6 +318,10 @@
<source>Try again</source>
<translation>Volver a intentar</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>No se puede descargar %1. No se puede crear el directorio para &quot;%2&quot;</translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -489,10 +505,6 @@
<translation>No se puede leer &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Error de análisis en %1 en %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Elemento raíz %1 inesperado, debería ser &quot;Updates&quot;.</translation>
</message>
@@ -744,6 +756,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>Contenido inválido en &quot;%1&quot;.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>Esto puede solucionarse reiniciando la aplicación después de borrar el caché de:</translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -752,18 +768,6 @@
<translation>Los componentes no pueden tener elementos secundarios en el modo actualizador.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>No se puede abrir el archivo de UI &quot;%1&quot; solicitado: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>No se puede cargar el archivo de UI &quot;%1&quot; solicitado: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>No se puede abrir el archivo de licencia &quot;%1&quot; solicitado: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -783,6 +787,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>Se ha producido un error cargando el componente seleccionado. Este componente no se puede instalar.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>No se puede abrir el archivo de UI &quot;%1&quot; solicitado: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>No se puede cargar el archivo de UI &quot;%1&quot; solicitado: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>No se puede abrir el archivo de licencia &quot;%1&quot; solicitado: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -830,62 +858,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>Predetermin&amp;ado</translation>
+ <source>Default</source>
+ <translation>Predeterminado</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Selecciona los componentes predeterminados en la vista de árbol.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;Restablecer</translation>
+ <source>Reset</source>
+ <translation>Restablecer</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Restablece todos los componentes a su estado de selección original en la vista de árbol.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>&amp;Seleccionar todo</translation>
+ <source>Select All</source>
+ <translation>Seleccionar todo</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Selecciona todos los componentes en la vista de árbol.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>Anular selección de to&amp;do</translation>
+ <source>Deselect All</source>
+ <translation>Anular selección de todo</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>Anula la selección de todos los componentes en la vista de árbol.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Examinar archivos QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Selecciona un archivo de paquete de soporte de Qt Board para instalar contenido adicional que no esté disponible directamente en los repositorios en línea.</translation>
</message>
@@ -918,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>
@@ -929,6 +933,26 @@
<source>Search</source>
<translation>Buscar</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>Examinar archivos &amp;QBSP</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>Seleccionar</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Crear instalador sin conexión</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>Crea un instalador sin conexión a partir de componentes seleccionados, en lugar de instalarlo ahora.</translation>
+ </message>
</context>
<context>
<name>QInstaller::ConsumeOutputOperation</name>
@@ -1126,14 +1150,6 @@
<translation>Error de descarga</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>Error de verificación del hash durante la descarga. Es un error temporal, vuelva a intentarlo.</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>No se puede verificar el hash</translation>
- </message>
- <message>
<source>Cannot download archive %1: %2</source>
<translation>No se puede descargar el archivo %1: %2</translation>
</message>
@@ -1207,6 +1223,28 @@ Error al descargar %2</translation>
<source>Total: </source>
<translation>Total: </translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>Se superó el recuento de reintentos (%1)</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>Error de verificación del hash durante la descarga. Es un error temporal, vuelva a intentarlo.
+
+Esperados: %1
+Descargado: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>No se puede verificar el hash
+Esperados: %1
+Descargado: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1344,7 +1382,7 @@ Error al descargar %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Completando el Asistente de %1</translation>
</message>
<message>
@@ -1352,7 +1390,7 @@ Error al descargar %2</translation>
<translation>Terminado</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Haga clic en %1 para salir del asistente de %2.</translation>
</message>
<message>
@@ -1364,7 +1402,7 @@ Error al descargar %2</translation>
<translation>Ejecute %1 ahora.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Error del Asistente de %1.</translation>
</message>
</context>
@@ -1413,11 +1451,11 @@ Error al descargar %2</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Programa de instalación - %1</translation>
+ <source>Welcome</source>
+ <translation>Bienvenido</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Bienvenido al Asistente de instalación de %1.</translation>
</message>
<message>
@@ -1445,10 +1483,6 @@ Error al descargar %2</translation>
<translation>No hay actualizaciones disponibles.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Solo está disponible la administración de paquetes locales.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Salir</translation>
</message>
@@ -1499,10 +1533,6 @@ Error al descargar %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>No se puede escribir en el archivo de licencia &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>No se han encontrado archivos de licencia para eliminar.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1526,10 +1556,6 @@ Error al descargar %2</translation>
<translation>Falta el motor de componente básico del administrador de paquetes.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Preparando la descarga de la información de metadatos...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Desempaquetando los repositorios comprimidos. Esta operación puede tardar...</translation>
</message>
@@ -1566,14 +1592,6 @@ Error al descargar %2</translation>
<translation>Discrepancia de suma de comprobación detectada para &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Recuperando información de metadatos del repositorio remoto... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Recuperando información de metadatos del repositorio remoto... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>Error al extraer el archivo &quot;%1&quot;: %2</translation>
</message>
@@ -1585,6 +1603,41 @@ Error al descargar %2</translation>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>Archivo &quot;%1&quot; no soportado: ningún controlador registrado para el sufijo de archivo &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>Obteniendo la información de la última actualización...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>Actualizando caché local con %n elemento nuevo...</numerusform>
+ <numerusform>Actualizando caché local con %n elementos nuevos...</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>Borrando el directorio de caché y reiniciando la aplicación puede resolver esto.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>Excepción desconocida durante la actualización de caché.</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>No se puede abrir el archivo extraído &quot;%1&quot; para leer: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>No se puede abrir el archivo &apos;%1&apos; para la escritura: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Recuperando información de repositorios remotos...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Recuperando información de metadatos del repositorio remoto...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1593,10 +1646,8 @@ Error al descargar %2</translation>
<translation>Error al escribir la herramienta de mantenimiento</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Descargando paquetes...</translation>
+ <source>Downloading packages...</source>
+ <translation>Descargando paquetes...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1705,38 +1756,24 @@ No es recomendable instalar en este directorio, ya que la instalación podría g
<translation>No se pueden resolver todas las dependencias.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>No se puede instalar el componente %1. El componente se instala sólo como dependencia automática de %2.
-</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>No se puede instalar el componente %1. El componente no es seleccionable, lo que significa que tiene que seleccionar uno de los subcomponentes.
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>No se puede instalar el componente %1. El componente se instala sólo como dependencia automática de %2.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>El componente %1 ya está instalado
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>No se puede instalar el componente %1. El componente no es seleccionable, lo que significa que tiene que seleccionar uno de los subcomponentes.</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>No se puede instalar %1. El componente es virtual.
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>El componente %1 ya está instalado</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>No se puede instalar %1. No se encuentra el componente.
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>No se puede instalar %1. El componente es virtual.</translation>
</message>
<message>
- <source>Running processes found.</source>
- <translation>Se han encontrado procesos en ejecución.</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>No se puede instalar %1. No se encuentra el componente.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1759,10 +1796,6 @@ No es recomendable instalar en este directorio, ya que la instalación podría g
<translation>No hay suficiente espacio en disco para almacenar todos los componentes seleccionados. Se dispone de %1 y se requiere al menos un espacio de %2.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>No hay suficiente espacio en disco para almacenar los archivos temporales. Se dispone de %1 y se requiere al menos un espacio de %2.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>El volumen seleccionado para la instalación parece tener espacio suficiente para la instalación, pero después habrá menos del 1% del espacio del volumen disponible.</translation>
</message>
@@ -1783,10 +1816,8 @@ No es recomendable instalar en este directorio, ya que la instalación podría g
<translation>Se requiere la entrada del usuario pero el dispositivo de salida no está asociado a un terminal.</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>No se puede instalar %1. El componente es descendiente de un componente virtual %2.
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>No se puede instalar %1. El componente es descendiente de un componente virtual %2.</translation>
</message>
<message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
@@ -1797,10 +1828,29 @@ No es recomendable instalar en este directorio, ya que la instalación podría g
<translation>Componentes a punto de ser removidos:</translation>
</message>
<message>
- <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.
-</source>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
<translation>No se puede instalar el componente %1. Hubo un problema al cargar este componente, por lo que está marcado como inestable y no se puede seleccionar.</translation>
</message>
+ <message>
+ <source>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.</source>
+ <translation>¡No hay suficiente espacio en disco para almacenar archivos temporales! %1 están disponibles, mientras que el mínimo requerido es %2. Puede seleccionar otra ubicación para los archivos temporales modificando la ruta de caché local desde la configuración del instalador.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>No se pueden resolver los componentes para desinstalar.</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>No se puede seleccionar el alias %1. Hubo un problema al cargar este alias, por lo que está marcado como inestable y no se puede seleccionar.</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>No se puede seleccionar %1. El alias está marcado como virtual, lo que significa que no se puede seleccionar manualmente.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>El instalador creado utilizará %1 del espacio en disco.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1917,16 +1967,12 @@ No es recomendable instalar en este directorio, ya que la instalación podría g
<translation>Creando herramienta de mantenimiento</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Instalación completada.</translation>
+ <source>Installation finished!</source>
+ <translation>Instalación completada.</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Instalación cancelada.</translation>
+ <source>Installation aborted!</source>
+ <translation>Instalación cancelada.</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1937,16 +1983,12 @@ Instalación cancelada.</translation>
<translation>Eliminando componentes anulados de la selección...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Actualización completada.</translation>
+ <source>Update finished!</source>
+ <translation>Actualización completada.</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Actualización cancelada.</translation>
+ <source>Update aborted!</source>
+ <translation>Actualización cancelada.</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1989,10 +2031,8 @@ Actualización cancelada.</translation>
<translation>¡Generación fuera de línea cancelada!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Instalando componente %1</translation>
+ <source>Installing component %1</source>
+ <translation>Instalando componente %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2039,10 +2079,6 @@ Instalando componente %1</translation>
<translation>No se puede recuperar la información de los metadatos: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>No se puede agregar la información de la fuente de actualización temporal.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>No se puede encontrar ninguna información de fuente de actualización.</translation>
</message>
@@ -2051,8 +2087,7 @@ Instalando componente %1</translation>
<translation>Se ha detectado un ciclo de dependencia entre componentes los &quot;%1&quot; y &quot;%2&quot;.</translation>
</message>
<message>
- <source>
-Preparing to unpack components...</source>
+ <source>Preparing to unpack components...</source>
<translation>Preparándose para desempaquetar componentes...</translation>
</message>
<message>
@@ -2060,8 +2095,7 @@ Preparing to unpack components...</source>
<translation>%1 de %2 operaciones completadas.</translation>
</message>
<message>
- <source>
-Unpacking components...</source>
+ <source>Unpacking components...</source>
<translation>Desempaquetando componentes...</translation>
</message>
<message>
@@ -2080,6 +2114,22 @@ Unpacking components...</source>
<source>All components installed.</source>
<translation>Todos los componentes fueron instalados.</translation>
</message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>Cargando scripts de componentes...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>El alias declara un nombre que entra en conflicto con un componente existente &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>Alias de componentes no resueltos</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>Se detectó dependencia cíclica entre los alias &quot;%1&quot; y &quot;%2&quot;.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2183,6 +2233,18 @@ Copie el instalador en una unidad local</translation>
<source>Uninstalling</source>
<translation>Desinstalando</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>&amp;Crear instalador sin conexión</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>Creando un instalador sin conexión para %1</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>Creando un instalador sin conexión</translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2226,7 +2288,7 @@ Copie el instalador en una unidad local</translation>
<translation>Preparado para desinstalar</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>El programa de instalación está preparado para empezar a eliminar %1 del equipo.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;El directorio del programa %2 se eliminará completamente&lt;/font&gt;, incluido todo el contenido del directorio.</translation>
</message>
<message>
@@ -2238,7 +2300,7 @@ Copie el instalador en una unidad local</translation>
<translation>Preparado para actualizar paquetes</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>El programa de instalación está preparado para empezar a actualizar la instalación.</translation>
</message>
<message>
@@ -2250,13 +2312,25 @@ Copie el instalador en una unidad local</translation>
<translation>Preparado para instalar</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>El programa de instalación está preparado para empezar a instalar %1 en su equipo.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Listo para la actualización</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Crear instalador sin conexión</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>Listo para crear instalador sin conexión</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>Toda la información requerida ahora está disponible para crear un instalador sin conexión para los componentes seleccionados.</translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2313,7 +2387,7 @@ Copie el instalador en una unidad local</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Completando el Asistente de instalación de %1</translation>
</message>
</context>
@@ -2540,6 +2614,10 @@ Copie el instalador en una unidad local</translation>
<source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
<translation>Valor inválido para &apos;max-concurrent-operations&apos;.</translation>
</message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>Valor vacío para la opción &apos;cache-path&apos;.</translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2586,8 +2664,8 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<translation>No se puede abrir el archivo de configuración %1 para la lectura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Seleccione las categorías</translation>
+ <source>Categories</source>
+ <translation>Categorías</translation>
</message>
</context>
<context>
@@ -2724,6 +2802,30 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<source>Deselect All</source>
<translation>Anular seleccionar todo</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>Caché local</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>La metainformación de los repositorios remotos se almacena en caché en el disco para mejorar los tiempos de carga. Puede seleccionar otro directorio para almacenar el caché o borrar el contenido del caché actual.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>Ruta para el caché:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>Elimina el contenido del directorio de caché</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>Limpiar cache</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>Limpiando caché...</translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2785,8 +2887,8 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<translation>Error</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Información de componentes</translation>
+ <source>Information</source>
+ <translation>Información</translation>
</message>
</context>
<context>
@@ -2868,10 +2970,6 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<translation>No se puede abrir el archivo &apos;%1&apos; para la escritura: %2</translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot;for reading: %2</source>
- <translation>No se puede abrir el archivo &quot;%1&quot; para la lectura: %2</translation>
- </message>
- <message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>No se puede abrir el archivo &quot;%1&quot; para la lectura: %2</translation>
</message>
@@ -2914,4 +3012,98 @@ O bien acepte la elevación de los derechos de acceso si se le pide.</translatio
<translation>Acerca herramienta de mantención %1</translation>
</message>
</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>No se puede inicializar el caché con la ruta vacía.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>No se puede crear el directorio &quot;%1&quot; para la memoria caché.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>No se puede inicializar el caché: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>No se puede borrar el caché invalidado.</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>No se puede eliminar el archivo de manifiesto: %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>Error al borrar el caché: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>No se pueden recuperar elementos de la memoria caché invalidada.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>No se puede recuperar el elemento de la memoria caché invalidada.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>No se puede registrar el elemento en la memoria caché invalidada.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>No se puede registrar un artículo nulo.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>No se puede registrar un artículo no válido con la suma de verificación %1</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>No se puede registrar el elemento con la suma de verificación %1. Ya existe un elemento con la misma suma de comprobación en la memoria caché.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>Error al copiar el elemento a la ruta &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>No se puede eliminar el elemento de la memoria caché invalidada.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>No se puede eliminar el elemento especificado por la suma de comprobación %1: no existe tal elemento.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>Error al eliminar el directorio &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>Error al invalidar caché: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>No se puede abrir el archivo de manifiesto: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>No se puede escribir el contenido del archivo de manifiesto: %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>No se puede sincronizar el caché invalidado.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>¡Modo de registro desconocido seleccionado!</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>¡Caché borrada con éxito!</translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_fr.ts b/src/sdk/translations/ifw_fr.ts
index 51a5d9695..2f2bd5bf5 100644
--- a/src/sdk/translations/ifw_fr.ts
+++ b/src/sdk/translations/ifw_fr.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>La dépendance manquante &quot;%1&quot; est introuvable pour &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>Résolution de dépendance impossible détectée. Le composant dont l&apos;installation est forcée &quot;%1&quot; serait désinstallé car sa dépendance &quot;%2&quot; est marquée pour désinstallation avec la raison : &quot;%3&quot;.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>Composants sélectionnés par alias &quot;%1&quot; :</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>Récursion détectée, componsant alias &quot;%1&quot; déjà ajouté.</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -306,6 +318,10 @@
<source>Try again</source>
<translation>Réessayez</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>Impossible de télécharger %1. Impossible de créer le répertoire pour &quot;%2&quot;</translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -489,10 +505,6 @@
<translation>Impossible de lire &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Erreur d’analyse dans %1 sur %2, %3 : %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Élément racine %1 inattendu, il doit s’agir de &quot;Updates&quot;.</translation>
</message>
@@ -744,6 +756,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>Contenu invalide dans &quot;%1&quot;.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>Le problème peut être résolu en redémarrant l&apos;application après avoir vidé le cache de :</translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -752,18 +768,6 @@
<translation>Les composants ne peuvent pas comporter d’enfants en mode de mise à jour.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Impossible d’ouvrir le fichier d’interface utilisateur demandé &quot;%1&quot; : %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Impossible de charger le fichier d’interface utilisateur demandé &quot;%1&quot; : %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Impossible d’ouvrir le fichier de licence demandé &quot;%1&quot; : %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Erreur</translation>
</message>
@@ -783,6 +787,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>Une erreur s&amp;apos;est produite lors du chargement du composant sélectionné. Ce composant ne peut pas être installé.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Impossible d’ouvrir le fichier d’interface utilisateur demandé &quot;%1&quot; : %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Impossible de charger le fichier d’interface utilisateur demandé &quot;%1&quot; : %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Impossible d’ouvrir le fichier de licence demandé &quot;%1&quot; : %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -830,62 +858,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>Par déf&amp;aut</translation>
+ <source>Default</source>
+ <translation>Par défaut</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Sélectionner les composants par défaut dans l&apos;arborescence.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;Réinitialiser</translation>
+ <source>Reset</source>
+ <translation>Réinitialiser</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Réinitialiser tous les composants à leur état de sélection d&apos;origine dans l&apos;arborescence.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>&amp;Sélectionner tout</translation>
+ <source>Select All</source>
+ <translation>Sélectionner tout</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Sélectionner tous les composants dans l&apos;arborescence.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Tout désélectionner</translation>
+ <source>Deselect All</source>
+ <translation>Tout désélectionner</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>Désélectionner tous les composants dans l&apos;arborescence.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Parcourir les fichiers QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Sélectionner un fichier Qt Board Support Package pour installer du contenu supplémentaire qui n&apos;est pas directement disponible à partir des référentiels en ligne.</translation>
</message>
@@ -918,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>
@@ -929,6 +933,26 @@
<source>Search</source>
<translation>Recherche</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>Parcourir les fichiers QBSP</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>Sélectionner</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Erreur</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Créer un programme d’installation hors ligne</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>Créer un programme d’installation hors ligne à partir des composants sélectionnés, au lieu d&apos;installer maintenant.</translation>
+ </message>
</context>
<context>
<name>QInstaller::ConsumeOutputOperation</name>
@@ -945,12 +969,8 @@
<translation>Impossible d’enregistrer la sortie de &quot;%1&quot; dans une valeur clé du programme d’installation vide.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>Le fichier &quot;%1&quot; n’existe pas ou n’est pas un objet binaire exécutable.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>L’exécution de &quot;%1&quot; a entraîné un blocage.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation>Echec d&apos;exécution de la commande : &quot;%1&quot;: %2</translation>
</message>
</context>
<context>
@@ -1130,14 +1150,6 @@
<translation>Erreur de téléchargement</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>La vérification du hachage lors du téléchargement a échoué. Cette erreur est temporaire, réessayez.</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>Impossible de vérifier le hachage</translation>
- </message>
- <message>
<source>Cannot download archive %1: %2</source>
<translation>Impossible de télécharger l’archive %1 : %2</translation>
</message>
@@ -1211,6 +1223,28 @@ Erreur lors du chargement de %2</translation>
<source>Total: </source>
<translation>Total: </translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>Nombre de tentatives (%1) dépassé</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>La vérification du hachage lors du téléchargement a échoué. Cette erreur est temporaire, réessayez.
+
+Attendu: %1
+Téléchargé: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>Impossible de vérifier le hachage
+Attendu: %1
+Téléchargé: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1348,7 +1382,7 @@ Erreur lors du chargement de %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Exécution de l’assistant de %1</translation>
</message>
<message>
@@ -1356,7 +1390,7 @@ Erreur lors du chargement de %2</translation>
<translation>Terminé</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Cliquez sur %1 pour quitter l’assistant de %2.</translation>
</message>
<message>
@@ -1368,7 +1402,7 @@ Erreur lors du chargement de %2</translation>
<translation>Exécutez %1 maintenant.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>L’assistant de %1 a échoué.</translation>
</message>
</context>
@@ -1417,11 +1451,11 @@ Erreur lors du chargement de %2</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Installation - %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Bienvenue dans l’assistant d’installation de %1</translation>
</message>
<message>
@@ -1449,10 +1483,6 @@ Erreur lors du chargement de %2</translation>
<translation>Aucune mise à jour disponible.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Seule la gestion des paquetages locaux est disponible.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Quitter</translation>
</message>
@@ -1503,10 +1533,6 @@ Erreur lors du chargement de %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Impossible d’écrire le fichier de licence &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Aucun fichier de licence à supprimer.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1530,10 +1556,6 @@ Erreur lors du chargement de %2</translation>
<translation>Moteur principal du gestionnaire de paquetages manquant.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Préparation du téléchargement des métadonnées...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Décompression des référentiels compressés. Cette opération peut prendre du temps...</translation>
</message>
@@ -1570,14 +1592,6 @@ Erreur lors du chargement de %2</translation>
<translation>Non-concordance des sommes de contrôle détectée pour &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Extraction des métadonnées depuis le référentiel distant... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Extraction des métadonnées depuis le référentiel distant... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>Erreur lors de l’extraction de l’archive &quot;%1&quot; : %2</translation>
</message>
@@ -1589,6 +1603,41 @@ Erreur lors du chargement de %2</translation>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>Archive non prise en charge &quot;%1&quot;: pas de gestionnaire enregistré pour les fichiers avec suffixe &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>Récuperation des dernières informations de mise a jour...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>Mise à jour du cache local avec %n nouvel élément...</numerusform>
+ <numerusform>Mise à jour du cache local avec %n nouveaux éléments</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>Effacer le répertoire de cache et redémarrer l&apos;application peut résoudre ce problème.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>Exception inconnue lors de la mise à jour du cache.</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>Impossible d&apos;ouvrir le fichier extrait &quot;%1&quot; en lecture : %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Impossible d’ouvrir le fichier &quot;%1&quot; en écriture : %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Extraction des informations depuis le référentiels distants...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Extraction des métadonnées depuis le référentiel distant...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1597,10 +1646,8 @@ Erreur lors du chargement de %2</translation>
<translation>Erreur d’écriture de l’outil de maintenance</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Téléchargement des paquetages...</translation>
+ <source>Downloading packages...</source>
+ <translation>Téléchargement des paquetages...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1709,42 +1756,24 @@ Souhaitez-vous continuer ?</translation>
<translation>Impossible de résoudre toutes les dépendances.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Les composants vont être supprimés.</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>Impossible d&apos;installer le composant %1. Le composant est installé uniquement en tant que dépendance automatique à %2.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>Impossible d&apos;installer le composant %1. Le composant est installé uniquement en tant que dépendance automatique à %2.
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>Impossible d&apos;installer le composant %1. Le composant ne peut pas être coché, ce qui signifie que vous devez sélectionner l&apos;un des sous-composants.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>Impossible d&apos;installer le composant %1. Le composant ne peut pas être coché, ce qui signifie que vous devez sélectionner l&apos;un des sous-composants.
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>Composant %1 déjà installé</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>Composant %1 déjà installé
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>Impossible d&apos;installer %1. Le composant est virtuel.</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>Impossible d&apos;installer %1. Le composant est virtuel.
-</translation>
- </message>
- <message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>Impossible d&apos;installer le composant %1. Composant introuvable.
-</translation>
- </message>
- <message>
- <source>Running processes found.</source>
- <translation>Processus en cours trouvés.</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>Impossible d&apos;installer le composant %1. Composant introuvable.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1767,10 +1796,6 @@ Souhaitez-vous continuer ?</translation>
<translation>L’espace disque est insuffisant pour stocker tous les composants sélectionnés ! %1 sont disponibles, alors qu’au moins %2 sont nécessaires.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>L’espace disque est insuffisant pour stocker les fichiers temporaires ! %1 sont disponibles, alors qu’au moins %2 sont nécessaires.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>Le volume que vous avez sélectionné pour l&apos;installation semble avoir suffisamment d&apos;espace pour l&apos;installation, mais il restera moins de 1% de l&apos;espace du volume disponible par la suite.</translation>
</message>
@@ -1791,18 +1816,40 @@ Souhaitez-vous continuer ?</translation>
<translation>Une saisie par l’utilisateur est requise mais le périphérique de sortie n&apos;est pas associé à un terminal.</translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation>Impossible d&apos;enregistrer le composant&#xa0;! Le composant avec l&apos;identifiant %1 existe déjà.</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>Impossible d&apos;installer le composant %1. Le composant est le descendant d&apos;un composant virtuel %2.</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>Impossible d&apos;installer le composant %1. Le composant est le descendant d&apos;un composant virtuel %2.
-</translation>
+ <source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
+ <translation>La taille estimée de l installeur %1 dépasserait la taille limite de l&apos;exécutable prise en charge %2. L&apos;application pourrait ne pas pouvoir s&apos;exécuter.</translation>
</message>
<message>
- <source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
- <translation>La taille estimée de l installeur %1 dépasserait la taille limite de l'exécutable prise en charge %2. L'application pourrait ne pas pouvoir s'exécuter.</translation>
+ <source>Components about to be removed:</source>
+ <translation>Composants sur le point d&apos;être supprimés :</translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>Impossible d&apos;installer le composant %1. Un problème est survenu lors du chargement de ce composant, il est donc marqué comme instable et ne peut pas être sélectionné.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>L’espace disque est insuffisant pour stocker les fichiers temporaires ! %1 sont disponibles, alors qu’au moins %2 sont nécessaires. Vous pouvez sélectionner un autre emplacement pour les fichiers temporaires en modifiant le chemin du cache local à partir des paramètres du programme d&apos;installation.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>Impossible de résoudre les composants à désinstaller.</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>Impossible de sélectionner l&apos;alias %1. Un problème est survenu lors du chargement de cet alaias, il est donc marqué comme instable et ne peut pas être sélectionné</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>Impossible de sélectionner %1. L&apos;alias est marqué virtuel, ce qui signifie qu&apos;il ne peut pas être sélectionné manuellement.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>Le programme d&apos;installation créé utilisera %1 d&apos;espace disque.</translation>
</message>
</context>
<context>
@@ -1920,16 +1967,12 @@ Souhaitez-vous continuer ?</translation>
<translation>Création d’un outil de maintenance</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Installation terminée !</translation>
+ <source>Installation finished!</source>
+ <translation>Installation terminée !</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Installation annulée !</translation>
+ <source>Installation aborted!</source>
+ <translation>Installation annulée !</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1940,16 +1983,12 @@ Installation annulée !</translation>
<translation>Suppression des composants désélectionnés...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Mise à jour terminée !</translation>
+ <source>Update finished!</source>
+ <translation>Mise à jour terminée !</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Mise à jour annulée !</translation>
+ <source>Update aborted!</source>
+ <translation>Mise à jour annulée !</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1992,10 +2031,8 @@ Mise à jour annulée !</translation>
<translation>Génération hors ligne interrompue !</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Installation du composant %1</translation>
+ <source>Installing component %1</source>
+ <translation>Installation du composant %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2042,10 +2079,6 @@ Installation du composant %1</translation>
<translation>Impossible d’extraire les métadonnées : %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Impossible d’ajouter des informations sur la source de mise à jour temporaire.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Informations introuvables sur la source de mise à jour.</translation>
</message>
@@ -2053,6 +2086,50 @@ Installation du composant %1</translation>
<source>Dependency cycle between components &quot;%1&quot; and &quot;%2&quot; detected.</source>
<translation>Cycle de dépendance entre les composants &quot;%1&quot; et &quot;%2&quot; détecté.</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation>Préparation du déballage des composants...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation>%1 opérations sur %2 terminées.</translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation>Déballage des composants...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation>%1 des %2 opérations annulées.</translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation>Annulations terminées.</translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation>%1 des %2 composants installés.</translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation>Tous les composants sont installés.</translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>Chargement des scripts du composant...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>L&apos;alias déclare un nom en conflit avec un composant existant : &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>Alias ​​de composants non résolus</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>Dépendance cyclique entre les alias &quot;%1&quot; et &quot;%2&quot; détectée.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2156,6 +2233,18 @@ Copiez le programme d’installation sur un disque local</translation>
<source>Uninstalling</source>
<translation>Désinstallation</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>&amp;Créer un programme d’installation hors ligne</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>Création d&apos;un programme d’installation hors ligne pour %1</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>Création d&apos;un programme d’installation hors ligne</translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2199,7 +2288,7 @@ Copiez le programme d’installation sur un disque local</translation>
<translation>Prêt pour la désinstallation</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Le programme d’installation est maintenant prêt à supprimer %1 de votre ordinateur.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;Le répertoire du programme %2 va être entièrement supprimé&lt;/font&gt;, y compris tout son contenu !</translation>
</message>
<message>
@@ -2211,7 +2300,7 @@ Copiez le programme d’installation sur un disque local</translation>
<translation>Prêt à mettre à jour les paquetages</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>Le programme d’installation est maintenant prêt à mettre à jour votre installation.</translation>
</message>
<message>
@@ -2223,13 +2312,25 @@ Copiez le programme d’installation sur un disque local</translation>
<translation>Prêt pour l’installation</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Le programme d’installation est maintenant prêt à installer %1 sur votre ordinateur.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Prêt pour la mise à jour</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Créer un programme d’installation hors ligne</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>Prêt pour la création du programme d’installation hors ligne</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>La création du programme d’installation hors ligne pour les composants sélectionnés est maintenant prête.</translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2286,7 +2387,7 @@ Copiez le programme d’installation sur un disque local</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Exécution de l’assistant d’installation de %1</translation>
</message>
</context>
@@ -2509,6 +2610,14 @@ Copiez le programme d’installation sur un disque local</translation>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation>Veuillez veiller à ce que l’utilisateur actuel dispose d’un accès en lecture au fichier &amp;quot;%1&amp;quot; ou essayez d’exécuter %2 en tant qu’administrateur.</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>Valeur non valide pour &apos;max-concurrent-operations&apos;.</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>Valeur vide pour l&apos;option &apos;cache-path&apos;.</translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2557,8 +2666,8 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<translation>Impossible d’ouvrir le fichier de paramètres %1 en lecture : %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Sélectionner les catégories</translation>
+ <source>Categories</source>
+ <translation>Catégories</translation>
</message>
</context>
<context>
@@ -2695,6 +2804,30 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<source>Deselect All</source>
<translation>Tout désélectionner</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>Cache local</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>Les méta-informations des référentiels distants sont mises en cache sur le disque pour améliorer les temps de chargement. Vous pouvez sélectionner un autre répertoire pour stocker le cache ou effacer le contenu du cache actuel.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>Chemin d&apos;accès au cache :</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>Supprime le contenu du répertoire cache</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>Vider le cache</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>Vidage du cache...</translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2756,8 +2889,8 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<translation>Erreur</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informations sur les composants</translation>
+ <source>Information</source>
+ <translation>Informations</translation>
</message>
</context>
<context>
@@ -2766,6 +2899,22 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<source>Extracting &quot;%1&quot;</source>
<translation>Extraction de &quot;%1&quot;</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>Archive non prise en charge &quot;%1&quot;: pas de gestionnaire enregistré pour les fichiers avec suffixe &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Impossible d’ouvrir l’archive &quot;%1&quot; en lecture : %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>Erreur lors de la lecture des contenus de l&apos;archive &quot;%1&quot; : %2</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>Suppression des fichiers extraits de &quot;%1&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2785,20 +2934,178 @@ Ou acceptez l’élévation des droits d’accès si un message vous y invite.</
<translation>Impossible d’ouvrir l’archive &quot;%1&quot; en lecture : %2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
- <translation>Erreur lors de la lecture des contenus de l'archive &quot;%1&quot; : %2</translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Erreur lors de l’extraction de l’archive &quot;%1&quot; : %2</translation>
</message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation>Impossible de préparer le fichier &quot;%1&quot;</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>Impossible d&apos;ouvrir l&apos;archive en lecture : %1</translation>
</message>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation>Extraction pour l'archive &quot;%1&quot; annulée.</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>Impossible de lire l&apos;en-tête de l&apos;entrée : %1</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Erreur lors de l’extraction de l’archive &quot;%1&quot; : %2</translation>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>Impossible d&apos;écrire l&apos;entrée &quot;%1&quot; sur disque : %</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>Impossible d&apos;ouvrir l&apos;archive en lecture : %1</translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation>Impossible de lire l&apos;en-tête de l&apos;entrée : %1</translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>Impossible d&apos;écrire l&apos;entrée &quot;%1&quot; sur le disque : %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Impossible d&apos;ouvrir le fichier &quot;%1&quot; pour l&apos;écriture : %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Impossible d&apos;ouvrir le fichier &quot;%1&quot;en lecture &quot;%2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation>Impossible d&apos;écrire l&apos;en-tête d&apos;entrée pour &quot;%1&quot; : %2</translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation>Composants désélectionnés :</translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation>Composants remplacés par &quot;%1&quot; :</translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation>Suppression de composants virtuels sans dépendances existantes :</translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation>Dépendance des composants &quot;%1&quot; supprimé:</translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation>Autodépendance des composants &quot;%1&quot; supprimé:</translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation>À propos du programme d&apos;installation %1</translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation>À propos de l&apos;outil de maintenance %1</translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>Impossible d&apos;initialiser le cache avec un chemin vide.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>Impossible de créer le répertoire &quot;%1&quot; pour le cache.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>Impossible d&apos;initialiser le cache : %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>Impossible d&apos;effacer le cache invalidé.</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>Impossible de supprimer le fichier manifeste : %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>Erreur lors de la suppression du cache : %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>Impossible de récupérer les éléments du cache invalidé.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>Impossible de récupérer l&apos;élément du cache invalidé.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>Impossible d&apos;enregistrer l&apos;élément dans le cache invalidé.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>Impossible d&apos;enregistrer un élément nul.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>Impossible d&apos;enregistrer un élément non valide avec la somme de contrôle %1</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>Impossible d&apos;enregistrer l&apos;élément avec la somme de contrôle %1. Un élément avec la même somme de contrôle existe déjà dans le cache.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>Erreur lors de la copie de l&apos;élément vers le chemin &quot;%1&quot; : %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>Impossible de supprimer l&apos;élément du cache invalidé.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>Impossible de supprimer l&apos;élément spécifié par la somme de contrôle %1 : aucun élément de ce type n&apos;existe.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>Erreur lors de la suppression du répertoire &quot;%1&quot; : %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>Erreur lors de l&apos;invalidation du cache : %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>Impossible d&apos;ouvrir le fichier manifeste : %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>Impossible d&apos;écrire le contenu du fichier manifeste : %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>Impossible de synchroniser le cache invalidé.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>Le mode de registre sélectionné est inconnu !</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>Cache vidé avec succès !</translation>
</message>
</context>
</TS>
diff --git a/src/sdk/translations/ifw_hr.ts b/src/sdk/translations/ifw_hr.ts
index b694046f0..d219b17f6 100644
--- a/src/sdk/translations/ifw_hr.ts
+++ b/src/sdk/translations/ifw_hr.ts
@@ -202,16 +202,40 @@
</message>
<message>
<source>Select All</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">Odaberi sve</translation>
</message>
<message>
<source>Deselect All</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">dznači sav odabir</translation>
</message>
<message>
<source>The server&apos;s URL that contains a valid repository.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QObject</name>
@@ -271,6 +295,14 @@
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller</name>
@@ -382,6 +414,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>BinaryLayout</name>
@@ -442,18 +478,6 @@
<translation>Komponente nemaju podređenih u modusu aktualiziranja.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Nije moguće otvoriti traženu UI datoteku &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Nije moguće učitati traženu UI datoteku &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Nije moguće otvoriti traženu licencnu datoteku &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Greška</translation>
</message>
@@ -474,8 +498,28 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>There was an error loading the selected component. This component cannot be updated.</source>
- <translation type="unfinished"></translation>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nije moguće otvoriti traženu UI datoteku &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nije moguće učitati traženu UI datoteku &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nije moguće otvoriti traženu licencnu datoteku &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
</context>
<context>
@@ -536,12 +580,8 @@
<translation>Izlaz &quot;%1&quot; nije moguće spremiti u praznu vrijednost ključa programa za instaliranje.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>Datoteka &quot;%1&quot; ne postoji ili nije izvršna binarna datoteka.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>Pokretanje &quot;%1&quot; je prouzročilo urušavanje.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -678,6 +718,14 @@
<source>Cannot remove directory &quot;%1&quot;: %2</source>
<translation>Nije moguće ukloniti mapu &quot;%1&quot;: %2</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create archive &quot;%1&quot;: %2</source>
+ <translation>Nije moguće stvoriti arhiv &quot;%1&quot;: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::CreateShortcutOperation</name>
@@ -742,6 +790,66 @@ Greška prilikom učitavanja %2</translation>
<source>Cannot find component for %1.</source>
<translation>Nije moguće naći komponentu za %1.</translation>
</message>
+ <message>
+ <source>%1 of %2</source>
+ <translation>%1 od %2</translation>
+ </message>
+ <message>
+ <source>%1 downloaded.</source>
+ <translation>Preuzeto: %1.</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s), </source>
+ <translation>
+ <numerusform>%n dan, </numerusform>
+ <numerusform>%n dana, </numerusform>
+ <numerusform>%n dana, </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s), </source>
+ <translation>
+ <numerusform>%n sat, </numerusform>
+ <numerusform>%n sata, </numerusform>
+ <numerusform>%n sati, </numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation>
+ <numerusform>%n minuta</numerusform>
+ <numerusform>%n minute</numerusform>
+ <numerusform>%n minuta</numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <source> - %1%2%3%4 remaining.</source>
+ <translation> – preostaje %1%2%3%4.</translation>
+ </message>
+ <message>
+ <source> - unknown time remaining.</source>
+ <translation> – nepoznato preostalo vrijeme.</translation>
+ </message>
+ <message>
+ <source>Archive: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Total: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -878,20 +986,21 @@ Greška prilikom učitavanja %2</translation>
<source>Extracting &quot;%1&quot;</source>
<translation>Raspakiravanje &quot;%1&quot;</translation>
</message>
-</context>
-<context>
- <name>QInstaller::ExtractArchiveOperation::Runnable</name>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
<message>
<source>Cannot open archive &quot;%1&quot; for reading: %2</source>
<translation>Nije moguće otvoriti arhivu &quot;%1&quot; za učitavanje: %2</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Greška prilikom raspakiravanja arhive &quot;%1&quot;: %2</translation>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Unknown exception caught while extracting &quot;%1&quot;.</source>
- <translation>Nepoznata iznimka prilikom raspakiravanja &quot;%1&quot;.</translation>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -946,6 +1055,18 @@ Greška prilikom učitavanja %2</translation>
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Nije moguće naći nedostajuću ovisnost &quot;%1&quot; za &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::InstallIconsOperation</name>
@@ -973,6 +1094,10 @@ Greška prilikom učitavanja %2</translation>
<source>Cannot create directory &quot;%1&quot;: %2</source>
<translation>Nije moguće stvoriti mapu &quot;%1&quot;: %2</translation>
</message>
+ <message>
+ <source>Cannot prepare to backup file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Lib7z</name>
@@ -1101,10 +1226,6 @@ Greška prilikom učitavanja %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Nije moguće zapisati licencnu datoteku &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Nema licencnih datoteka za brisanje.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1128,10 +1249,6 @@ Greška prilikom učitavanja %2</translation>
<translation>Nedostaje osnovni uređaj upravljača paketa.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Pripremanje preuzimanja meta informacija …</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Raspakiravanje komprimiranih spremišta. Može ponešto potrajati …</translation>
</message>
@@ -1164,29 +1281,57 @@ Greška prilikom učitavanja %2</translation>
<translation>Ustanovljena je neusklađenost kontrolnog zbroja za &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Dohvaćanje meta informacija s udaljenih spremišta … %1/%2 </translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Greška prilikom raspakiravanja arhive &quot;%1&quot;: %2</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Dohvaćanje meta informacija s udaljenih spremišta … </translation>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Nije moguće otvoriti datoteku &quot;%1&quot; za učitavanje: %2</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Greška prilikom raspakiravanja arhive &quot;%1&quot;: %2</translation>
+ <source>Metadata download canceled.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Unknown exception caught while extracting archive &quot;%1&quot;.</source>
- <translation>Nepoznata iznimka prilikom raspakiravanja &quot;%1&quot;.</translation>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot; for reading: %2</source>
- <translation>Nije moguće otvoriti datoteku &quot;%1&quot; za učitavanje: %2</translation>
+ <source>Fetching latest update information...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
</message>
<message>
- <source>Metadata download canceled.</source>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Nije moguće otvoriti datoteku &quot;%1&quot; za zapisivanje: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Dohvaćanje informacija s udaljenih spremišta …</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Dohvaćanje meta informacija s udaljenih spremišta …</translation>
+ </message>
</context>
<context>
<name>QInstaller::FileTaskObserver</name>
@@ -1250,10 +1395,8 @@ Greška prilikom učitavanja %2</translation>
<translation>Greška prilikom pisanja u alat za održavanje</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Preuzimanje paketa …</translation>
+ <source>Downloading packages...</source>
+ <translation>Preuzimanje paketa …</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1358,31 +1501,19 @@ Ne preporučujemo instalirati u ovu mapu, jer instaliranje možda neće uspjeti.
<translation>Nije moguće razriješiti sve ovisnosti.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Komponente se uklanjaju.</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Component %1 already installed
-</source>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
+ <source>Component %1 already installed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
+ <source>Cannot install %1. Component is virtual.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Running processes found.</source>
+ <source>Cannot install %1. Component not found.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1414,10 +1545,6 @@ Ne preporučujemo instalirati u ovu mapu, jer instaliranje možda neće uspjeti.
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation type="unfinished"></translation>
</message>
@@ -1426,13 +1553,11 @@ Ne preporučujemo instalirati u ovu mapu, jer instaliranje možda neće uspjeti.
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1444,11 +1569,35 @@ Ne preporučujemo instalirati u ovu mapu, jer instaliranje možda neće uspjeti.
<translation type="unfinished"></translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
+ <source>Invalid</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Invalid</source>
+ <source>Components about to be removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1567,16 +1716,12 @@ Ne preporučujemo instalirati u ovu mapu, jer instaliranje možda neće uspjeti.
<translation>Stvaranje alata za održavanje</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Instaliranje je dovršeno!</translation>
+ <source>Installation finished!</source>
+ <translation>Instaliranje je dovršeno!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Instaliranje je prekinuto!</translation>
+ <source>Installation aborted!</source>
+ <translation>Instaliranje je prekinuto!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1587,16 +1732,12 @@ Instaliranje je prekinuto!</translation>
<translation>Uklanjanje neodabranih komponenata …</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Aktualiziranje je dovršeno!</translation>
+ <source>Update finished!</source>
+ <translation>Aktualiziranje je dovršeno!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Aktualiziranje je prekinuto!</translation>
+ <source>Update aborted!</source>
+ <translation>Aktualiziranje je prekinuto!</translation>
</message>
<message>
<source>Cannot create target directory for installer.</source>
@@ -1631,10 +1772,8 @@ Aktualiziranje je prekinuto!</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Instaliranje komponente %1</translation>
+ <source>Installing component %1</source>
+ <translation>Instaliranje komponente %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1667,10 +1806,6 @@ Instaliranje komponente %1</translation>
<translation>Nije moguće pronaći meta podatke: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Nije moguće dodati privremene podatke izvora nadogradnje.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Nije moguće naći bilo koje podatke izvora nadogradnje.</translation>
</message>
@@ -1699,6 +1834,50 @@ Instaliranje komponente %1</translation>
%1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -1756,11 +1935,11 @@ Kopiraj program za instaliranje na računalo</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Postavljanje – %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Dobrodošli u čarobnjak postavaka za %1.</translation>
</message>
<message>
@@ -1788,11 +1967,11 @@ Kopiraj program za instaliranje na računalo</translation>
<translation>Nema dostupnih nadogradnja.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Dostupno je samo lokalno upravljanje paketima.</translation>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Quit</source>
+ <source>There is an important update available. Please select &apos;%1&apos; first</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1827,42 +2006,38 @@ Kopiraj program za instaliranje na računalo</translation>
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>St&amp;andardne</translation>
+ <source>Default</source>
+ <translation>Standardne</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Resetiraj</translation>
+ <source>Reset</source>
+ <translation>Resetiraj</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>Odaberi &amp;sve</translation>
+ <source>Select All</source>
+ <translation>Odaberi sve</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>O&amp;dznači sav odabir</translation>
+ <source>Deselect All</source>
+ <translation>dznači sav odabir</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Pretraži QBSP datoteke</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation type="unfinished"></translation>
</message>
@@ -1891,37 +2066,41 @@ 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>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation type="unfinished">Alt+A</translation>
+ <source>Filter the enabled repository categories</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation type="unfinished">Alt+R</translation>
+ <source>Search</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation type="unfinished">Alt+S</translation>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation type="unfinished">Alt+D</translation>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
+ <source>Browse &amp;QBSP files</source>
+ <translation>&amp;Pretraži QBSP datoteke</translation>
+ </message>
+ <message>
+ <source>Select</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Error</source>
+ <translation type="unfinished">Greška</translation>
+ </message>
</context>
<context>
<name>QInstaller::TargetDirectoryPage</name>
@@ -1973,7 +2152,7 @@ Kopiraj program za instaliranje na računalo</translation>
<translation>Spremno za deinstaliranje</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Postavljanje je sad spremno za uklanjanje %1 s računala.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;Mapa programa %2 će se potpuno izbrisati&lt;/font&gt;, uključujući sav sadržaj mape!</translation>
</message>
<message>
@@ -1985,10 +2164,6 @@ Kopiraj program za instaliranje na računalo</translation>
<translation>Spremno za aktualiziranje paketa</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
- <translation>Postavljanje je sad spremno za aktualiziranje tvoje instalacije.</translation>
- </message>
- <message>
<source>&amp;Install</source>
<translation>&amp;Instaliraj</translation>
</message>
@@ -1997,13 +2172,29 @@ Kopiraj program za instaliranje na računalo</translation>
<translation>Spremno za instaliranje</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Postavljanje je sad spremno za instaliranje %1 na tvoje računalo.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>All required information is now available to begin updating your installation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PerformInstallationPage</name>
@@ -2043,11 +2234,23 @@ Kopiraj program za instaliranje na računalo</translation>
<source>Uninstalling</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Dovršavanje čarobnjaka %1</translation>
</message>
<message>
@@ -2055,7 +2258,7 @@ Kopiraj program za instaliranje na računalo</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Klikni %1 za izlaženje iz čarobnjaka %2.</translation>
</message>
<message>
@@ -2067,14 +2270,14 @@ Kopiraj program za instaliranje na računalo</translation>
<translation>Sad pokreni %1.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Neuspješno pokretanje čarobnjaka %1.</translation>
</message>
</context>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Dovršavanje čarobnjaka postavaka %1</translation>
</message>
</context>
@@ -2195,7 +2398,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Nije moguće otvoriti datoteke postavaka %1 za učitavanje: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2391,6 +2594,10 @@ or accept the elevation of access rights if being asked.</source>
<source>Try again</source>
<translation>Pokušaj ponovo</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -2663,10 +2870,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>Nije moguće čitati &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Greška u obradi u %1 na %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Neočekivani Root element %1, mora biti &quot;Updates&quot;.</translation>
</message>
@@ -2709,7 +2912,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Greška</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2720,4 +2923,189 @@ or accept the elevation of access rights if being asked.</source>
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation::Worker</name>
+ <message>
+ <source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Nije moguće otvoriti arhivu &quot;%1&quot; za učitavanje: %2</translation>
+ </message>
+ <message>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Greška prilikom raspakiravanja arhive &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Nije moguće otvoriti datoteku &quot;%1&quot; za zapisivanje: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Nije moguće otvoriti datoteku &quot;%1&quot; za učitavanje: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_hu.ts b/src/sdk/translations/ifw_hu.ts
index b703d99f6..cd247fd8b 100644
--- a/src/sdk/translations/ifw_hu.ts
+++ b/src/sdk/translations/ifw_hu.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Nem található hiányzó &quot;%1&quot; függőség &quot;%2&quot; számára. </translation>
</message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -302,6 +314,10 @@
<source>Try again</source>
<translation>Próbálja újra</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -484,10 +500,6 @@
<translation>&quot;%1&quot; olvasása nem sikerült</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Elemzési hiba %1-ben itt: %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>%1 gyökér elem váratlan, &apos;Updates&apos; kell, hogy legyen.</translation>
</message>
@@ -739,6 +751,10 @@
<source>The specified module could not be found.</source>
<translation>A megadott modul nem található.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -747,18 +763,6 @@
<translation>A komponenseknek nem lehetnek gyermekei frissítés módban. </translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Nem tudja megnyitni a kért &quot;%1&quot; UI fájlt: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Nem tudja betölteni a kért &quot;%1&quot; UI fájlt: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Nem lehet megnyitni a kért &quot;%1&quot; licencfájlt: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Hiba</translation>
</message>
@@ -778,6 +782,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>Hiba történt a kiválasztott komponens betöltésekor. Ez az komponens nem telepíthető.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nem tudja megnyitni a kért &quot;%1&quot; UI fájlt: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nem tudja betölteni a kért &quot;%1&quot; UI fájlt: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nem lehet megnyitni a kért &quot;%1&quot; licencfájlt: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -825,38 +853,23 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>&amp;Alapértelmezett</translation>
+ <source>Default</source>
+ <translation>Alapértelmezett</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Alapértelmezett komponensek kiválasztása a fa nézetben.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;Visszaállítás</translation>
+ <source>Reset</source>
+ <translation>Visszaállítás</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Összes komponens visszaállítása az eredeti kiválasztási állapotba a fa nézetben. </translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
+ <source>Select All</source>
<translation>Mindent kijelöl</translation>
</message>
<message>
@@ -864,12 +877,7 @@
<translation>Minden komponens kijelölése a fa nézetben.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
+ <source>Deselect All</source>
<translation>Összes kijelölés megszüntetése</translation>
</message>
<message>
@@ -877,10 +885,6 @@
<translation>Minden komponens kijelölésének a megszüntetése a fa nézetben.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>QBSP fájlok böngészése</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Qt Board támogatási csomagfájl kiválasztása a további tartalom telepítéséhez, amely közvetlenül nem érhető el az online tárolókból.</translation>
</message>
@@ -913,13 +917,37 @@
<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>
<translation>A kötelező összetevőket először frissíteni kell, mielőtt kiválaszthatja a frissítendő egyéb összetevőket.</translation>
</message>
+ <message>
+ <source>Search</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>QBSP fájlok böngészése</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation type="unfinished">Hiba</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentSelectionPagePrivate</name>
@@ -928,8 +956,8 @@
<translation>Szűrő</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Komponens Információ</translation>
+ <source>Information</source>
+ <translation type="unfinished">Komponens Információ</translation>
</message>
<message>
<source>Error</source>
@@ -951,12 +979,8 @@
<translation>Nem lehet menteni &quot;%1&quot; kimenetét üres telepítőkulcs értékre.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>&quot;%1&quot; fájl nem létezik, vagy nem futtatható.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>&quot;%1&quot; fájl futtatása összeomlást eredményezett.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1097,6 +1121,10 @@
<source>Cannot remove directory &quot;%1&quot;: %2</source>
<translation>Nem sikerült eltávolítani &quot;%1&quot; könyvtárat: %2</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::CreateShortcutOperation</name>
@@ -1209,6 +1237,10 @@ Hiba %2 betöltése közben</translation>
<source>Cannot find component for %1.</source>
<translation>Nem található komponens %1 számára.</translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1283,12 +1315,9 @@ Hiba %2 betöltése közben</translation>
<source>Extracting &quot;%1&quot;</source>
<translation>&quot;%1&quot; kibontása</translation>
</message>
-</context>
-<context>
- <name>QInstaller::ExtractArchiveOperation::Worker</name>
<message>
- <source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
- <translation>Nem sikerült létrehozni kezelő objektumot &quot;%1&quot; archívumhoz: &quot;%2&quot;. </translation>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<source>Cannot open archive &quot;%1&quot; for reading: %2</source>
@@ -1299,12 +1328,19 @@ Hiba %2 betöltése közben</translation>
<translation>Hiba a &quot;%1&quot; archívum olvasása közben: %2 </translation>
</message>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation>Nem lehet előkészülni &quot;%1&quot; fájlra</translation>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation::Worker</name>
+ <message>
+ <source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
+ <translation>Nem sikerült létrehozni kezelő objektumot &quot;%1&quot; archívumhoz: &quot;%2&quot;. </translation>
</message>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation>&quot;%1&quot; archívum kibontása megszakítva. </translation>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Nem sikerült megnyitni &quot;%1&quot; archívumot olvasásra: %2</translation>
</message>
<message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
@@ -1376,7 +1412,7 @@ Hiba %2 betöltése közben</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>%1 varázsló befejezése</translation>
</message>
<message>
@@ -1384,7 +1420,7 @@ Hiba %2 betöltése közben</translation>
<translation>Befejezett</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Kattintson %1 gombra %2 varázslóból való kilépéshez.</translation>
</message>
<message>
@@ -1396,7 +1432,7 @@ Hiba %2 betöltése közben</translation>
<translation>Futtassa most %1-et.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>%1 varázsló elbukott.</translation>
</message>
</context>
@@ -1437,15 +1473,19 @@ Hiba %2 betöltése közben</translation>
<source>Cannot create directory &quot;%1&quot;: %2</source>
<translation>Nem sikerült létrehozni &quot;%1&quot; könyvtárat: %2</translation>
</message>
+ <message>
+ <source>Cannot prepare to backup file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Beállítás - %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Üdvözöljük a %1 telepítő varázslóban.</translation>
</message>
<message>
@@ -1473,10 +1513,6 @@ Hiba %2 betöltése közben</translation>
<translation>Nincs elérhető frissítés.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation>Csak helyi csomagkezelés áll rendelkezésre.</translation>
- </message>
- <message>
<source>There is an important update available. Please select &apos;%1&apos; first</source>
<translation>Egy fontos frissítés áll rendelkezésre. Kérjük, válassza előbb &apos;%1&apos; -t</translation>
</message>
@@ -1527,10 +1563,6 @@ Hiba %2 betöltése közben</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Nem lehet írni &quot;%1&quot; licencfájlt.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Nem található törlendő licencfájl.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1554,10 +1586,6 @@ Hiba %2 betöltése közben</translation>
<translation>Hiányzik a csomagkezelő alapmotorja.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Metainformációk letöltésének előkészítése...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>A tömörített tárolók kicsomagolása. Ez eltarthat egy ideig...</translation>
</message>
@@ -1594,14 +1622,6 @@ Hiba %2 betöltése közben</translation>
<translation>Ellenőrző összeg eltérést észlelt &quot;%1&quot; fájlnál.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Metainformációk lekérése távoli tárolóból... %1/ %2</translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Metainformációk lekérése távoli tárolóból...</translation>
- </message>
- <message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>Nem sikerült megnyitni &quot;%1&quot; fájlt olvasásra: %2</translation>
</message>
@@ -1609,6 +1629,44 @@ Hiba %2 betöltése közben</translation>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>Hiba &quot;%1&quot; archívum kibontása közben: %2</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Nem sikerült megnyitni &quot;%1&quot; fájlt írásra: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Információk lekérése távoli tárolóból...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Metainformációk lekérése távoli tárolóból...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1617,10 +1675,8 @@ Hiba %2 betöltése közben</translation>
<translation>Hiba a karbantartó eszköz írásakor</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Csomagok letöltése...</translation>
+ <source>Downloading packages...</source>
+ <translation>Csomagok letöltése...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1725,10 +1781,6 @@ Kívánja folytatni?</translation>
<translation>Nem található telepített csomag.</translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation>Nem lehet regisztrálni a komponenst! %1 azonosítójú komponens már létezik.</translation>
- </message>
- <message>
<source>Application running in Uninstaller mode.</source>
<translation>Az alkalmazás eltávolítás (uninstall) módban fut.</translation>
</message>
@@ -1741,44 +1793,30 @@ Kívánja folytatni?</translation>
<translation>Nem lehet minden függőséget feloldani.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Eltávolítandó komponensek.</translation>
- </message>
- <message>
- <source>Cannot install %1. Component not found.
-</source>
+ <source>Cannot install %1. Component not found.</source>
<translation>%1 telepítése nem sikerült. A komponens nem található.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
<translation>Nem sikerült telepíteni %1 komponenst. A komponens csak %2 automatikus függőségeként van telepítve. </translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation>Nem sikerült telepíteni %1 komponenst. A komponens nem válaszható ki, vagyis kell választania az egyik alkomponenst.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
+ <source>Component %1 already installed</source>
<translation>%1 komponens már telepítve van.</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation>%1 telepítése nem sikerült. A komponens %2 virtuális komponens leszármazottja. </translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
+ <source>Cannot install %1. Component is virtual.</source>
<translation>%1 telepítése nem sikerült. A komponens virtuális.</translation>
</message>
<message>
- <source>Running processes found.</source>
- <translation>Futó folyamatok találhatók.</translation>
- </message>
- <message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
<translation>A parancssorból történő futtatás során nem növelhetők a hozzáférési jogok. Indítsa újra az alkalmazást rendszergazdaként.</translation>
</message>
@@ -1795,10 +1833,6 @@ Kívánja folytatni?</translation>
<translation>Nincs elég lemezterület az összes kiválasztott komponens tárolásához! %1 elérhető, miközben minimum %2 szükséges.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>Nincs elég lemezterület az ideiglenes fájlok tárolására! %1 elérhető, míközben minimum %2 szükséges.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>Úgy tűnik, hogy a telepítéshez kiválasztott kötet elegendő helyet biztosít a telepítéshez, de a kötet helyének kevesebb mint 1%-a áll majd rendelkezésre a telepítés befejeztével.</translation>
</message>
@@ -1818,6 +1852,34 @@ Kívánja folytatni?</translation>
<source>Invalid</source>
<translation>Érvénytelen</translation>
</message>
+ <message>
+ <source>Components about to be removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1932,13 +1994,11 @@ Kívánja folytatni?</translation>
<translation>Karbantartó eszköz létrehozása</translation>
</message>
<message>
- <source>
-Installation finished!</source>
+ <source>Installation finished!</source>
<translation>Telepítés befejeződött!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
+ <source>Installation aborted!</source>
<translation>Telepítés megszakítva!</translation>
</message>
<message>
@@ -1950,13 +2010,11 @@ Installation aborted!</source>
<translation>Kijelölt összetevők eltávolítása...</translation>
</message>
<message>
- <source>
-Update finished!</source>
+ <source>Update finished!</source>
<translation>Frissítés befejeződött!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
+ <source>Update aborted!</source>
<translation>Frissítés megszakítva!</translation>
</message>
<message>
@@ -2000,8 +2058,7 @@ Update aborted!</source>
<translation>Offline generáció megszakítva!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
+ <source>Installing component %1</source>
<translation>%1 összetevő telepítése</translation>
</message>
<message>
@@ -2049,10 +2106,6 @@ Installing component %1</source>
<translation>Nem sikerült lekérni metaadatokat: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Nem adhatók hozzá ideiglenes frissítési forrásadatok.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Nem található frissítési forrás információ.</translation>
</message>
@@ -2060,6 +2113,50 @@ Installing component %1</source>
<source>Dependency cycle between components &quot;%1&quot; and &quot;%2&quot; detected.</source>
<translation>Függőségi ciklus észlelve &quot;%1&quot; és &quot;%2&quot; összetevők között.</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2163,6 +2260,18 @@ Másolja a telepítőt egy helyi meghajtóra</translation>
<source>Uninstalling</source>
<translation>Eltávolítás</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2213,7 +2322,7 @@ Másolja a telepítőt egy helyi meghajtóra</translation>
<translation>Eltávolításra kész</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>A telepítő készen áll %1 eltávolítására a számítógépről.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;%2 programkönyvtár teljesen törlődik&lt;/font&gt;, beleértve a könyvtár teljes tartalmát!</translation>
</message>
<message>
@@ -2225,7 +2334,7 @@ Másolja a telepítőt egy helyi meghajtóra</translation>
<translation>Készen áll a csomagok frissítésére</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>A telepítő készen áll a telepítés frissítésére.</translation>
</message>
<message>
@@ -2237,13 +2346,25 @@ Másolja a telepítőt egy helyi meghajtóra</translation>
<translation>Telepítésre készen</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Telepítő készen áll %1 számítógépre történő telepítésére.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Frissítésre kész</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2300,7 +2421,7 @@ Másolja a telepítőt egy helyi meghajtóra</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>%1 telepítő varázsló befejezése</translation>
</message>
</context>
@@ -2523,6 +2644,14 @@ Másolja a telepítőt egy helyi meghajtóra</translation>
<source>Incorrect arguments for %1</source>
<translation>Hibás argumentumok %1-hez</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2569,8 +2698,8 @@ a megfelelő jogokkal rendelkező felhasználóként, majd kattintson az OK gomb
<translation>Nem sikerült megnyitni &quot;%1&quot; beállítás fájlt olvasásra: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Válasszon Kategóriákat</translation>
+ <source>Categories</source>
+ <translation type="unfinished">Válasszon Kategóriákat</translation>
</message>
</context>
<context>
@@ -2707,6 +2836,30 @@ a megfelelő jogokkal rendelkező felhasználóként, majd kattintson az OK gomb
<source>User defined repositories</source>
<translation>Felhasználó által megadott tárolók</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2755,4 +2908,174 @@ a megfelelő jogokkal rendelkező felhasználóként, majd kattintson az OK gomb
<translation>&quot;%1&quot; fájl átnevezése &quot;%2&quot; fájlra nem sikerült: %3</translation>
</message>
</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Nem sikerült megnyitni &quot;%1&quot; fájlt írásra: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Nem sikerült megnyitni &quot;%1&quot; fájlt olvasásra: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_it.ts b/src/sdk/translations/ifw_it.ts
index 89c608933..5f08dc1a7 100644
--- a/src/sdk/translations/ifw_it.ts
+++ b/src/sdk/translations/ifw_it.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Impossibile trovare la dipendenza mancante &quot;%1&quot; per &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -306,6 +318,10 @@
<source>Try again</source>
<translation>Riprova</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -489,10 +505,6 @@
<translation>Impossibile leggere &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Errore di analisi in %1 a %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Elemento radice %1 imprevisto, dovrebbe essere &quot;Updates&quot;.</translation>
</message>
@@ -744,6 +756,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -752,18 +768,6 @@
<translation>I componenti non possono avere figli in modalità updater.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Impossibile aprire il file di interfaccia utente richiesto &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Impossibile caricare il file di interfaccia utente richiesto &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Impossibile aprire il file di licenza richiesto &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Errore</translation>
</message>
@@ -784,8 +788,28 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>There was an error loading the selected component. This component cannot be updated.</source>
- <translation type="unfinished"></translation>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Impossibile aprire il file di interfaccia utente richiesto &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Impossibile caricare il file di interfaccia utente richiesto &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Impossibile aprire il file di licenza richiesto &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
</context>
<context>
@@ -834,42 +858,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;Predefinito</translation>
+ <source>Default</source>
+ <translation>Predefinito</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Reimposta</translation>
+ <source>Reset</source>
+ <translation>Reimposta</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Seleziona tutto</translation>
+ <source>Select All</source>
+ <translation>Seleziona tutto</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Deseleziona tutto</translation>
+ <source>Deselect All</source>
+ <translation>Deseleziona tutto</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Sfoglia file QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation type="unfinished"></translation>
</message>
@@ -898,37 +918,41 @@
<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>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation type="unfinished">Alt+A</translation>
+ <source>Filter the enabled repository categories</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation type="unfinished">Alt+F</translation>
+ <source>Search</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation type="unfinished">Alt+S</translation>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation type="unfinished">Alt+D</translation>
+ <source>Browse &amp;QBSP files</source>
+ <translation>&amp;Sfoglia file QBSP</translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
+ <source>Select</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Error</source>
+ <translation type="unfinished">Errore</translation>
+ </message>
</context>
<context>
<name>QInstaller::ConsumeOutputOperation</name>
@@ -945,12 +969,8 @@
<translation>Impossibile salvare l&apos;output di &quot;%1” in un valore chiave del programma di installazione vuoto.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>Il file &quot;%1&quot; non esiste o non è un file binario eseguibile.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>L&apos;esecuzione di &quot;%1&quot; ha causato un arresto anomalo.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1087,6 +1107,14 @@
<source>Cannot remove directory &quot;%1&quot;: %2</source>
<translation>Impossibile rimuovere la directory &quot;%1&quot;: %2</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create archive &quot;%1&quot;: %2</source>
+ <translation>Impossibile creare l&apos;archivio &quot;%1&quot;: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::CreateShortcutOperation</name>
@@ -1151,6 +1179,62 @@ Errore durante il caricamento di %2</translation>
<source>Cannot find component for %1.</source>
<translation>Impossibile trovare il componente per %1.</translation>
</message>
+ <message>
+ <source>%1 of %2</source>
+ <translation>%1 di %2</translation>
+ </message>
+ <message>
+ <source>%1 downloaded.</source>
+ <translation>%1 scaricato.</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s), </source>
+ <translation>
+ <numerusform>%n giorno/i, </numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s), </source>
+ <translation>
+ <numerusform>%n ora/e, </numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation>
+ <numerusform>%n minuto/i</numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation>
+ <numerusform>%n secondo/i</numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <source> - %1%2%3%4 remaining.</source>
+ <translation> - %1%2%3%4 rimanente.</translation>
+ </message>
+ <message>
+ <source> - unknown time remaining.</source>
+ <translation> - tempo rimanente sconosciuto.</translation>
+ </message>
+ <message>
+ <source>Archive: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Total: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1220,21 +1304,6 @@ Errore durante il caricamento di %2</translation>
</message>
</context>
<context>
- <name>QInstaller::ExtractArchiveOperation::Runnable</name>
- <message>
- <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
- <translation>Impossibile aprire l’archivio &quot;%1&quot; per la lettura: %2</translation>
- </message>
- <message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Errore durante l&apos;estrazione dell&apos;archivio &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Unknown exception caught while extracting &quot;%1&quot;.</source>
- <translation>Rilevata eccezione sconosciuta durante l&apos;estrazione di &quot;%1&quot;.</translation>
- </message>
-</context>
-<context>
<name>QInstaller::FakeStopProcessForUpdateOperation</name>
<message>
<source>Cannot get package manager core.</source>
@@ -1303,7 +1372,7 @@ Errore durante il caricamento di %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Completamento della procedura guidata %1</translation>
</message>
<message>
@@ -1311,7 +1380,7 @@ Errore durante il caricamento di %2</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Fare clic su %1 per uscire dalla procedura guidata %2.</translation>
</message>
<message>
@@ -1323,7 +1392,7 @@ Errore durante il caricamento di %2</translation>
<translation>Eseguire %1 ora.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Procedura guidata %1 non riuscita.</translation>
</message>
</context>
@@ -1364,15 +1433,19 @@ Errore durante il caricamento di %2</translation>
<source>Cannot create directory &quot;%1&quot;: %2</source>
<translation>Impossibile creare la directory &quot;%1&quot;: %2</translation>
</message>
+ <message>
+ <source>Cannot prepare to backup file &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Installazione - %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Installazione guidata di %1.</translation>
</message>
<message>
@@ -1400,13 +1473,13 @@ Errore durante il caricamento di %2</translation>
<translation>Nessun aggiornamento disponibile.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Disponibile solo gestione pacchetto locale.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Esci</translation>
</message>
+ <message>
+ <source>There is an important update available. Please select &apos;%1&apos; first</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::LicenseAgreementPage</name>
@@ -1450,10 +1523,6 @@ Errore durante il caricamento di %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Impossibile scrivere il file di licenza &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Nessun file di licenza da eliminare trovato.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1477,10 +1546,6 @@ Errore durante il caricamento di %2</translation>
<translation>Motore core di gestione pacchetti mancante.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Preparazione del download delle informazioni sui metadati...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Decompressione repository compressi. Potrebbe volerci qualche momento...</translation>
</message>
@@ -1513,29 +1578,56 @@ Errore durante il caricamento di %2</translation>
<translation>Rilevata mancata corrispondenza checksum per &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Recupero delle informazioni sui metadati dal repository remoto in corso... %1/%2 </translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Errore durante l&apos;estrazione dell&apos;archivio &quot;%1&quot;: %2</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Recupero delle informazioni sui metadati dal repository remoto in corso... </translation>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Impossibile aprire il file &quot;%1&quot; per la lettura: %2</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Errore durante l&apos;estrazione dell&apos;archivio &quot;%1&quot;: %2</translation>
+ <source>Metadata download canceled.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Unknown exception caught while extracting archive &quot;%1&quot;.</source>
- <translation>Rilevata eccezione sconosciuta durante l&apos;estrazione dell&apos;archivio &quot;%1&quot;.</translation>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot; for reading: %2</source>
- <translation>Impossibile aprire il file &quot;%1&quot; per la lettura: %2</translation>
+ <source>Fetching latest update information...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
</message>
<message>
- <source>Metadata download canceled.</source>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Impossibile aprire il file &quot;%1&quot; per la scrittura: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Recupero di informazioni da repository remoti in corso...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Recupero delle informazioni sui metadati dal repository remoto in corso...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1544,10 +1636,8 @@ Errore durante il caricamento di %2</translation>
<translation>Errore durante la scrittura dello strumento di manutenzione</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Download pacchetti in corso...</translation>
+ <source>Downloading packages...</source>
+ <translation>Download pacchetti in corso...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1652,31 +1742,19 @@ Continuare?</translation>
<translation>Impossibile risolvere tutte le dipendenze.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Componenti che si sta per rimuovere.</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Component %1 already installed
-</source>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
+ <source>Component %1 already installed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
+ <source>Cannot install %1. Component is virtual.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Running processes found.</source>
+ <source>Cannot install %1. Component not found.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1708,10 +1786,6 @@ Continuare?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation type="unfinished"></translation>
</message>
@@ -1720,13 +1794,11 @@ Continuare?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1738,11 +1810,35 @@ Continuare?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
+ <source>Invalid</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Invalid</source>
+ <source>Components about to be removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1861,16 +1957,12 @@ Continuare?</translation>
<translation>Creazione strumento di manutenzione</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Installazione completata.</translation>
+ <source>Installation finished!</source>
+ <translation>Installazione completata.</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Installazione annullata.</translation>
+ <source>Installation aborted!</source>
+ <translation>Installazione annullata.</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1881,16 +1973,12 @@ Installazione annullata.</translation>
<translation>Rimozione dei componenti deselezionati in corso...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Aggiornamento completato.</translation>
+ <source>Update finished!</source>
+ <translation>Aggiornamento completato.</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Aggiornamento annullato.</translation>
+ <source>Update aborted!</source>
+ <translation>Aggiornamento annullato.</translation>
</message>
<message>
<source>Cannot create target directory for installer.</source>
@@ -1925,10 +2013,8 @@ Aggiornamento annullato.</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Installazione componente %1</translation>
+ <source>Installing component %1</source>
+ <translation>Installazione componente %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1961,10 +2047,6 @@ Installazione componente %1</translation>
<translation>Impossibile recuperare le meta informazioni: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Impossibile aggiungere informazioni sull&apos;origine di aggiornamento temporanea.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Impossibile trovare informazioni sull&apos;origine di aggiornamento.</translation>
</message>
@@ -1993,6 +2075,50 @@ Installazione componente %1</translation>
%1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2096,6 +2222,18 @@ Copiare il programma di installazione in un&apos;unità locale</translation>
<source>Uninstalling</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2139,7 +2277,7 @@ Copiare il programma di installazione in un&apos;unità locale</translation>
<translation>Pronto alla disinstallazione</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Si è ora pronti per iniziare la rimozione di %1 dal computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;La directory del programma %2 verrà eliminata completamente&lt;/font&gt;, incluso tutto il contenuto di tale directory!</translation>
</message>
<message>
@@ -2151,7 +2289,7 @@ Copiare il programma di installazione in un&apos;unità locale</translation>
<translation>Pronto all&apos;aggiornamento dei pacchetti</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>Si è ora pronti per iniziare l&apos;installazione.</translation>
</message>
<message>
@@ -2163,13 +2301,25 @@ Copiare il programma di installazione in un&apos;unità locale</translation>
<translation>Pronto all&apos;installazione</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Si è ora pronti per iniziare l&apos;installazione di %1 nel computer.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2226,7 +2376,7 @@ Copiare il programma di installazione in un&apos;unità locale</translation>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Completamento dell&apos;installazione guidata di %1</translation>
</message>
</context>
@@ -2449,6 +2599,14 @@ Copiare il programma di installazione in un&apos;unità locale</translation>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2492,7 +2650,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Impossibile aprire il file di impostazioni %1 per la lettura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2620,16 +2778,40 @@ or accept the elevation of access rights if being asked.</source>
</message>
<message>
<source>Select All</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">Seleziona tutto</translation>
</message>
<message>
<source>Deselect All</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">Deseleziona tutto</translation>
</message>
<message>
<source>The server&apos;s URL that contains a valid repository.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2691,7 +2873,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>Errore</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2701,6 +2883,22 @@ or accept the elevation of access rights if being asked.</source>
<source>Extracting &quot;%1&quot;</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished">Impossibile aprire l’archivio &quot;%1&quot; per la lettura: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2709,4 +2907,189 @@ or accept the elevation of access rights if being asked.</source>
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>QInstaller::ExtractArchiveOperation::Worker</name>
+ <message>
+ <source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Impossibile aprire l’archivio &quot;%1&quot; per la lettura: %2</translation>
+ </message>
+ <message>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Errore durante l&apos;estrazione dell&apos;archivio &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Impossibile aprire il file &quot;%1&quot; per la scrittura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Impossibile aprire il file &quot;%1&quot; per la lettura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_ja.ts b/src/sdk/translations/ifw_ja.ts
index 3ee346f70..5152944d3 100644
--- a/src/sdk/translations/ifw_ja.ts
+++ b/src/sdk/translations/ifw_ja.ts
@@ -16,11 +16,11 @@
<name>BinaryContent</name>
<message>
<source>Cannot seek to %1 to read the operation data.</source>
- <translation>操作データを読み取るための %1 のシークができません。</translation>
+ <translation>操作データを読み取るための %1 とうファイルポジションに移動できません。</translation>
</message>
<message>
<source>Cannot seek to %1 to read the resource collection block.</source>
- <translation>リソース コレクション ブロックを読み取るための %1 のシークができません。</translation>
+ <translation>リソース コレクション ブロックを読み取るため%1 とうファイルポジションに移動できません。</translation>
</message>
<message>
<source>Cannot open meta resource %1.</source>
@@ -31,11 +31,11 @@
<name>BinaryLayout</name>
<message>
<source>Cannot seek to %1 to read the embedded meta data count.</source>
- <translation>埋め込みメタ データ数を読み取るための %1 のシークができません。</translation>
+ <translation>埋め込みメタ データ数を読み取るため%1 とうファイルポジションに移動できません。</translation>
</message>
<message>
<source>Cannot seek to %1 to read the resource collection segment.</source>
- <translation>リソース コレクション セグメントを読み取るための %1 のシークができません。</translation>
+ <translation>リソース コレクション セグメントを読み取るため %1 とうファイルポジションに移動できません。</translation>
</message>
<message>
<source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source>
@@ -136,12 +136,24 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>&quot;%2&quot; の欠落した依存関係 &quot;%1&quot; が見つかりません。</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>不可能な依存関係が検出されました。強制にインストールされる&quot;%1&quot;コンポーネントは、&quot;%1&quot;に依存する &quot;%2&quot;がアンインストールの対象ですので、アンインストールされます。&quot;%2&quot;のアンインストレーション理由は: &quot;%3&quot;。</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>エイリアス &quot;%1&quot; によって選択されたコンポーネント:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>再帰が検出されました。コンポーネント エイリアス &quot;%1&quot; がすでに追加されています。</translation>
+ </message>
</context>
<context>
<name>Job</name>
<message>
<source>Canceled</source>
- <translation>キャンセル</translation>
+ <translation>キャンセルされました</translation>
</message>
</context>
<context>
@@ -209,7 +221,7 @@
<name>KDUpdater::FileDownloader</name>
<message>
<source>Download finished.</source>
- <translation>ダウンロードが終了しました。</translation>
+ <translation>ダウンロードが完了しました。</translation>
</message>
<message>
<source>Cryptographic hashes do not match.</source>
@@ -284,7 +296,7 @@
</message>
<message>
<source>Secure Connection Failed</source>
- <translation>セキュリティで保護された接続に失敗しました</translation>
+ <translation>セキュアな接続が失敗しました</translation>
</message>
<message>
<source>There was an error during connection to: %1.</source>
@@ -292,7 +304,7 @@
</message>
<message>
<source>This could be a problem with the server&apos;s configuration, or it could be someone trying to impersonate the server.</source>
- <translation>サーバーの構成に問題があるか、誰かがサーバーを偽装しようとした可能性があります。</translation>
+ <translation>サーバーの構成に問題があるか、誰かがサーバーを偽装しようとしている可能性があります。</translation>
</message>
<message>
<source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source>
@@ -302,6 +314,10 @@
<source>Try again</source>
<translation>再試行</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>%1をダウンロードできません。&quot;%2&quot;のためディレクトリを作成することができません。</translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -421,11 +437,11 @@
</message>
<message>
<source>%1 cannot be paused</source>
- <translation>%1 を解析できません</translation>
+ <translation>%1 を一時停止できません</translation>
</message>
<message>
<source>Cannot pause task %1</source>
- <translation>タスク %1 を解析できません</translation>
+ <translation>タスク&#x3000;%1 を一時停止できません</translation>
</message>
<message>
<source>Cannot resume task %1</source>
@@ -484,10 +500,6 @@
<translation>&quot;%1&quot; を読み取れません</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>%2、%3 で %1 のエラーを解析します: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>ルート エレメント %1 は予期しないものです。&quot;Updates&quot; でなければなりません。</translation>
</message>
@@ -739,6 +751,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>&quot;%1&quot; に無効なコンテンツがあります。</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>以下のキャッシュを削除した上でアプリケーションを再起動することで問題が解決することもあります。</translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -747,18 +763,6 @@
<translation>コンポーネントは、アップデーター モードで子を持つことができません。</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>要求された UI ファイル &quot;%1&quot; を開けません: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>要求された UI ファイル &quot;%1&quot; を読み込めません: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>要求されたライセンス ファイル &quot;%1&quot; を開けません: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>エラー</translation>
</message>
@@ -778,6 +782,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>選択したコンポーネントの読み込み中にエラーが発生しました。このコンポーネントをインストールできません。</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>要求された UI ファイル &quot;%1&quot; を開けません: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>要求された UI ファイル &quot;%1&quot; を読み込めません: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>要求されたライセンス ファイル &quot;%1&quot; を開けません: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -825,62 +853,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt + A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>デフォルト (&amp;A)</translation>
+ <source>Default</source>
+ <translation>デフォルト</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>ツリー表示でデフォルトのコンポーネントを選択します。</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt + R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>リセット(&amp;R)</translation>
+ <source>Reset</source>
+ <translation>リセット</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>ツリー表示ですべてのコンポーネントを元の選択状態にリセットします。</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt + S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>すべて選択(&amp;S)</translation>
+ <source>Select All</source>
+ <translation>すべて選択</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>ツリー表示ですべてのコンポーネントを選択します。</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt + D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>すべて選択解除 (&amp;D)</translation>
+ <source>Deselect All</source>
+ <translation>すべて選択解除</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>ツリー表示ですべてのコンポーネントの選択を解除します。</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>QBSP ファイルの参照 (&amp;B)</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Qt Board Support Packageファイルを選択し、オンラインリポジトリから直接取得できない追加のコンテンツをインストールします。</translation>
</message>
@@ -913,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>
@@ -924,6 +928,26 @@
<source>Search</source>
<translation>検索</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>QBSPファイルを検索</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>選択</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>エラー</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>オフラインインストーラーの作成</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>今すぐインストールするのではなく、選択したコンポーネントでオフライン インストーラーを作成します。</translation>
+ </message>
</context>
<context>
<name>QInstaller::ConsumeOutputOperation</name>
@@ -940,12 +964,8 @@
<translation>&quot;%1&quot; の出力を空のインストーラー キー値に保存できません。</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>ファイル &quot;%1&quot; は存在しないか、実行可能なバイナリ ファイルではありません。</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>&quot;%1&quot; を実行した結果、クラッシュが発生しました。</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation>コマンド&quot;%1&quot;の実行に失敗しました。: %2</translation>
</message>
</context>
<context>
@@ -1196,11 +1216,15 @@ Error while loading %2</source>
</message>
<message>
<source>Archive: </source>
- <translation type="unfinished"></translation>
+ <translation>アーカイブ</translation>
</message>
<message>
<source>Total: </source>
- <translation type="unfinished"></translation>
+ <translation>合計</translation>
+ </message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>再試行回数 (%1) を超えました</translation>
</message>
</context>
<context>
@@ -1335,7 +1359,7 @@ Error while loading %2</source>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>%1 ウィザードを完了しています</translation>
</message>
<message>
@@ -1343,7 +1367,7 @@ Error while loading %2</source>
<translation>終了</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>%2 ウィザードを終了するには、%1 をクリックします。</translation>
</message>
<message>
@@ -1355,7 +1379,7 @@ Error while loading %2</source>
<translation>今すぐ %1 を実行します。</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>%1 ウィザードが正常に実行されませんでした。</translation>
</message>
</context>
@@ -1404,11 +1428,11 @@ Error while loading %2</source>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>設定 - %1</translation>
+ <source>Welcome</source>
+ <translation>ようこそ</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>%1 設定ウィザードへようこそ。</translation>
</message>
<message>
@@ -1436,16 +1460,12 @@ Error while loading %2</source>
<translation>利用できる更新はありません。</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation>ローカル パッケージ管理のみ利用可能です。</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>中止</translation>
</message>
<message>
<source>There is an important update available. Please select &apos;%1&apos; first</source>
- <translation type="unfinished"></translation>
+ <translation>重要な更新が見つかりました。まず&apos;%1&apos; を選択してください</translation>
</message>
</context>
<context>
@@ -1490,10 +1510,6 @@ Error while loading %2</source>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>ライセンス ファイル &quot;%1&quot; に書き込めません</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>削除対象のライセンス ファイルが見つかりません。</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1517,10 +1533,6 @@ Error while loading %2</source>
<translation>パッケージ マネージャー コア エンジンが見つかりません。</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>メタ情報のダウンロードを準備しています...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>圧縮されたリポジトリを解凍しています。 しばらくお待ちください...</translation>
</message>
@@ -1557,14 +1569,6 @@ Error while loading %2</source>
<translation>&quot;%1&quot; でチェックサムの不一致が検出されました。</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>リモート リポジトリからメタ情報を取得しています... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>リモート リポジトリからメタ情報を取得しています... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>アーカイブ &quot;%1&quot; の抽出中にエラーが発生しました: %2</translation>
</message>
@@ -1576,6 +1580,40 @@ Error while loading %2</source>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>&quot;%1&quot; は非サポートのアーカイブです。拡張子 &quot;%2&quot; ファイルのハンドラが登録されていません</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>最新の更新情報を取得しています...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>%n 個の新しいアイテムでローカル キャッシュを更新しています...</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>キャッシュディレクトリを空にしてアプリケーションを再起動すると、この問題が解決する場合があります。</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>キャッシュの更新中に不明な例外が発生しました。</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>読み取り用の、解凍されたファイル &quot;%1&quot; を開けません:%2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>書き込み用の、ファイル &quot;%1&quot; を開けません:%2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>リモート リポジトリから情報を取得しています...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>リモート リポジトリからメタ情報を取得しています... </translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1584,10 +1622,8 @@ Error while loading %2</source>
<translation>保守ツールへの書き込み中にエラーが発生しました</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-パッケージをダウンロードしています...</translation>
+ <source>Downloading packages...</source>
+ <translation>パッケージをダウンロードしています...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1696,42 +1732,24 @@ Do you want to continue?</source>
<translation>一部の依存関係を解決できません。</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>コンポーネントを削除しようとしています。</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>コンポーネント %1 をインストールできません。コンポーネントは %2 への自動依存性としてのみインストールされます。
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>コンポーネント %1 をインストールできません。コンポーネントは %2 への自動依存性としてのみインストールされます。</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>コンポーネント %1 をインストールできません。コンポーネントはチェックできないため、サブコンポーネントの一つを選択する必要があります。
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>コンポーネント %1 をインストールできません。コンポーネントはチェックできないため、サブコンポーネントの一つを選択する必要があります。</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>コンポーネント %1 はすでにインストールされています
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>コンポーネント %1 はすでにインストールされています</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>%1 をインストールできません。コンポーネントは仮想です。
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>%1 をインストールできません。コンポーネントは仮想です。</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>%1 をインストールできません。コンポーネントが見つかりません。
-</translation>
- </message>
- <message>
- <source>Running processes found.</source>
- <translation>実行中のプロセスが見つかりました。</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>%1 をインストールできません。コンポーネントが見つかりません。</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1754,10 +1772,6 @@ Do you want to continue?</source>
<translation>十分なディスク空き容量がないため、選択された一部のコンポーネントを格納できません。 %2 が最低限必要な場合は、%1 を利用できます。</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>十分なディスク空き容量がないため、一時ファイルを格納できません。 %2 が最低限必要な場合は、%1 を利用できます。</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>インストール用に選択されたボリュームにはインストールに十分な空き容量があるようですが、後から使用可能な空き容量はボリュームの 1% 未満です。</translation>
</message>
@@ -1778,18 +1792,40 @@ Do you want to continue?</source>
<translation>ユーザー入力が必要ですが、出力デバイスが端末に関連づけられていません。</translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation>コンポーネントを登録できません!識別子 %1 のコンポーネントはすでに存在します。</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>%1 をインストールできません。コンポーネントは仮想コンポーネント %2 を継承しています。</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>%1 をインストールできません。コンポーネントは仮想コンポーネント %2 を継承しています。
-</translation>
+ <source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
+ <translation>インストーラの推定サイズ %1 は、実行可能ファイルのサイズ制限 %2 を超えています。アプリケーションが実行できない場合があります。</translation>
</message>
<message>
- <source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
- <translation type="unfinished"></translation>
+ <source>Components about to be removed:</source>
+ <translation>削除されるコンポーネント: </translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>コンポーネント %1をインストールできません。このコンポーネントのロードに問題があったため、不安定と記され、選択できません。</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>時ファイルを保存するのに十分なディスク容量がありません!&#x3000;%1 が利用可能ですが、必要な最小値は %2 です。 インストーラー設定からローカル キャッシュ パスを変更することにより、一時ファイルの別の場所を選択できます。</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>アンインストール対象のコンポーネントを解決できません。</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>エイリアス %1 を選択できません。エイリアスのロード中に問題が発生したため、不安定と記され、選択できません。</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>%1を選択できません。エイリアスは仮想として記され、手動で選択できません。</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>作成されるインストーラーはディスク容量の %1 を使用します。</translation>
</message>
</context>
<context>
@@ -1907,16 +1943,12 @@ Do you want to continue?</source>
<translation>保守ツールを作成しています</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-インストールが終了しました。</translation>
+ <source>Installation finished!</source>
+ <translation>インストールが終了しました。</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-インストールが中止されました。</translation>
+ <source>Installation aborted!</source>
+ <translation>インストールが中止されました。</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1927,16 +1959,12 @@ Installation aborted!</source>
<translation>選択解除したコンポーネントを削除しています...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-更新が終了しました。</translation>
+ <source>Update finished!</source>
+ <translation>更新が終了しました。</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-更新が中止されました。</translation>
+ <source>Update aborted!</source>
+ <translation>更新が中止されました。</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1979,10 +2007,8 @@ Update aborted!</source>
<translation>オフライン生成が中止されました!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-コンポーネント %1 をインストールしています</translation>
+ <source>Installing component %1</source>
+ <translation>コンポーネント %1 をインストールしています</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2029,10 +2055,6 @@ Installing component %1</source>
<translation>メタ情報を取得できません: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>一時的な更新ソース情報を追加できません。</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>更新ソース情報が見つかりません。</translation>
</message>
@@ -2040,6 +2062,50 @@ Installing component %1</source>
<source>Dependency cycle between components &quot;%1&quot; and &quot;%2&quot; detected.</source>
<translation>コンポーネント &quot;%1&quot; と &quot;%2&quot; との依存サイクルが検出されました。</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation>コンポーネントを開梱する準備をしています。</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation>%2 中 %1 の操作が完了しました。</translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation>コンポーネントを開梱しています。</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation>%2 中 %1 の操作がロールバックされました。</translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation>ロールバックが完了しました。</translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation>%2 中 %1のコンポーネントがインストールされました。</translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation>すべてのコンポーネントがインストールされました。</translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>コンポーネント スクリプトを読み込んでいます...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>エイリアスの名前が既存のコンポーネント &quot;%1&quot;&#x3000;の名前と対立しまていす。</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>コンポーネントエイリアスが未解決です</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>エイリアス &quot;%1&quot; と &quot;%2&quot;&#x3000;の間の循環依存関係が検出されました。</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2143,6 +2209,18 @@ Please copy the installer to a local drive</source>
<source>Uninstalling</source>
<translation>アンインストールしています</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>&amp;オフラインインストーラーを作成</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>%1 のオフラインインストーラーを作成しています</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>オフラインインストーラーを作成しています</translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2186,7 +2264,7 @@ Please copy the installer to a local drive</source>
<translation>アンインストールの準備ができました</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>コンピューターから %1 を削除する準備が整っています。&lt;br&gt;&lt;font color=&quot;red&quot;&gt;プログラム ディレクトリ %2 が完全に削除されます&lt;/font&gt;。このディレクトリ内のコンテンツもすべて削除されます。</translation>
</message>
<message>
@@ -2198,7 +2276,7 @@ Please copy the installer to a local drive</source>
<translation>パッケージを更新する準備ができました</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>インストールを更新する準備が整っています。</translation>
</message>
<message>
@@ -2210,13 +2288,25 @@ Please copy the installer to a local drive</source>
<translation>インストールの準備ができました</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>コンピューターに %1 をインストールする準備が整っています。</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>更新の準備ができました</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>オフラインインストーラーを作成</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>オフラインインストーラーの準備ができました</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>選択したコンポーネントのオフラインインストーラーを作成するために準備が整っています</translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2273,7 +2363,7 @@ Please copy the installer to a local drive</source>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>%1 設定ウィザードを完了しています</translation>
</message>
</context>
@@ -2496,6 +2586,14 @@ Please copy the installer to a local drive</source>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation>現在のユーザーがファイル &amp;quot;%1&amp;quot; への読み取りアクセス権限を持っていることを確認するか、管理者として %2 を実行してください。</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>&apos;max-concurrent-operations&apos;の値が無効です。</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>オプション &apos;cache-path&apos; の値が空です。</translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2542,7 +2640,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>読み取り用の設定ファイル %1 を開けません: %2</translation>
</message>
<message>
- <source>Select Categories</source>
+ <source>Categories</source>
<translation>カテゴリを選択</translation>
</message>
</context>
@@ -2680,6 +2778,30 @@ or accept the elevation of access rights if being asked.</source>
<source>Deselect All</source>
<translation>すべての選択を解除</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>ローカル キャッシュ</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>読み込み時間を改善するために、リモートリポジトリからのメタ情報がディスクにキャッシュされます。別のディレクトリを選択してキャッシュを保存する、または現在のキャッシュの内容を消去することができます。</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>キャッシュのパス:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>キャッシュ ディレクトリの内容を削除します</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>キャッシュの消去</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>キャッシュを消去しています…</translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2739,7 +2861,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>エラー</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation>コンポーネント情報</translation>
</message>
</context>
@@ -2749,6 +2871,22 @@ or accept the elevation of access rights if being asked.</source>
<source>Extracting &quot;%1&quot;</source>
<translation>&quot;%1&quot; を抽出しています</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>&quot;%1&quot; は非サポートのアーカイブです。拡張子 &quot;%2&quot; ファイルのハンドラが登録されていません。</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>読み取り用のアーカイブ &quot;%1&quot; を開けません。: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>アーカイブ&quot;%1&quot;のコンテンツを読みこんでいる際にエラーが発生しました。:&#x3000;%2</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>&quot;%1&quot;から解凍されたファイルを削除します。</translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2761,27 +2899,185 @@ or accept the elevation of access rights if being asked.</source>
<name>QInstaller::ExtractArchiveOperation::Worker</name>
<message>
<source>Could not create handler object for archive &quot;%1&quot;: &quot;%2&quot;.</source>
- <translation type="unfinished"></translation>
+ <translation>アーカイブ &quot;%1&quot; のハンドラ オブジェクトを作成できませんでした: &quot;%2&quot;。</translation>
</message>
<message>
<source>Cannot open archive &quot;%1&quot; for reading: %2</source>
<translation>読み取り用のアーカイブ &quot;%1&quot; を開けません: %2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
- <translation type="unfinished"></translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>アーカイブ &quot;%1&quot; の抽出中にエラーが発生しました: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>読み取り用のアーカイブを開くことができません。: %1</translation>
</message>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation type="unfinished"></translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>エントリヘッダーを読み取れません。: %1</translation>
</message>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation type="unfinished"></translation>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>エントリ&quot;%1&quot;をディスクに書き込めません。: %2</translation>
</message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>アーカイブ &quot;%1&quot; の抽出中にエラーが発生しました: %2</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>読み取り用にアーカイブを開くことができません。: %1</translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation>エントリヘッダーを読み取れません。: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>エントリ&quot;%1&quot;をディスクへ書き込めません。: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>書き込み用のファイル &quot;%1&quot; を開けません。: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>読み取り用のファイル &quot;%1&quot; を開けません。: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation>&quot;%1&quot;のエントリーヘッダーを書き込めません。: %2</translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation>選択解除されたコンポーネント:</translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation>コンポーネントが&quot;%1&quot;に置き換えられました。:</translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation>既存の依存関係のない仮想コンポーネントの削除:</translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation>コンポーネントの依存関係&quot;%1&quot;削除:</translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation>コンポーネントの自動依存性&quot;%1&quot;削除:</translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation>%1 インストーラについて</translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation>%1メンテナンスツールについて</translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>空のパスでキャッシュを初期化できません。</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>キャッシュ用のディレクトリ &quot;%1&quot; を作成できません。</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>キャッシュを初期化できません: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>無効化されたキャッシュを消去できません。</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>マニフェストファイルを削除できません: %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>キャッシュを空にしている際にエラーが発生しました: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>無効化されたキャッシュからアイテムを取得できません。</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>無効化されたキャッシュからアイテムを取得できません。</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>無効化されたキャッシュにアイテムを登録できません。</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>空のアイテムは登録できません。</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>チェックサム %1 で無効なアイテムを登録できません</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>チェックサム %1 でアイテムを登録できません。同じチェックサムを持つアイテムが既にキャッシュに存在します。</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>アイテムをパス &quot;%1&quot; にコピー中にエラーが発生しました: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>無効化されたキャッシュからアイテムを削除できません。</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>チェックサム %1 で指定されたアイテムを削除できません: そのようなアイテムは存在しません。</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>ディレクトリ &quot;%1&quot; の削除中にエラーが発生しました: %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>キャッシュの無効化中にエラーが発生しました: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>マニフェストファイルを開けません: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>マニフェストファイルの内容を書き込めません: %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>無効化されたキャッシュを同期できません。</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>不明な登録モードが選択されました!</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>キャッシュが正常に消去されました!</translation>
</message>
</context>
</TS>
diff --git a/src/sdk/translations/ifw_ko.ts b/src/sdk/translations/ifw_ko.ts
index c0e309e27..39bd1ff85 100644
--- a/src/sdk/translations/ifw_ko.ts
+++ b/src/sdk/translations/ifw_ko.ts
@@ -212,6 +212,30 @@
<source>User defined repositories</source>
<translation>사용자 정의 저장소</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>로컬 캐시</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>로딩 시간을 줄이기 위해 원격 저장소 메타 정보가 디스크에 캐시(임시 저장)됩니다. 캐시를 저장하기 위해 다른 디렉터리를 선택하거나 현재 캐시를 지울 수 있습니다.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>캐시를 위한 경로</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>캐시 디렉터리 내용 삭제</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>캐시 모두 삭제</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>캐시 삭제 중...</translation>
+ </message>
</context>
<context>
<name>QInstaller</name>
@@ -323,6 +347,10 @@
<source>The specified module could not be found.</source>
<translation>지정된 모듈을 찾을 수 없습니다.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>캐시를 삭제한 후에 애플리케이션을 재시작하면 해결될 수 있습니다.</translation>
+ </message>
</context>
<context>
<name>QObject</name>
@@ -386,6 +414,10 @@
<source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
<translation>&apos;최대 동시 작업&apos;값이 올바르지 않습니다.</translation>
</message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>&apos;cache-path&apos; 옵션의 값이 비어 있습니다.</translation>
+ </message>
</context>
<context>
<name>BinaryLayout</name>
@@ -446,18 +478,6 @@
<translation>업데이터 모드에서는 구성요소에 하위 요소가 있으면 안 됩니다.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>요청된 UI 파일(&quot;%1&quot;)을 열 수 없음: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>요청된 UI 파일(&quot;%1&quot;)을 로드할 수 없음: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>요청된 라이선스 파일(&quot;%1&quot;)을 열 수 없음: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>오류</translation>
</message>
@@ -477,6 +497,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>선택한 구성요소를 로드하는 중에 오류가 발생했습니다. 이 구성요소를 설치할 수 없습니다.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>요청된 UI 파일(&quot;%1&quot;)을 열 수 없음: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>요청된 UI 파일(&quot;%1&quot;)을 로드할 수 없음: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>요청된 라이선스 파일(&quot;%1&quot;)을 열 수 없음: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -524,62 +568,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>기본(&amp;A):</translation>
+ <source>Default</source>
+ <translation>기본:</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>트리 보기에서 기본 구성요소를 선택합니다.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>재설정(&amp;R)</translation>
+ <source>Reset</source>
+ <translation>재설정</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>트리 보기에서 모든 구성요소를 원래 선택된 상태로 재설정합니다.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>모두 선택(&amp;S)</translation>
+ <source>Select All</source>
+ <translation>모두 선택</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>트리 보기에서 모든 구성요소를 선택합니다.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>모두 선택 해제(&amp;D)</translation>
+ <source>Deselect All</source>
+ <translation>모두 선택 해제</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>트리 보기에서 모든 구성요소를 선택 해제합니다.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>QBSP 파일 탐색(&amp;B)</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>QBSP(Qt Board Support Package) 파일을 선택하여 온라인 저장소에서 직접 사용할 수 없는 추가 콘텐츠를 설치합니다.</translation>
</message>
@@ -612,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>
@@ -623,6 +643,26 @@
<source>Search</source>
<translation>검색</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>QBSP 파일 탐색(&amp;Q)</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>선택</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>오류</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>오프라인 설치 프로그램 생성합니다.</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>지금 설치하지 않고 선택된 구성요소들로 부터 오프라인 설치 프로그램을 생성합니다.</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentSelectionPagePrivate</name>
@@ -631,8 +671,8 @@
<translation>필터</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>구성요소 정보</translation>
+ <source>Information</source>
+ <translation type="unfinished">구성요소 정보</translation>
</message>
<message>
<source>Error</source>
@@ -835,14 +875,6 @@
<translation>다운로드 오류</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>다운로드 중에 해시를 검증하지 못했습니다. 일시적인 오류이니 다시 시도하십시오.</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>해시를 검증할 수 없음</translation>
- </message>
- <message>
<source>Cannot download archive %1: %2</source>
<translation>%1 아카이브를 다운로드할 수 없음: %2</translation>
</message>
@@ -912,6 +944,28 @@ Error while loading %2</source>
<source>Total: </source>
<translation>총계:</translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>재시도 회수(%1) 초과</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>다운로드 중에 해시를 검증하지 못했습니다. 일시적인 오류이니 다시 시도하십시오.
+
+예상 해시값: %1
+다운로드 해시값: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>해시를 검증할 수 없음
+예상 해시값: %1
+다운로드 해시값: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1113,6 +1167,18 @@ Error while loading %2</source>
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>&quot;%2&quot;에 대한 누락된 종속성 &quot;%1&quot;을(를) 찾을 수 없습니다.</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>해결할 수 없는 종속성 문제가 발생. 설치된 구성 요소 &quot;%1&quot;이(가) 종속성 &quot;%2&quot;(으)로 설정되어 제거됩니다.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>별칭 &quot;%1&quot;으로 선택된 구성요소.</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>반복이 감지됨, 별칭 &quot;%1&quot;가 이미 추가됨.</translation>
+ </message>
</context>
<context>
<name>QInstaller::InstallIconsOperation</name>
@@ -1272,10 +1338,6 @@ Error while loading %2</source>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>&quot;%1&quot; 라이선스 파일을 쓸 수 없습니다.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>삭제할 라이선스 파일을 찾을 수 없습니다.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1299,10 +1361,6 @@ Error while loading %2</source>
<translation>패키지 관리자 코어 엔진이 누락되었습니다.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>메타 정보 다운로드 준비 중...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>압축된 저장소의 압축을 해제합니다. 약간의 시간이 걸릴 수 있습니다...</translation>
</message>
@@ -1339,14 +1397,6 @@ Error while loading %2</source>
<translation>&quot;%1&quot;에 대한 체크섬 불일치가 감지되었습니다.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>원격 저장소에서 메타 정보 검색 중... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>원격 저장소에서 메타 정보 검색 중... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>자료 보관소 &quot;%1&quot; 추출 중에 오류 발생: %2</translation>
</message>
@@ -1358,6 +1408,40 @@ Error while loading %2</source>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>지원되지 않는 &quot;%1&quot; 아카이브: &quot;%2&quot; 파일 접미사를 위해 등록된 핸들러가 없습니다.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>최신 업데이트 정보를 가져오는 중...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>로컬캐시에 새 항목 %n개를 업데이트하는 중...</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>캐시 디렉터리를 모두 삭제하고 애플리케이션을 다시 시작하면 문제를 해결할 수 있습니다.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>캐시 업데이트 도중 알수 없는 예외 발생</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>&quot;%1&quot; 파일을 읽기 위해 열 수 없음: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>&quot;%1&quot; 파일을 쓰기 위해 열 수 없음: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>원격 저장소에서 정보를 검색하는 중...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>원격 저장소에서 메타 정보 검색 중...</translation>
+ </message>
</context>
<context>
<name>QInstaller::FileTaskObserver</name>
@@ -1413,10 +1497,8 @@ Error while loading %2</source>
<translation>유지 보수 도구를 쓰는 중에 오류 발생</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-패키지 다운로드 중...</translation>
+ <source>Downloading packages...</source>
+ <translation>패키지 다운로드 중...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1533,44 +1615,28 @@ Do you want to continue?</source>
<translation>모든 종속성을 해결할 수 없습니다.</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>%1을(를) 설치할 수 없습니다. 구성요소를 찾을 수 없습니다.
-</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>%1을(를) 설치할 수 없습니다. 구성요소를 찾을 수 없습니다.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>%1 구성요소를 설치할 수 없습니다. %2에 대한 자동 종속성으로만 구성요소가 설치됩니다.
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>%1 구성요소를 설치할 수 없습니다. %2에 대한 자동 종속성으로만 구성요소가 설치됩니다.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>%1 구성요소를 설치할 수 없습니다. 구성요소를 확인할 수 없어 하위 구성요소 중 하나를 설치해야 합니다.
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>%1 구성요소를 설치할 수 없습니다. 구성요소를 확인할 수 없어 하위 구성요소 중 하나를 설치해야 합니다.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>%1 구성요소가 이미 설치됨
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>%1 구성요소가 이미 설치됨</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>%1을(를) 설치할 수 없습니다. 구성요소가 가상 구성요소 %2의 하위 항목입니다.
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>%1을(를) 설치할 수 없습니다. 구성요소가 가상 구성요소 %2의 하위 항목입니다.</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>%1을(를) 설치할 수 없습니다. 가상 구성요소입니다.
-</translation>
- </message>
- <message>
- <source>Running processes found.</source>
- <translation>실행 중인 프로세스를 찾았습니다.</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>%1을(를) 설치할 수 없습니다. 가상 구성요소입니다.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1589,10 +1655,6 @@ Do you want to continue?</source>
<translation>디스크 공간이 부족하여 선택한 구성요소를 모두 저장할 수 없습니다! %1은(는) 사용 가능하지만 최소한 %2이(가) 필요합니다.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>디스크 공간이 부족하여 임시 파일을 저장할 수 없습니다! %1은(는) 사용 가능하지만 최소한 %2이(가) 필요합니다.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>설치를 위해 선택한 볼륨은 설치 공간이 충분한 것으로 보이지만, 설치 후에 남은 공간이 볼륨 공간의 %1 미만일 것으로 예상됩니다.</translation>
</message>
@@ -1617,10 +1679,29 @@ Do you want to continue?</source>
<translation>구성요소들이 제거됩니다.</translation>
</message>
<message>
- <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.
-</source>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
<translation>%1 구성요소를 설치할 수 없습니다. 해당 구성요소를 불러오는 중 문제가 발생했으므로 불안정한 것으로 표시되었으며 선택할 수 없습니다.</translation>
</message>
+ <message>
+ <source>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.</source>
+ <translation>디스크 공간이 부족하여 임시 파일을 저장할 수 없습니다. %1은(는) 사용 가능하지만 최소한 %2이(가) 필요합니다. 설치 프로그램 설정에서 로컬 캐시 경로를 수정하여 임시 파일의 다른 위치를 선택할 수 있습니다.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>구성 요소들의 설치 제거를 해결 할 수 없습니다.</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>별칭 %1을 선택할 수 없습니다. 이 명칭을 불러오는 데 문제가 발생해 불안정한 것으로 설정되며 선택할 수 없습니다.</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>%1을 선택할 수 없습니다. 명칭이 가상(virtual)로 설정되어 있으며 이는 수동으로 선택될 수 없습니다.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>생성된 설치 프로그램이 %1 디스크 공간을 사용할 것입니다.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1737,16 +1818,12 @@ Do you want to continue?</source>
<translation>유지 보수 도구 생성 중</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-설치가 완료되었습니다!</translation>
+ <source>Installation finished!</source>
+ <translation>설치가 완료되었습니다!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-설치가 중단되었습니다!</translation>
+ <source>Installation aborted!</source>
+ <translation>설치가 중단되었습니다!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1757,16 +1834,12 @@ Installation aborted!</source>
<translation>선택 취소한 구성요소 제거 중...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-업데이트가 완료되었습니다!</translation>
+ <source>Update finished!</source>
+ <translation>업데이트가 완료되었습니다!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-업데이트가 중단되었습니다!</translation>
+ <source>Update aborted!</source>
+ <translation>업데이트가 중단되었습니다!</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1809,10 +1882,8 @@ Update aborted!</source>
<translation>오프라인 생성이 중단되었습니다!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-구성요소 %1 설치 중</translation>
+ <source>Installing component %1</source>
+ <translation>구성요소 %1 설치 중</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1859,10 +1930,6 @@ Installing component %1</source>
<translation>메타 정보를 검색할 수 없음: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>임시 업데이트 소스 정보를 추가할 수 없습니다.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>어떠한 업데이트 소스 정보도 추가할 수 없습니다.</translation>
</message>
@@ -1871,20 +1938,16 @@ Installing component %1</source>
<translation>구성요소 &quot;%1&quot; 및 &quot;%2&quot; 간의 종속성 사이클이 감지되었습니다.</translation>
</message>
<message>
- <source>
-Preparing to unpack components...</source>
- <translation>
-구성요소들의 압축해제를 준비합니다.</translation>
+ <source>Preparing to unpack components...</source>
+ <translation>구성요소들의 압축해제를 준비합니다.</translation>
</message>
<message>
<source>%1 of %2 operations completed.</source>
<translation>%2 의 %1 작업들을 완료했습니다.</translation>
</message>
<message>
- <source>
-Unpacking components...</source>
- <translation>
-구성요소들의 압축해제를 준비합니다.</translation>
+ <source>Unpacking components...</source>
+ <translation>구성요소들의 압축해제를 준비합니다.</translation>
</message>
<message>
<source>%1 of %2 operations rolled back.</source>
@@ -1902,6 +1965,22 @@ Unpacking components...</source>
<source>All components installed.</source>
<translation>모든 구성요소들을 설치했습니다.</translation>
</message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>구성요소 스크립트를 불러오는 중...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>별칭이 존재하는 구성요소 &quot;%1&quot;와 충돌하는 이름을 정의합니다.</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>해결되지 않은 구성 요소 별칭들</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>별칭들 &quot;%1&quot;와 &quot;%2&quot; 사이에 순환 의존성 발견됨.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -1959,11 +2038,11 @@ Please copy the installer to a local drive</source>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>설정 - %1</translation>
+ <source>Welcome</source>
+ <translation>환영</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>%1 설정 마법사에 오신 것을 환영합니다.</translation>
</message>
<message>
@@ -1991,10 +2070,6 @@ Please copy the installer to a local drive</source>
<translation>사용 가능한 업데이트가 없습니다.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> 로컬 패키지 관리만 가능합니다.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>종료(&amp;Q)</translation>
</message>
@@ -2081,7 +2156,7 @@ Please copy the installer to a local drive</source>
<translation>설치 제거 준비 완료</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>컴퓨터에 %1 제거를 시작할 준비가 되었습니다.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;%2 프로그램 디렉터리는 모두 삭제되며&lt;/font&gt;, 해당 디렉토리에 포함된 모든 콘텐츠도 삭제됩니다!</translation>
</message>
<message>
@@ -2093,7 +2168,7 @@ Please copy the installer to a local drive</source>
<translation>패키지 업데이트 준비 완료</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>이제 설치 업데이트를 시작할 준비가 되었습니다.</translation>
</message>
<message>
@@ -2105,13 +2180,25 @@ Please copy the installer to a local drive</source>
<translation>설치 준비 완료</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>컴퓨터에 %1 설치를 시작할 준비가 되었습니다.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>업데이트 준비 완료</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>오프라인 설치 프로그램을 생성합니다.</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>오프라인 설치 프로그램을 생성하기 위해 준비합니다.</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>선택된 구성요소들에 대한 오프라인 설치 프로그램을 생성하기 위한 모든 필요한 정보가 사용 가능합니다</translation>
+ </message>
</context>
<context>
<name>QInstaller::PerformInstallationPage</name>
@@ -2151,11 +2238,23 @@ Please copy the installer to a local drive</source>
<source>Uninstalling</source>
<translation>설치 제거 중</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>오프라인 설치 프로그램을 생성합니다.</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>%1에 대한 오프라인 설치 프로그램을 생성하는 중</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>오프라인 설치 프로그램을 생성하는 중</translation>
+ </message>
</context>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>%1 마법사 완료 중</translation>
</message>
<message>
@@ -2163,7 +2262,7 @@ Please copy the installer to a local drive</source>
<translation>완료됨</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>%2 마법사를 종료하려면 %1을(를) 클릭하십시오.</translation>
</message>
<message>
@@ -2175,14 +2274,14 @@ Please copy the installer to a local drive</source>
<translation>지금 %1을(를) 실행하십시오.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>%1 마법사가 실패했습니다.</translation>
</message>
</context>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>%1 설정 마법사 완료 중</translation>
</message>
</context>
@@ -2313,8 +2412,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>설정 파일 &quot;%1&quot;을(를) 읽기 위해 열 수 없음: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>범주 선택</translation>
+ <source>Categories</source>
+ <translation type="unfinished">범주 선택</translation>
</message>
</context>
<context>
@@ -2501,6 +2600,10 @@ or accept the elevation of access rights if being asked.</source>
<source>Try again</source>
<translation>다시 시도하기</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>%1을(를) 다운로드 할 수 없음. &quot;%2&quot;을(를) 위한 디렉터리를 생성할 수 없음.</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -2771,10 +2874,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>&quot;%1&quot;을(를) 읽을 수 없음</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>%2의 %1에서 파싱 오류 발생, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>%1 루트 요소는 예상하지 못했습니다. &apos;Updates&apos;여야 합니다.</translation>
</message>
@@ -2840,7 +2939,7 @@ or accept the elevation of access rights if being asked.</source>
<name>QInstaller::LibArchiveArchive</name>
<message>
<source>Cannot open archive for reading: %1</source>
- <translation >아카이브를 읽기 위해 열 수 없음: %1</translation>
+ <translation>아카이브를 읽기 위해 열 수 없음: %1</translation>
</message>
<message>
<source>Cannot read entry header: %1</source>
@@ -2855,10 +2954,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>&quot;%1&quot; 파일을 쓰기 위해 열 수 없음: %2</translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot;for reading: %2</source>
- <translation>&quot;%1&quot; 파일을 읽기 위해 열 수 없음: %2</translation>
- </message>
- <message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>&quot;%1&quot; 파일을 읽기 위해 열 수 없음: %2</translation>
</message>
@@ -2901,4 +2996,98 @@ or accept the elevation of access rights if being asked.</source>
<translation>%1 유지 보수 도구에 대하여</translation>
</message>
</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>빈 경로에 캐시를 초기화할 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>캐시 디렉터리 &quot;%1&quot;를 만들 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>캐시를 초기화할 수 없음: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>무효화한 캐시를 삭제할 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>매니페스트 파일을 삭제할 수 없음: %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>캐시를 모두 삭제하는 중 오류 발생: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>무효화한 캐시에서 아이템들을 가져올 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>무효화한 캐시에서 아이템을 가져올 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>무효화한 캐시에 아이템을 등록할 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>빈 아이템을 등록할 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>체크섬 %1 무효한 아이템을 등록할 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>체크섬 %1 무효한 아이템을 등록할 수 없음. 같은 체크섬을 가진 아이템이 캐시에 있습니다.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>아이템을 &quot;%1&quot;경로에 복사하는 중 오류 발생: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>무효화한 캐시에서 아이템을 지울 수 없음.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>체크섬 %1 아이템을 지울 수 없음: 해당 아이템이 없음.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>&quot;%1&quot; 디렉터리를 지우는 중 오류 발생: %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>캐시를 무효화하는 중 오류 발생: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>매니페스트 파일을 열 수 없음: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>매니페스트 파일 내용을 쓸 수 없음: %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>무효화된 캐시를 동기화할 수 없습니다.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>알 수 없는 등록 방법이 사용됨!</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>캐시 삭제를 성공했습니다!</translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_pl.ts b/src/sdk/translations/ifw_pl.ts
index 3309e81ec..572ea5ddc 100644
--- a/src/sdk/translations/ifw_pl.ts
+++ b/src/sdk/translations/ifw_pl.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Nie można znaleźć brakującej zależności &quot;%1&quot; dla &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -310,6 +322,10 @@
<source>Try again</source>
<translation>Spróbuj ponownie</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -494,10 +510,6 @@
<translation>Nie można odczytać &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Błąd analizy w %1 przy %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Nieoczekiwany element główny %1, powinno być &quot;Aktualizacje&quot;.</translation>
</message>
@@ -749,6 +761,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>Nieprawidłowa zawartość w &quot;%1&quot;.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -757,18 +773,6 @@
<translation>Elementy nie mogą mieć elementów podrzędnych w trybie programu aktualizującego.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Nie można otworzyć żądanego pliku interfejsu użytkownika &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Nie można wczytać żądanego pliku interfejsu użytkownika &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Nie można otworzyć żądanego pliku licencji &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Błąd</translation>
</message>
@@ -788,6 +792,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>Podczas ładowania wybranego komponentu wystąpił błąd. Nie można zainstalować tego składnika.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nie można otworzyć żądanego pliku interfejsu użytkownika &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nie można wczytać żądanego pliku interfejsu użytkownika &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Nie można otworzyć żądanego pliku licencji &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -835,42 +863,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;Domyślne</translation>
+ <source>Default</source>
+ <translation>Domyślne</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Zaznacz domyślne komponenty.</translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Resetuj</translation>
+ <source>Reset</source>
+ <translation>Resetuj</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Zresetuj wszystkie komponenty do ich pierwotnego stanu zaznaczenia.</translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Zaznacz wszystkie</translation>
+ <source>Select All</source>
+ <translation>Zaznacz wszystkie</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Zaznacz wszystkie komponenty.</translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Usuń zaznaczenie wszystkich</translation>
+ <source>Deselect All</source>
+ <translation>Usuń zaznaczenie wszystkich</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>Odznacz wszystkie komponenty.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Znajdź pliki QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Wybierz plik Qt Board Support Package, aby zainstalować dodatkową zawartość, która nie jest bezpośrednio dostępna w repozytoriach online.</translation>
</message>
@@ -899,40 +923,40 @@
<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>
<translation>Wymagane komponenty muszą zostać zaktualizowane, zanim będzie można wybrać inne komponenty do aktualizacji</translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
+ <source>Filter the enabled repository categories</source>
+ <translation>Filtruj włączone kategorie repozytorium</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
+ <source>Search</source>
+ <translation>Szukaj</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
- <translation>Filtruj włączone kategorie repozytorium</translation>
+ <source>Browse &amp;QBSP files</source>
+ <translation>&amp;Znajdź pliki QBSP</translation>
</message>
<message>
- <source>Search</source>
- <translation>Szukaj</translation>
+ <source>Select</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation type="unfinished">Błąd</translation>
</message>
</context>
<context>
@@ -1216,6 +1240,10 @@ Błąd podczas wczytywania %2</translation>
<source>Total: </source>
<translation>Razem: </translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1357,7 +1385,7 @@ Błąd podczas wczytywania %2</translation>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Wykonywanie Kreatora %1</translation>
</message>
<message>
@@ -1365,7 +1393,7 @@ Błąd podczas wczytywania %2</translation>
<translation>Zakończone</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Kliknij pozycję %1, aby zamknąć Kreator %2.</translation>
</message>
<message>
@@ -1377,7 +1405,7 @@ Błąd podczas wczytywania %2</translation>
<translation>Uruchom %1 teraz.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Działanie Kreatora %1 nie powiodło się.</translation>
</message>
</context>
@@ -1426,11 +1454,11 @@ Błąd podczas wczytywania %2</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Konfiguracja - %1</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Witamy w Kreatorze konfiguracji %1.</translation>
</message>
<message>
@@ -1458,10 +1486,6 @@ Błąd podczas wczytywania %2</translation>
<translation>Brak dostępnych aktualizacji.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Dostępne jedynie lokalne zarządzanie pakietami.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Zakończ</translation>
</message>
@@ -1512,10 +1536,6 @@ Błąd podczas wczytywania %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Nie można zapisać pliku licencji: &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Nie znaleziono plików licencji do usunięcia.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1539,10 +1559,6 @@ Błąd podczas wczytywania %2</translation>
<translation>Brak podstawowego mechanizmu menedżera pakietów.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Przygotowanie metainformacji do pobrania...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Rozpakowywanie skompresowanych repozytoriów. Może to chwilę potrwać...</translation>
</message>
@@ -1575,14 +1591,6 @@ Błąd podczas wczytywania %2</translation>
<translation>Wykryto niezgodność sumy kontrolnej dla &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Pobieranie metainformacji z repozytorium zdalnego... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Pobieranie metainformacji z repozytorium zdalnego... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>Błąd podczas wyodrębniania archiwum &quot;%1&quot;: %2</translation>
</message>
@@ -1598,6 +1606,42 @@ Błąd podczas wczytywania %2</translation>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>Nieznane archiwum &quot;%1&quot;: brak zarejestrowanego uchwytu dla pliku z rozszerzeniem &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Nie można otworzyć pliku &quot;%1&quot; do zapisu: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Pobieranie informacji ze zdalnych repozytoriów...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Pobieranie metainformacji z repozytorium zdalnego...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1606,10 +1650,8 @@ Błąd podczas wczytywania %2</translation>
<translation>Błąd zapisu w narzędziu konserwacji</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Pobieranie pakietów...</translation>
+ <source>Downloading packages...</source>
+ <translation>Pobieranie pakietów...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1714,32 +1756,22 @@ Czy chcesz kontynuować?</translation>
<translation>Nie można rozwiązać wszystkich zależności.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>Nie można zainstalować komponentu %1. Komponent jest instalowany tylko jako automatyczna zależność od %2.
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>Nie można zainstalować komponentu %1. Komponent jest instalowany tylko jako automatyczna zależność od %2.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
+ <source>Component %1 already installed</source>
<translation>Komponent %1 jest już zainstalowany</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>Nie można zainstalować %1. Komponent jest wirtualny.
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>Nie można zainstalować %1. Komponent jest wirtualny.</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
+ <source>Cannot install %1. Component not found.</source>
<translation>Nie można zainstalować %1. Komponent nieznaleziony.</translation>
</message>
<message>
- <source>Running processes found.</source>
- <translation>Znaleziono uruchomione procesy.</translation>
- </message>
- <message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
<translation>Nie można podnieść praw dostępu podczas uruchamiania z wiersza poleceń. Uruchom ponownie aplikację jako administrator.</translation>
</message>
@@ -1776,16 +1808,12 @@ Czy chcesz kontynuować?</translation>
<translation>Anulowanie instalacji</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>Nie można zainstalować składnika %1. Komponent nie jest dostępny, co oznacza, że ​​musisz wybrać jeden z podkomponentów.
- </translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>Nie można zainstalować składnika %1. Komponent nie jest dostępny, co oznacza, że ​​musisz wybrać jeden z podkomponentów.</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>Nie można zainstalować %1. Komponent jest potomkiem komponentu wirtualnego %2.
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>Nie można zainstalować %1. Komponent jest potomkiem komponentu wirtualnego %2.</translation>
</message>
<message>
<source>Not enough disk space to store temporary files and the installation. %1 are available, while the minimum required is %2.</source>
@@ -1796,10 +1824,6 @@ Czy chcesz kontynuować?</translation>
<translation>Za mało miejsca na dysku do zapisu wszystkich wybranych komponentów! %1 jest dostępnych, podczas gdy wymagane minimum to %2.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>Za mało miejsca na dysku do zapisu plików tymczasowych! %1 jest dostępnych, podczas gdy wymagane minimum to %2.</translation>
- </message>
- <message>
<source>Invalid</source>
<translation>Nieprawidłowy</translation>
</message>
@@ -1808,10 +1832,29 @@ Czy chcesz kontynuować?</translation>
<translation>Komponenty do usunięcia</translation>
</message>
<message>
- <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.
-</source>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
<translation>Nie można zainstalować komponentu %1. Wystąpił problem z jego załadowaniem, został oznaczony jako niestabilny i nie można go wybrać</translation>
</message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1844,7 +1887,7 @@ Czy chcesz kontynuować?</translation>
%1</source>
<translation>Poniższe procesy powinny być zatrzymane, aby kontynuować:
-
+
%1</translation>
</message>
<message>
@@ -1928,16 +1971,12 @@ Czy chcesz kontynuować?</translation>
<translation>Tworzenie narzędzia konserwacji</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Ukończono instalację!</translation>
+ <source>Installation finished!</source>
+ <translation>Ukończono instalację!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Przerwano instalację!</translation>
+ <source>Installation aborted!</source>
+ <translation>Przerwano instalację!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1948,16 +1987,12 @@ Przerwano instalację!</translation>
<translation>Trwa usuwanie elementów, których zaznaczenie wyłączono...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Aktualizacja zakończona!</translation>
+ <source>Update finished!</source>
+ <translation>Aktualizacja zakończona!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Aktualizację przerwano!</translation>
+ <source>Update aborted!</source>
+ <translation>Aktualizację przerwano!</translation>
</message>
<message>
<source>Cannot create target directory for installer.</source>
@@ -1992,10 +2027,8 @@ Aktualizację przerwano!</translation>
<translation>Przerwano generowanie instalatora offline</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Trwa instalowanie elementu %1</translation>
+ <source>Installing component %1</source>
+ <translation>Trwa instalowanie elementu %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2028,10 +2061,6 @@ Trwa instalowanie elementu %1</translation>
<translation>Nie można pobrać metainformacji: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Nie można dodać informacji o tymczasowym źródle aktualizacji.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Nie można znaleźć żadnych informacji o źródle aktualizacji.</translation>
</message>
@@ -2062,8 +2091,7 @@ Trwa instalowanie elementu %1</translation>
%1</translation>
</message>
<message>
- <source>
-Preparing to unpack components...</source>
+ <source>Preparing to unpack components...</source>
<translation>Przygotowanie do usunięcia komponentów...</translation>
</message>
<message>
@@ -2071,8 +2099,7 @@ Preparing to unpack components...</source>
<translation>Ukończono %1 z %2 operacji.</translation>
</message>
<message>
- <source>
-Unpacking components...</source>
+ <source>Unpacking components...</source>
<translation>Rozpakowywanie komponentów...</translation>
</message>
<message>
@@ -2091,6 +2118,22 @@ Unpacking components...</source>
<source>All components installed.</source>
<translation>Wszystkie komponenty zostały zainstalowane</translation>
</message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2194,6 +2237,18 @@ Please copy the installer to a local drive</source>
<source>Uninstalling</source>
<translation>Odinstalowywanie</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2237,7 +2292,7 @@ Please copy the installer to a local drive</source>
<translation>Gotowy do dezinstalacji</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Instalator jest już gotowy, aby rozpocząć usuwanie %1 z komputera.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;Katalog programu %2 zostanie w całości usunięty&lt;/font&gt;, włącznie z całą zawartością!</translation>
</message>
<message>
@@ -2249,7 +2304,7 @@ Please copy the installer to a local drive</source>
<translation>Gotowy do aktualizacji pakietów</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>Instalator jest już gotowy do rozpoczęcia aktualizowania instalacji.</translation>
</message>
<message>
@@ -2261,13 +2316,25 @@ Please copy the installer to a local drive</source>
<translation>Gotowy do instalacji</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Instalator jest gotowy do rozpoczęcia instalacji %1 na komputerze.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Gotowy do aktualizacji</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2324,7 +2391,7 @@ Please copy the installer to a local drive</source>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Wykonywanie Kreatora instalacji %1</translation>
</message>
</context>
@@ -2551,6 +2618,10 @@ Please copy the installer to a local drive</source>
<source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
<translation>Nieprawidłowa wartość dla &apos;max-concurrent-operations&apos;.</translation>
</message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2597,8 +2668,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Nie można otworzyć pliku ustawień %1 do odczytu: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Wybierz kategorie</translation>
+ <source>Categories</source>
+ <translation>Kategorie</translation>
</message>
</context>
<context>
@@ -2735,6 +2806,30 @@ or accept the elevation of access rights if being asked.</source>
<source>The server&apos;s URL that contains a valid repository.</source>
<translation>Adres URL serwera, który zawiera prawidłowe repozytorium.</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2798,8 +2893,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Błąd</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informacje o składnikach</translation>
+ <source>Information</source>
+ <translation>Informacja</translation>
</message>
</context>
<context>
@@ -2892,10 +2987,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>Nie można otworzyć pliku &quot;%1&quot; do zapisu: %2</translation>
</message>
<message>
- <source>Cannot open file &quot;%1&quot;for reading: %2</source>
- <translation>Nie można otworzyć pliku &quot;%1&quot;do odczytu: %2</translation>
- </message>
- <message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
<translation>Nie można otworzyć pliku &quot;%1&quot; do odczytu: %2</translation>
</message>
@@ -2927,4 +3018,98 @@ or accept the elevation of access rights if being asked.</source>
<translation>Usunięte automatyczne zależności komponentu &quot;%1&quot;:</translation>
</message>
</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/src/sdk/translations/ifw_pt_BR.ts b/src/sdk/translations/ifw_pt_BR.ts
index 0dac44232..bae3a695c 100644
--- a/src/sdk/translations/ifw_pt_BR.ts
+++ b/src/sdk/translations/ifw_pt_BR.ts
@@ -210,6 +210,30 @@
</message>
<message>
<source>The server&apos;s URL that contains a valid repository.</source>
+ <translation>URL do servidor que contém um repositório válido.</translation>
+ </message>
+ <message>
+ <source>Local cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -225,31 +249,31 @@
</message>
<message>
<source>Cannot start installer binary as updater.</source>
- <translation type="unfinished"></translation>
+ <translation>Erro a iniciar o binário do instalador como atualizador.</translation>
</message>
<message>
<source>Cannot start installer binary as package manager.</source>
- <translation type="unfinished"></translation>
+ <translation>Erro a iniciar o binário do instalador como gestor pacotes.</translation>
</message>
<message>
<source>Cannot start installer binary as uninstaller.</source>
- <translation type="unfinished"></translation>
+ <translation>Erro a iniciar o binário do instalador em modo desinstalador.</translation>
</message>
<message>
<source>Empty repository list for option &apos;addRepository&apos;.</source>
- <translation type="unfinished"></translation>
+ <translation>Lista de repositórios para a opção &apos;Adicionar Repositório&apos; está vazia.</translation>
</message>
<message>
<source>Empty repository list for option &apos;addTempRepository&apos;.</source>
- <translation type="unfinished"></translation>
+ <translation>Lista de repositórios para a opção &apos;Adicionar Repositório Temporário&apos; está vazia.</translation>
</message>
<message>
<source>Empty repository list for option &apos;setTempRepository&apos;.</source>
- <translation type="unfinished"></translation>
+ <translation>Lista de repositórios para a opção &apos;Adicionar Repositório Temporário&apos; está vazia.</translation>
</message>
<message>
<source>Empty repository list for option &apos;installCompressedRepository&apos;.</source>
- <translation type="unfinished"></translation>
+ <translation>Lista de repositórios para a opção &apos;Instalar Repositório Comprimido&apos; está vazia.</translation>
</message>
<message>
<source>The file %1 does not exist.</source>
@@ -257,18 +281,26 @@
</message>
<message>
<source>Arguments missing for option %1</source>
- <translation type="unfinished"></translation>
+ <translation>Falta argumentos para opção %1</translation>
</message>
<message>
<source>Invalid button value %1 </source>
- <translation type="unfinished"></translation>
+ <translation>Valor de butão %1 não é válido.</translation>
</message>
<message>
<source>Incorrect arguments for %1</source>
- <translation type="unfinished"></translation>
+ <translation>Argumentos para %1 não são corretos.</translation>
</message>
<message>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
+ <translation>Por favor, verifique que o usuário tem direitos de leitura do arquivo &quot;%1&quot; ou tente correr %2 com um conta de administrador.</translation>
+ </message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>Valor inválido para &apos;max-concurrent-operations&apos;.</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -372,7 +404,7 @@
</message>
<message>
<source>Cannot copy file &quot;%1&quot; to &quot;%2&quot;.</source>
- <translation type="unfinished"></translation>
+ <translation>Não é possível copiar o arquivo &quot;%1&quot; para &quot;%2&quot;.</translation>
</message>
<message>
<source>The specified module could not be found.</source>
@@ -380,6 +412,10 @@
</message>
<message>
<source>Invalid content in &quot;%1&quot;.</source>
+ <translation>Conteúdo inválido em &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -442,18 +478,6 @@
<translation>Componentes não podem ter filhos no modo de atualização.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Não é possível abrir o arquivo da interface do usuário solicitado &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Não é possível carregar o arquivo da interface do usuário solicitado &quot;%1&quot;:%2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Não é possível abrir o arquivo de licença solicitado &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Erro</translation>
</message>
@@ -471,7 +495,31 @@
</message>
<message>
<source>There was an error loading the selected component. This component cannot be installed.</source>
- <translation type="unfinished"></translation>
+ <translation>Ocorreu um erro a carregar o componente selecionado. Este componente não pode ser instalado.</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Não é possível abrir o arquivo da interface do usuário solicitado &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Não é possível carregar o arquivo da interface do usuário solicitado &quot;%1&quot;:%2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Não é possível abrir o arquivo de licença solicitado &quot;%1&quot;: %2
+
+%3 &quot;%4&quot;</translation>
</message>
</context>
<context>
@@ -532,12 +580,8 @@
<translation>Não é possível salvar a saída de &quot;%1&quot; em uma chave de instalação vazia.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>O arquivo &quot;%1&quot; não existe ou não é um binário executável.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>A execução de &quot;%1&quot; resultou em uma falha.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation>Falha a executar o comando: &quot;%1&quot;: %2</translation>
</message>
</context>
<context>
@@ -636,11 +680,11 @@
</message>
<message>
<source>Cannot create path &quot;%1&quot;.</source>
- <translation type="unfinished"></translation>
+ <translation>Não é possível criar o caminho &quot;%1&quot;. </translation>
</message>
<message>
<source>Cannot remove directory &quot;%1&quot;.</source>
- <translation type="unfinished"></translation>
+ <translation>Não é possível remover o </translation>
</message>
<message>
<source>Cannot open file &quot;%1&quot; for reading.</source>
@@ -676,7 +720,11 @@
</message>
<message>
<source>Cannot create archive &quot;%1&quot;: %2</source>
- <translation type="unfinished">Não é possível criar o arquivo &quot;%1&quot;: %2</translation>
+ <translation>Não é possível criar o arquivo &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>O arquivo &quot;%1&quot; não é suportado: Não tem programa registado para a extenção &quot;%2&quot;.</translation>
</message>
</context>
<context>
@@ -744,11 +792,11 @@ Erro ao carregar %2</translation>
</message>
<message>
<source>%1 of %2</source>
- <translation type="unfinished">%1 de %2</translation>
+ <translation>%1 de %2</translation>
</message>
<message>
<source>%1 downloaded.</source>
- <translation type="unfinished">%1 baixado.</translation>
+ <translation>%1 baixado.</translation>
</message>
<message numerus="yes">
<source>%n day(s), </source>
@@ -788,10 +836,14 @@ Erro ao carregar %2</translation>
</message>
<message>
<source>Archive: </source>
- <translation type="unfinished"></translation>
+ <translation>Arquivo comprimido:</translation>
</message>
<message>
<source>Total: </source>
+ <translation>Total:</translation>
+ </message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -974,6 +1026,18 @@ Erro ao carregar %2</translation>
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Não foi possível encontrar a dependência ausente &quot;%1&quot; para &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::InstallIconsOperation</name>
@@ -1003,7 +1067,7 @@ Erro ao carregar %2</translation>
</message>
<message>
<source>Cannot prepare to backup file &quot;%1&quot;: %2</source>
- <translation type="unfinished"></translation>
+ <translation>Não é possível preparar o backup do arquivo &quot;%1&quot;: %2</translation>
</message>
</context>
<context>
@@ -1074,11 +1138,11 @@ Erro ao carregar %2</translation>
</message>
<message>
<source>Internal code: %1</source>
- <translation type="unfinished"></translation>
+ <translation>Código interno: %1</translation>
</message>
<message>
<source>Not enough memory</source>
- <translation type="unfinished"></translation>
+ <translation>Não há memória suficiente.</translation>
</message>
</context>
<context>
@@ -1133,10 +1197,6 @@ Erro ao carregar %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Não é possível gravar o arquivo de licença &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Nenhum arquivo de licença foi encontrado para ser excluído.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1160,10 +1220,6 @@ Erro ao carregar %2</translation>
<translation>Faltando o mecanismo principal do gerenciador de pacotes.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Preparando o download de metadados...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Descompactando repositórios compactados. Isso pode demorar um pouco...</translation>
</message>
@@ -1188,14 +1244,6 @@ Erro ao carregar %2</translation>
<translation>Incompatibilidade detectada na soma de verificação para &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Recuperando metadados do repositório remoto...%1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Recuperando metadados do repositório remoto... </translation>
- </message>
- <message>
<source>Failure to fetch repositories.</source>
<translation>Falha ao buscar repositórios.</translation>
</message>
@@ -1215,6 +1263,45 @@ Erro ao carregar %2</translation>
<source>Metadata download canceled.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Não é possível abrir o arquivo &quot;%1&quot; para gravação: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Recuperando informações de repositórios remotos...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Recuperando metadados do repositório remoto...</translation>
+ </message>
</context>
<context>
<name>QInstaller::FileTaskObserver</name>
@@ -1274,10 +1361,8 @@ Erro ao carregar %2</translation>
<translation>Erro ao gravar a Ferramenta de Manutenção</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Baixando pacotes...</translation>
+ <source>Downloading packages...</source>
+ <translation>Baixando pacotes...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1375,7 +1460,7 @@ Você quer continuar?</translation>
</message>
<message>
<source>Application running in Uninstaller mode.</source>
- <translation>Aplicação em execução no modo Desinstalador.</translation>
+ <translation>Aplicação em execução no modo desinstalador.</translation>
</message>
<message>
<source>There is an important update available, please run the updater first.</source>
@@ -1386,36 +1471,23 @@ Você quer continuar?</translation>
<translation>Não é possível resolver todas as dependências.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Componentes prestes a serem removidos.</translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
+ <source>Component %1 already installed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
+ <source>Cannot install %1. Component is virtual.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Running processes found.</source>
+ <source>Cannot install %1. Component not found.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1439,10 +1511,6 @@ Você quer continuar?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation type="unfinished"></translation>
</message>
@@ -1463,16 +1531,39 @@ Você quer continuar?</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
+ <source>Components about to be removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1591,16 +1682,12 @@ Você quer continuar?</translation>
<translation>Criando Ferramenta de Manutenção</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Instalação finalizada!</translation>
+ <source>Installation finished!</source>
+ <translation>Instalação finalizada!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Instalação cancelada!</translation>
+ <source>Installation aborted!</source>
+ <translation>Instalação cancelada!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1611,16 +1698,12 @@ Instalação cancelada!</translation>
<translation>Removendo componentes não selecionados...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Atualização finalizada!</translation>
+ <source>Update finished!</source>
+ <translation>Atualização finalizada!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Atualização cancelada!</translation>
+ <source>Update aborted!</source>
+ <translation>Atualização cancelada!</translation>
</message>
<message>
<source>Cannot create target directory for installer.</source>
@@ -1655,10 +1738,8 @@ Atualização cancelada!</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Instalando o componente %1</translation>
+ <source>Installing component %1</source>
+ <translation>Instalando o componente %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1691,10 +1772,6 @@ Instalando o componente %1</translation>
<translation>Não é possível recuperar metadados: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Não é possível adicionar informações de fontes de atualização temporária.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Não é possível encontrar informações de fonte de atualização.</translation>
</message>
@@ -1723,6 +1800,50 @@ Instalando o componente %1</translation>
%1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -1780,11 +1901,11 @@ Por favor, copie o instalador para uma unidade local</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>%1 - Configuração</translation>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Bem-vindo ao Assistente de Configuração %1.</translation>
</message>
<message>
@@ -1812,10 +1933,6 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation>Nenhuma atualização disponível.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Apenas gerenciamento de pacotes locais disponível.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Sair</translation>
</message>
@@ -1855,42 +1972,38 @@ Por favor, copie o instalador para uma unidade local</translation>
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>Def&amp;ault</translation>
+ <source>Default</source>
+ <translation>Default</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Resetar</translation>
+ <source>Reset</source>
+ <translation>Resetar</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Selecionar Todos</translation>
+ <source>Select All</source>
+ <translation>Selecionar Todos</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Desmarcar Todos</translation>
+ <source>Deselect All</source>
+ <translation>Desmarcar Todos</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Procurar arquivos QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation type="unfinished"></translation>
</message>
@@ -1919,37 +2032,41 @@ 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>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
+ <source>Filter the enabled repository categories</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
+ <source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
+ <source>Create Offline Installer</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
+ <source>Create offline installer from selected components, instead of installing now.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
+ <source>Browse &amp;QBSP files</source>
+ <translation>&amp;Procurar arquivos QBSP</translation>
+ </message>
+ <message>
+ <source>Select</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Error</source>
+ <translation type="unfinished">Erro</translation>
+ </message>
</context>
<context>
<name>QInstaller::TargetDirectoryPage</name>
@@ -2001,7 +2118,7 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation>Pronto para Desinstalar</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>O assistente de configuração está pronto para remover %1 do seu computador.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;O diretório do programa %2 será excluído completamente&lt;/font&gt;, incluindo todo o conteúdo nesse diretório!</translation>
</message>
<message>
@@ -2013,7 +2130,7 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation>Pronto para Atualizar Pacotes</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>O assistente de configuração está pronto para começar a atualizar sua instalação.</translation>
</message>
<message>
@@ -2025,13 +2142,25 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation>Pronto para Instalar</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>O assistente de configuração está pronto para começar a instalar o %1 no seu computador.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::PerformInstallationPage</name>
@@ -2071,11 +2200,23 @@ Por favor, copie o instalador para uma unidade local</translation>
<source>Uninstalling</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Concluindo o Assistente %1</translation>
</message>
<message>
@@ -2083,7 +2224,7 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Clique em %1 para sair do Assistente %2.</translation>
</message>
<message>
@@ -2095,14 +2236,14 @@ Por favor, copie o instalador para uma unidade local</translation>
<translation>Executar %1 agora.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>O Assistente %1 falhou.</translation>
</message>
</context>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Concluindo o Assistente de Configuração %1</translation>
</message>
</context>
@@ -2153,7 +2294,10 @@ como um usuário com os privilégios apropriados e, em seguida, clicando em OK.<
Please start the setup program as a user with the appropriate rights,
or accept the elevation of access rights if being asked.</source>
- <translation type="unfinished"></translation>
+ <translation>Não é possível obter a autorização necessária para continuar a instalação.
+
+ Inicie o instalador como usuário com os privilégios apropriados,
+ ou aceite a elevação dos direitos acesso, se forem pedidos.</translation>
</message>
</context>
<context>
@@ -2167,11 +2311,11 @@ or accept the elevation of access rights if being asked.</source>
<name>QInstaller::ReplaceOperation</name>
<message>
<source>Current search argument calling &quot;%1&quot; with empty search argument is not supported.</source>
- <translation type="unfinished"></translation>
+ <translation>A pesquisa atual &quot;%1&quot; com argumento vazio não é suportado.</translation>
</message>
<message>
<source>Current mode argument calling &quot;%1&quot; with arguments &quot;%2&quot; is not supported. Please use string or regex.</source>
- <translation type="unfinished"></translation>
+ <translation>A pesquisa atual &quot;%1&quot; com os argumentos &quot;%2&quot; não são suportados. Use &quot;string&quot; ou &quot;regex&quot;.</translation>
</message>
<message>
<source>Cannot open file &quot;%1&quot; for reading: %2</source>
@@ -2223,8 +2367,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Não é possível abrir o arquivo de configurações %1 para leitura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation type="unfinished"></translation>
+ <source>Categories</source>
+ <translation>Categorias</translation>
</message>
</context>
<context>
@@ -2235,7 +2379,7 @@ or accept the elevation of access rights if being asked.</source>
</message>
<message>
<source>Current method argument calling &quot;%1&quot; with arguments &quot;%2&quot; is not supported. Please use set, remove, add_array_value, or remove_array_value.</source>
- <translation type="unfinished"></translation>
+ <translation>O método atual &quot;%1&quot; com argumentos &quot;%2&quot; não são suportados. Use &quot;set&quot;, &quot;remove&quot;, &quot;add_array_value&quot;, ou &quot;remove_array_value&quot;</translation>
</message>
</context>
<context>
@@ -2415,6 +2559,10 @@ or accept the elevation of access rights if being asked.</source>
<source>Try again</source>
<translation>Tente novamente</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -2686,10 +2834,6 @@ or accept the elevation of access rights if being asked.</source>
<translation>Não é possível ler &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Erro de análise em %1 em %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Elemento raiz %1 inesperado. Deveria ser &apos;Updates&apos;.</translation>
</message>
@@ -2718,22 +2862,22 @@ or accept the elevation of access rights if being asked.</source>
<name>InstallerBase</name>
<message>
<source>Unable to start installer</source>
- <translation type="unfinished"></translation>
+ <translation>Não é possível iniciar o instalador</translation>
</message>
</context>
<context>
<name>QInstaller::ComponentSelectionPagePrivate</name>
<message>
<source>Filter</source>
- <translation type="unfinished"></translation>
+ <translation>Filtro</translation>
</message>
<message>
<source>Error</source>
<translation>Erro</translation>
</message>
<message>
- <source>Component Information</source>
- <translation type="unfinished"></translation>
+ <source>Information</source>
+ <translation>Informação</translation>
</message>
</context>
<context>
@@ -2742,6 +2886,22 @@ or accept the elevation of access rights if being asked.</source>
<source>Extracting &quot;%1&quot;</source>
<translation>Extraindo &quot;%1&quot;</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>O arquivo comprimido &quot;%1&quot; não é suportado. Não existe um programa registado para este extensão de nome de arquivo.</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Não é possível abrir o arquivo &quot;%1&quot; para leitura: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>Ocorreu um erro ao ler o conteúdo do arquivo comprimido &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>A remover arquivos extraidos de &quot;%1&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2761,20 +2921,178 @@ or accept the elevation of access rights if being asked.</source>
<translation type="unfinished">Não é possível abrir o arquivo &quot;%1&quot; para leitura: %2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation type="unfinished">Erro ao extrair o arquivo &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
+ <source>Cannot open archive for reading: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation type="unfinished">Erro ao extrair o arquivo &quot;%1&quot;: %2</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation type="unfinished">Não é possível abrir o arquivo &quot;%1&quot; para gravação: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation type="unfinished">Não é possível abrir o arquivo &quot;%1&quot; para leitura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation type="unfinished"></translation>
</message>
</context>
</TS>
diff --git a/src/sdk/translations/ifw_pt.ts b/src/sdk/translations/ifw_pt_PT.ts
index 58d504ba3..3d4cf7c50 100644
--- a/src/sdk/translations/ifw_pt.ts
+++ b/src/sdk/translations/ifw_pt_PT.ts
@@ -212,6 +212,30 @@
<source>Deselect All</source>
<translation>Deselecionar Todos</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>Cache local</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>As informações meta de repositórios remotos são armazenadas em cache no disco para melhorar os tempos de carregamento. Você pode selecionar outro diretório para armazenar o cache ou limpar o conteúdo do cache atual.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>Caminho para o cache:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>Exclui o conteúdo do diretório de cache</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>Limpar cache</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>Limpando cache...</translation>
+ </message>
</context>
<context>
<name>QObject</name>
@@ -271,6 +295,14 @@
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation>Por favor verique que o utilizador atual tem permissões de leitura ao ficheiro &amp;quot;%1&amp;quot; ou execute %2 como administrador.</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>O valor de &apos;max-concurrent-operations&apos; não é válido.</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>Valor vazio para a opção &apos;cache-path&apos;.</translation>
+ </message>
</context>
<context>
<name>QInstaller</name>
@@ -292,7 +324,7 @@
</message>
<message>
<source>Copy failed: %1</source>
- <translation>Ocorreu um erro na cópia: %1</translation>
+ <translation>Ocorreu um erro a copiar: %1</translation>
</message>
<message>
<source>Write failed after %1 bytes: %2</source>
@@ -382,6 +414,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>O conteúdo em &quot;%1&quot; é inválido.</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>Esta situação pode ficar resolvida se reiniciar a aplicação após limpar a cache:</translation>
+ </message>
</context>
<context>
<name>BinaryLayout</name>
@@ -442,18 +478,6 @@
<translation>Em modo de atualização os componentes não podem descendentes.</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Ocorreu um erro a abrir o ficheiro da interface &quot;%1&quot;: %2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Ocorreu um erro a carregar o ficheiro da interface do utilizador &quot;%1&quot;:%2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Ocorreu um erro a abrir o ficheiro de licença &quot;%1&quot;: %2</translation>
- </message>
- <message>
<source>Error</source>
<translation>Erro</translation>
</message>
@@ -473,6 +497,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>Ocorreu um erro ao carregar o componente selecionado. Este componente não pode ser instalado.</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Ocorreu um erro a abrir o ficheiro da interface &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Ocorreu um erro a carregar o ficheiro da interface do utilizador &quot;%1&quot;:%2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Ocorreu um erro a abrir o ficheiro de licença &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -532,12 +580,8 @@
<translation>Não é possível guardar o retorno de &quot;%1&quot; numa chave de instalação vazia.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>O ficheiro &quot;%1&quot; não existe ou não é executável.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>Ocorreu uma saida abrupta durante a execução de &quot;%1&quot;.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation>Não é possível executar o comando: &quot;%1&quot;: %2</translation>
</message>
</context>
<context>
@@ -798,6 +842,28 @@ Erro ao carregar %2</translation>
<source>Total: </source>
<translation>Total:</translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>Excedeu o número máximo de tentativas (%1).</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>Falhou a verificação do código hash durante o download. Este é um erro temporário. Por favor, tente novamente.
+
+Previsto: %1
+Descarregado: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>Não é possível verificar o código hash
+Previsto: %1
+Descarregado: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -972,12 +1038,24 @@ Erro ao carregar %2</translation>
</message>
<message>
<source>Recursion detected, component &quot;%1&quot; already added with reason: &quot;%2&quot;</source>
- <translation>Foi detectado um ciclo recursivo, componente &quot;%1&quot; já foi adicionado com o motivo: &quot;%2&quot;</translation>
+ <translation>Foi detectado um ciclo recursivo, o componente &quot;%1&quot; já foi adicionado devido a: &quot;%2&quot;</translation>
</message>
<message>
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Não foi possível encontrar a dependência &quot;%1&quot; para &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>Foi detetado uma dependência impossível de resolver. A instalação do componente &quot;%1&quot; teria de ser desinstalado devido à dependência &quot;%2&quot; que se encontra sinalizada para desinstalar por: &quot;%3&quot;.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>Componentes selecionados pelo &apos;alias&apos; &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>Foi detectado um ciclo recursivo, o &apos;alias&apos; do componente &quot;%1&quot; já foi adicionado devido a: &quot;%2&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::InstallIconsOperation</name>
@@ -1137,10 +1215,6 @@ Erro ao carregar %2</translation>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Não é possível guardar o ficheiro de licença &quot;%1&quot;.</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>Não foi encontrado ficheiro de licença para ser excluído.</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1164,10 +1238,6 @@ Erro ao carregar %2</translation>
<translation>O motor principal do gestor de pacotes não está disponível.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>A preparar o descarregamento de metadados...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>A descompactar repositórios. Por favor espere...</translation>
</message>
@@ -1196,14 +1266,6 @@ Erro ao carregar %2</translation>
<translation>Incompatibilidade detectada na verificação para &quot;%1&quot;.</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>A obter metadados do repositório remoto...%1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>A obter metainformação do repositório remoto... </translation>
- </message>
- <message>
<source>Failure to fetch repositories.</source>
<translation>Ocorreu um erro ao obter repositórios.</translation>
</message>
@@ -1223,6 +1285,41 @@ Erro ao carregar %2</translation>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>O ficheiro &quot;%1&quot; não é suportado. Não está registado um programa para a extensão &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>Buscando informações de atualização mais recentes...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>Atualizando o cache local com %n novo item...</numerusform>
+ <numerusform>Atualizando o cache local com %s novos itens...</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>Limpar o diretório de cache e reiniciar o aplicativo pode resolver esta situação.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>Ocorreu um erro durante a actualização da cache.</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>Não foi possível abrir o ficheiro &quot;%1&quot; para leitura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Não foi possível abrir o ficheiro &quot;%1&quot; para escrita: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>A obter informações de repositórios remotos...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>A obter metainformação do repositório remoto...</translation>
+ </message>
</context>
<context>
<name>QInstaller::FileTaskObserver</name>
@@ -1282,10 +1379,8 @@ Erro ao carregar %2</translation>
<translation>Erro ao guardar a Ferramenta de Manutenção</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-A descarregar pacotes...</translation>
+ <source>Downloading packages...</source>
+ <translation>A descarregar pacotes...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1394,42 +1489,24 @@ De certeza que deseja continuar?</translation>
<translation>Não é possível obter todas as dependências.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Componentes a serem removidos.</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>Não é possível instalar o componente %1. O componente é instalado apenas como dependência automática de %2.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>Não é possível instalar o componente %1. O componente é instalado apenas como dependência automática de %2.
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>Não é possível instalar o componente %1. O componente não é verificável, o que significa que um dos subcomponentes deve ser selecionado.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>Não é possível instalar o componente %1. O componente não é verificável, o que significa que um dos subcomponentes deve ser selecionado.
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>Componente %1 já foi instalado</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>Componente %1 já foi instalado
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>Não é possível instalar %1. É um componente virtual.</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>Não é possível instalar %1. É um componente virtual.
-</translation>
- </message>
- <message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>Não é possível instalar %1. O componente não foi encontrado.
-</translation>
- </message>
- <message>
- <source>Running processes found.</source>
- <translation>Foram encontrados processos em execução.</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>Não é possível instalar %1. O componente não foi encontrado.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1452,10 +1529,6 @@ De certeza que deseja continuar?</translation>
<translation>Não há espaço em disco suficiente para armazenar todos os componentes selecionados! Estão disponíveis %1, mas é necessário no mínimo %2.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>Não há espaço em disco suficiente para armazenar ficheiros temporários! Estão disponíveis %1, mas é necessário no mínimo %2.</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>O volume que selecionou para instalação tem espaço suficiente para instalação, mas posteriormente terá menos de 1% do espaço disponível.</translation>
</message>
@@ -1476,19 +1549,41 @@ De certeza que deseja continuar?</translation>
<translation>É necessário a interação com o utilizador, mas o dispositivo de saída não está associado a uma linha de comandos.</translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation>Não é possível registar o componente! Já existe um componente com o identificador %1.</translation>
- </message>
- <message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>Não é possível instalar %1. O componente é descendente de um componente virtual %2.
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>Não é possível instalar %1. O componente é descendente de um componente virtual %2.</translation>
</message>
<message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation>O tamanho estimado do instalador é %1 e irá exceder o limite de tamanho executável de %2. A aplicação pode não iniciar. </translation>
</message>
+ <message>
+ <source>Components about to be removed:</source>
+ <translation>Os seguintes componentes irão ser removidos:</translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>Não é possível instalar o componente %1. Ocorreu um problema ao carregar o componente, este foi sinalizado como instável e não pode ser selecionado.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>Não há espaço em disco suficiente para armazenar arquivos temporários! %1 estão disponíveis, enquanto o mínimo necessário é %2. Você pode selecionar outro local para os arquivos temporários modificando o caminho do cache local nas configurações do instalador.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>Não foi possível resolver os componentes a serem desinstalados</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>O &apos;alias&apos; %1 não pode ser selecionado. Ocorreu um problema a carregar este &apos;alias&apos;, foi marcado com instável e não pode ser selecionado.</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>O &apos;alias&apos; %1 não pode ser selecionado. Este &apos;alias&apos; está categorizado como virtual, o que significa que não pode ser selecionado manualmente.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>O instalador criado irá utilizar %1 de espaco de disco.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1605,16 +1700,12 @@ De certeza que deseja continuar?</translation>
<translation>A criar Ferramenta de Manutenção</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Instalação concluída!</translation>
+ <source>Installation finished!</source>
+ <translation>Instalação concluída!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Instalação cancelada!</translation>
+ <source>Installation aborted!</source>
+ <translation>Instalação cancelada!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1625,16 +1716,12 @@ Instalação cancelada!</translation>
<translation>A remover componentes deselecionados...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Atualização concluída!</translation>
+ <source>Update finished!</source>
+ <translation>Atualização concluída!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Atualização cancelada!</translation>
+ <source>Update aborted!</source>
+ <translation>Atualização cancelada!</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1670,17 +1757,15 @@ Atualização cancelada!</translation>
</message>
<message>
<source>Offline generation completed successfully.</source>
- <translation>A geração offline foi concluída com sucesso.</translation>
+ <translation>A geração &apos;offline&apos; foi concluída com sucesso.</translation>
</message>
<message>
<source>Offline generation aborted!</source>
- <translation>A geração offline foi cancelada!</translation>
+ <translation>A geração &apos;offline&apos; foi cancelada!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-A instalar o componente %1</translation>
+ <source>Installing component %1</source>
+ <translation>A instalar o componente %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -1727,10 +1812,6 @@ A instalar o componente %1</translation>
<translation>Não é possível recuperar metadados: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Não é possível adicionar informações de fontes de atualização temporária.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Não é possível encontrar informações de fonte de atualização.</translation>
</message>
@@ -1738,6 +1819,50 @@ A instalar o componente %1</translation>
<source>Dependency cycle between components &quot;%1&quot; and &quot;%2&quot; detected.</source>
<translation>Ciclo de inter-dependência entre os componentes &quot;%1&quot; e &quot;%2&quot; detectados.</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation>A preparar para descompactar componentes...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation>Concluídas %1 de %2 operações.</translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation>A descompactar componentes...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation>Revertidos %1 de %2 operações.</translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation>As operações foram revertidas.</translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation>Instalados %1 de %2 componentes.</translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation>Todos os componentes foram instalados.</translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>Carregando scripts de componentes...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>O &apos;alias&apos; declara um nome em conflito com um componente pré-existente &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>&apos;Alias&apos; dos componentes não resolvidos.</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>Foi detetado um ciclo de dependência entre os alias &quot;%1&quot; e &quot;%2&quot;.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -1795,11 +1920,11 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>%1 - Configuração</translation>
+ <source>Welcome</source>
+ <translation>Bem-vindo</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Bem-vindo ao Assistente de Configuração %1.</translation>
</message>
<message>
@@ -1827,10 +1952,6 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Nenhuma atualização disponível.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Apenas gestãoo de pacotes locais disponível.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>Sair</translation>
</message>
@@ -1870,62 +1991,38 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>Def&amp;eito</translation>
+ <source>Default</source>
+ <translation>Defeito</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Selecione os componentes por defeito na vista de árvore.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>&amp;Reverter</translation>
+ <source>Reset</source>
+ <translation>Reverter</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Reverter todos os componentes para o seu estado original na visualização em árvore.</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>&amp;Selecionar Todos</translation>
+ <source>Select All</source>
+ <translation>Selecionar Todos</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Selecione todos os componentes na visualização em árvore.</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Desmarcar Todos</translation>
+ <source>Deselect All</source>
+ <translation>Desmarcar Todos</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>Desselecione todos os componentes na visualização em árvore.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Procurar ficheiros QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Selecione ficheiro &quot;Qt Board Support Package&quot; para instalar conteúdo adicional que não está disponível nos repositórios online.</translation>
</message>
@@ -1958,8 +2055,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>
@@ -1969,6 +2066,26 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<source>Search</source>
<translation>Pesquisa</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>Selecionar ficheiros do tipo &amp;QBSP</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>Selecione</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Erro</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Criar um Instalador &apos;Offline&apos;</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>Invés de instalar, criar um instalador &apos;Offline&apos; a partir dos componentes selecionados.</translation>
+ </message>
</context>
<context>
<name>QInstaller::TargetDirectoryPage</name>
@@ -2020,7 +2137,7 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Pronto para Desinstalar</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>O assistente de configuração está pronto para remover %1 do seu computador.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;O diretório do programa %2 será excluído completamente&lt;/font&gt;, incluindo todo o conteúdo nesse diretório!</translation>
</message>
<message>
@@ -2032,7 +2149,7 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Pronto para Atualizar Pacotes</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>O assistente de configuração está pronto para iniciar a atualização da sua instalação.</translation>
</message>
<message>
@@ -2044,13 +2161,25 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Pronto para Instalar</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>O assistente de configuração está pronto para iniciar a instalação de %1.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Pronto para atualizar</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Criar um Instalador &apos;Offline&apos;</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>Pronto para criar um Instalador &apos;Offline&apos;</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>O assistente de configuração está pronto para criar um instalador offline para os componentes selecionados.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PerformInstallationPage</name>
@@ -2084,17 +2213,29 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
</message>
<message>
<source>Updating</source>
- <translation>>A atualizar</translation>
+ <translation>&gt;A atualizar</translation>
</message>
<message>
<source>Uninstalling</source>
<translation>A desinstalar</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>Criar um Instalador &apos;Offline&apos;</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>A criar um Instalador &apos;Offline&apos; para %1</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>A criar um Instalador &apos;Offline&apos;</translation>
+ </message>
</context>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>A concluir o Assistente %1</translation>
</message>
<message>
@@ -2102,7 +2243,7 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Concluído</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Clique em %1 para fechar o Assistente %2.</translation>
</message>
<message>
@@ -2114,14 +2255,14 @@ Por favor, copie o instalador para uma unidade de disco local</translation>
<translation>Executar %1 agora.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Ocorreu um erro no assistente %1.</translation>
</message>
</context>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>A concluir o Assistente de Configuração %1</translation>
</message>
</context>
@@ -2245,8 +2386,8 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<translation>Não é possível abrir o ficheiro de configurações %1 para leitura: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Selecionar categorias</translation>
+ <source>Categories</source>
+ <translation>Categorias</translation>
</message>
</context>
<context>
@@ -2437,6 +2578,10 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<source>Try again</source>
<translation>Tente novamente</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>Não é possível fazer o descarregamento de %1. Não é possível criar o diretório &quot;%2&quot;</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -2708,10 +2853,6 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<translation>Não é possível ler &quot;%1&quot;</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Ocorreu um erro a análisar %1 em %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Não é esperado este elemento raiz %1. Deveria ser &apos;Updates&apos;.</translation>
</message>
@@ -2754,8 +2895,8 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<translation>Erro</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Informação do componente</translation>
+ <source>Information</source>
+ <translation>Informação</translation>
</message>
</context>
<context>
@@ -2764,6 +2905,22 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<source>Extracting &quot;%1&quot;</source>
<translation>A descomprimir &quot;%1&quot;</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>O ficheiro &quot;%1&quot; não é suportado. Não está registado um programa para a extensão &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Não é possível abrir o ficheiro comprimido &quot;%1&quot; para leitura: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>Ocorreu um erro a obter o conteudo do ficheiro comprimido &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>A remover ficheiros descomprimidos de &quot;%1&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2783,20 +2940,179 @@ Em alternativa, pode aceitar a alteração de permissões de acesso caso seja so
<translation>Não é possível abrir o ficheiro comprimido &quot;%1&quot; para leitura: %2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
- <translation>Ocorreu um erro a obter o conteudo do ficheiro comprimido &quot;%1&quot;: %2</translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Ocorreu um erro ao descomprimir o ficheiro comprimido &quot;%1&quot;: %2</translation>
</message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation>Ocorreu um erro ao preparar para o ficheiro &quot;%1&quot;</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>Não é possível abrir o ficheiro comprimido &quot;%1&quot; para leitura.</translation>
</message>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation>A descompressão do ficheiro comprimido &quot;%1&quot; foi cancelada</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>Não é possível abrir o cabeçalho de entrada &quot;%1&quot; para leitura.</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Ocorreu um erro ao descomprimir o ficheiro comprimido &quot;%1&quot;: %2</translation>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>Ocorreu um erro a escrever entrada &quot;%1&quot; para disco: %2.</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>Não é possível abrir o ficheiro comprimido &quot;%1&quot; para leitura</translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation>Não é possível abrir o cabeçalho de entrada &quot;%1&quot; para leitura.</translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>Ocorreu um erro a escrever entrada &quot;%1&quot; para disco: %2.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Não é possível abrir o ficheiro &quot;%1&quot; para escrita: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Não é possível abrir o ficheiro &quot;%1&quot; para leitura: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation>Ocorreu um erro a escrever o cabeçalho de entrada &quot;%1&quot;: %2.</translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation>Componentes desselecionados:</translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation>Componentes substituídos por &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation>Irão ser removidos os seguintes componentes virtuais sem dependências:</translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation>As dependências de &quot;%1&quot; foram removidas:</translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation>As autodependências de &quot;%1&quot; foram removidas:</translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation>Acerca do instalador %1 </translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation>Acerca da ferramenta de manutenção %1 </translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>Não é possível inicializar o cache com caminho vazio.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>Não é possível criar o diretório &quot;%1&quot; para cache.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>Não é possível inicializar o cache: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>Não é possível limpar o cache invalidado.</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>Não é possível remover o arquivo de manifesto: % 1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>Erro ao limpar o cache: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>Não é possível recuperar itens do cache invalidado.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>Não é possível recuperar o item do cache invalidado.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>Não é possível registrar o item no cache invalidado.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>Não é possível registrar item nulo.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>Não é possível registar um item inválido com a soma de verificação % 1</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>Não é possível registrar o item com a soma de verificação %1. Já existe um item com a mesma soma de verificação no cache.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>Erro ao copiar o item para o caminho &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>Não é possível remover o item do cache invalidado.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>Não é possível remover o item especificado pela soma de verificação %1: esse item não existe.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>Erro ao remover o diretório &quot;%1&quot;: %2
+</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>Erro ao invalidar o cache: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>Não é possível abrir o arquivo de manifesto: % 1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>Não é possível gravar o conteúdo do arquivo de manifesto: % 1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>Não é possível sincronizar o cache invalidado.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>Foi selecionado um modo de registo desconhecido.</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>Cache limpo com sucesso!</translation>
</message>
</context>
</TS>
diff --git a/src/sdk/translations/ifw_ru.ts b/src/sdk/translations/ifw_ru.ts
index 478f5bc02..830c2bc82 100644
--- a/src/sdk/translations/ifw_ru.ts
+++ b/src/sdk/translations/ifw_ru.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>Не удалось найти компонент «%1», необходимый для «%2».</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>Выявлено невозможное разрешение зависимостей. Принудительная установка компонента &quot;%1&quot; будет удалена, потому что зависимый &quot;%2&quot; помечен для удаления по причине: &quot;%3&quot;.</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>Выбран компонент под именем &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>Замечено повторение, компонент с именем &quot;%1&quot; уже выбран.</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -310,6 +322,10 @@
<source>Cannot download %1. Cannot create file &quot;%2&quot;: %3</source>
<translation>Невозможно загрузить «%1». Не удалось создать «%2»: %3</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>Невозможно загрузить %1. Невозможно создать каталог для &quot;%2&quot;</translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -494,10 +510,6 @@
<translation>Невозможно прочитать «%1»</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>Ошибка разбора XML в %1 в %2, %3: %4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>Непредвиденный корневой элемент %1, требуется «Updates».</translation>
</message>
@@ -749,6 +761,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>Недопустимые данные в «%1».</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>Это может быть решено перезапуском приложения после очистки кэша:</translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -769,24 +785,36 @@
<translation>Информация об обновлении: </translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>Не удалось открыть запрошенный UI файл «%1»: %2</translation>
+ <source>Error: Operation %1 does not exist.</source>
+ <translation>Ошибка: операция %1 не существует.</translation>
</message>
<message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>Не удалось загрузить запрошенный UI файл «%1»: %2</translation>
+ <source>There was an error loading the selected component. This component cannot be installed.</source>
+ <translation>Возникла ошибка при загрузке выбранного компонента. Установить его не получится.</translation>
</message>
<message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>Не удалось открыть запрошенный файл лицензии «%1»: %2</translation>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Не удалось открыть запрошенный UI файл «%1»: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
<message>
- <source>Error: Operation %1 does not exist.</source>
- <translation>Ошибка: операция %1 не существует.</translation>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Не удалось загрузить запрошенный UI файл «%1»: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
<message>
- <source>There was an error loading the selected component. This component cannot be installed.</source>
- <translation>Возникла ошибка при загрузке выбранного компонента. Установить его не получится.</translation>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>Не удалось открыть запрошенный файл лицензии «%1»: %2.
+
+%3 &quot;%4&quot;</translation>
</message>
</context>
<context>
@@ -835,42 +863,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Def&amp;ault</source>
- <translation>&amp;По умолчанию</translation>
+ <source>Default</source>
+ <translation>По умолчанию</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>Выберите компоненты по умолчанию в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Reset</source>
- <translation>&amp;Отменить</translation>
+ <source>Reset</source>
+ <translation>Отменить</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>Сбросьте все компоненты в исходное состояние выбора в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Select All</source>
- <translation>&amp;Выбрать всё</translation>
+ <source>Select All</source>
+ <translation>Выбрать всё</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>Выберите все компоненты в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Deselect All</source>
- <translation>&amp;Отменить выделенное</translation>
+ <source>Deselect All</source>
+ <translation>Отменить выделенное</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>Отмените выбор всех компонентов в древовидном представлении.</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>&amp;Обзор файлов QBSP</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>Выберите файл инструментальных средств для разработки Qt Board Support Package, чтобы установить дополнительное содержимое, которое недоступно непосредственно из сетевых хранилищ.</translation>
</message>
@@ -895,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>
@@ -907,32 +931,32 @@
<translation>Открытие файла</translation>
</message>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
+ <source>Filter the enabled repository categories</source>
+ <translation>Отфильтруйте категории включенного хранилища по выбору.</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
+ <source>Search</source>
+ <translation>Поиск</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
+ <source>Browse &amp;QBSP files</source>
+ <translation>Посмотреть &amp;QBSP файлы</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
+ <source>Select</source>
+ <translation>Выбрать</translation>
</message>
<message>
- <source>Filter the enabled repository categories</source>
- <translation>Отфильтруйте категории включенного хранилища по выбору.</translation>
+ <source>Error</source>
+ <translation>Ошибка</translation>
</message>
<message>
- <source>Search</source>
- <translation>Поиск</translation>
+ <source>Create Offline Installer</source>
+ <translation>Создать Автономный Установщик</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>Создать автономный установщик из выбранных компонентов вместо установки сейчас.</translation>
</message>
</context>
<context>
@@ -946,8 +970,8 @@
<translation>Ошибка</translation>
</message>
<message>
- <source>Component Information</source>
- <translation>Сведения о компонентах</translation>
+ <source>Information</source>
+ <translation>Информация</translation>
</message>
</context>
<context>
@@ -965,12 +989,8 @@
<translation>Невозможно сохранить вывод «%1» в пустое значение ключа установщика.</translation>
</message>
<message>
- <source>File &quot;%1&quot; does not exist or is not an executable binary.</source>
- <translation>Файл «%1» не существует или не является исполняемым.</translation>
- </message>
- <message>
- <source>Running &quot;%1&quot; resulted in a crash.</source>
- <translation>Запуск «%1» завершился крахом.</translation>
+ <source>Failed to run command: &quot;%1&quot;: %2</source>
+ <translation>Не удалось запустить команду: &quot;%1&quot;: %2</translation>
</message>
</context>
<context>
@@ -1150,14 +1170,6 @@
<translation>Ошибка загрузки</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>Не удалось проверить целостность хеша в процессе загрузки. Пожалуйста, повторите операцию.</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>Невозможно проверить хеш</translation>
- </message>
- <message>
<source>Cannot fetch archives: %1
Error while loading %2</source>
<translation>Невозможно получить архивы :%1
@@ -1235,6 +1247,28 @@ Error while loading %2</source>
<source>Total: </source>
<translation>Всего: </translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>Превышено количество (%1) повторов</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>Не удалось проверить целостность хеша в процессе загрузки. Пожалуйста, повторите операцию.
+
+ожидалось: %1
+загружено: %2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>Невозможно проверить хеш
+ожидалось: %1
+загружено: %2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1309,6 +1343,22 @@ Error while loading %2</source>
<source>Extracting &quot;%1&quot;</source>
<translation>Извлечение «%1»</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>Неподдерживаемый архив &quot;%1&quot;: нет зарегестрированного обработчика для файла с расширением &quot;%2&quot;.</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>Не удалось открыть архив «%1» для чтения: %2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>Ошибка при чтении содержимого архива &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>Удаление файлов, извлеченных из &quot;%1&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::FakeStopProcessForUpdateOperation</name>
@@ -1383,7 +1433,7 @@ Error while loading %2</source>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Завершение установки %1</translation>
</message>
<message>
@@ -1399,11 +1449,11 @@ Error while loading %2</source>
<translation>Запустить %1 сейчас.</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>Установка %1 не удалась.</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>Нажмите «%1» для выхода из мастера %2.</translation>
</message>
</context>
@@ -1452,11 +1502,11 @@ Error while loading %2</source>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>Установка - %1</translation>
+ <source>Welcome</source>
+ <translation>Добро пожаловать</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>Добро пожаловать в мастер установки %1.</translation>
</message>
<message>
@@ -1484,10 +1534,6 @@ Error while loading %2</source>
<translation>Нет доступных обновлений.</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation> Доступно только локальное управление пакетами.</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>&amp;Выйти</translation>
</message>
@@ -1535,10 +1581,6 @@ Error while loading %2</source>
<translation>В операции «%1» необходимый объект установщика пуст.</translation>
</message>
<message>
- <source>No license files found to delete.</source>
- <translation>Невозможно удалить файл лицензии: файл не найден.</translation>
- </message>
- <message>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>Не удалось записать файл лицензии «%1».</translation>
</message>
@@ -1565,10 +1607,6 @@ Error while loading %2</source>
<translation>Отсутствует менеджер пакетов.</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>Подготовка к загрузке метаданных...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>Распаковка сжатых хранилищ. Это может занять некоторое время...</translation>
</message>
@@ -1589,14 +1627,6 @@ Error while loading %2</source>
<translation>Обнаружено несовпадение контрольной суммы у «%1».</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>Получение метаданных из внешнего хранилища... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>Получение метаданных из внешнего хранилища... </translation>
- </message>
- <message>
<source>Failure to fetch repositories.</source>
<translation>Не удалось загрузить хранилища.</translation>
</message>
@@ -1624,6 +1654,42 @@ Error while loading %2</source>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>Неподдерживаемый архив &quot;%1&quot;: нет зарегестрированного обработчика для файла с расширением &quot;%2&quot;.</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>Получение информации о последнем обновлении</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>Обновление локального кэша с добавлением %n нового файла</numerusform>
+ <numerusform>Обновление локального кэша с добавлением %n новых файлов</numerusform>
+ <numerusform>Обновление локального кэша с добавлением %n новых файлов</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>Очистка кэш-директории и перезапуск приложения может исправить это.</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>Неизвестное исключение во время обновления кэша.</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>Невозможно открыть распакованный файл &quot;%1&quot; для чтения.</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Невозможно открыть файл &quot;%1&quot; для записи: %2</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>Получение информации из внешнего хранилища...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>Получение метаданных из внешнего хранилища...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1632,10 +1698,8 @@ Error while loading %2</source>
<translation>Ошибка записи Maintenance Tool</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-Загрузка пакетов...</translation>
+ <source>Downloading packages...</source>
+ <translation>Загрузка пакетов...</translation>
</message>
<message>
<source>All downloads finished.</source>
@@ -1720,32 +1784,20 @@ Do you want to continue?</source>
<translation>Доступно важное исправление, сначала запустите программу обновления.</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>Не удалось установить компонент %1. Компонент устанавливается только как автоматическая зависимость для %2.
-</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>Не удалось установить компонент %1. Компонент устанавливается только как автоматическая зависимость для %2.</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>Компонент %1 уже установлен
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>Компонент %1 уже установлен</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>Не удалось установить %1. Компонент является виртуальным.
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>Не удалось установить %1. Компонент является виртуальным.</translation>
</message>
<message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>Не удалось установить %1. Компонент не найден.
-</translation>
- </message>
- <message>
- <source>Running processes found.</source>
- <translation>Обнаружены запущенные процессы.</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>Не удалось установить %1. Компонент не найден.</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1792,29 +1844,19 @@ Do you want to continue?</source>
<translation>Не удалось разрешить все зависимости.</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>Удаляемые компоненты.</translation>
- </message>
- <message>
<source>User input is required but the output device is not associated with a terminal.</source>
<translation>Требуется ввод данных пользователем, но устройство вывода не связано с терминалом.</translation>
</message>
<message>
- <source>Cannot register component! Component with identifier %1 already exists.</source>
- <translation>Не удалось зарегистрировать компонент! Компонент с идентификатором %1 уже существует.</translation>
- </message>
- <message>
<source>Canceling the Installer</source>
<translation>Отмена программы установки</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
<translation>Не удалось установить компонент %1. Компонент не является проверяемым, и это означает, что необходимо выбрать один из подкомпонентов.</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
<translation>Не удалось установить %1. Компонент является производным от виртуального компонента %2.</translation>
</message>
<message>
@@ -1826,10 +1868,6 @@ Do you want to continue?</source>
<translation>Недостаточно места на диске для сохранения всех выбранных компонентов. Доступно %1, а требуется минимум: %2.</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>Недостаточно места на диске для временных файлов. Доступно %1, а требуется минимум %2.</translation>
- </message>
- <message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation>Приблизительный размер установочника %1 превысит поддерживаемый предел размера исполняемого файла %2. Возможно, приложение не сможет быть запущено. </translation>
</message>
@@ -1837,6 +1875,34 @@ Do you want to continue?</source>
<source>Invalid</source>
<translation>недопустимая версия</translation>
</message>
+ <message>
+ <source>Components about to be removed:</source>
+ <translation>Компонент практически удален:</translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>Невозможно установить компонент %1. Произошла ошибка загрузки этого компонента, он был помечен нестабильным и не может быть выбран.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>Недостаточно места на диске для хранения временных файлов! %1 доступно, минимально необходимо %2. Вы можете выбрать другое место для временных файлов, изменив путь к локальному кэшу в настройках установщика.</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>Невозможно определить компоненты для удаления.</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>Невозможно выбрать псевдоним %1. Была проблема с загрузкой этого псевдонима, он был помечен нестабильным и не может быть выбран.</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>Невозможно выбрать %1. Псевдоним помечен как виртуальный и не может быть выбран вручную.</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>Созданный установщик займет %1 дискового пространства.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1889,16 +1955,12 @@ Do you want to continue?</source>
<translation>Создаётся локальное хранилище</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-Установка завершена!</translation>
+ <source>Installation finished!</source>
+ <translation>Установка завершена!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-Установка прервана!</translation>
+ <source>Installation aborted!</source>
+ <translation>Установка прервана!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1909,16 +1971,12 @@ Installation aborted!</source>
<translation>Удаление компонентов...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-Обновление завершено!</translation>
+ <source>Update finished!</source>
+ <translation>Обновление завершено!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-Обновление прервано!</translation>
+ <source>Update aborted!</source>
+ <translation>Обновление прервано!</translation>
</message>
<message>
<source>Unresolved dependencies</source>
@@ -2009,10 +2067,8 @@ Update aborted!</source>
<translation>Создание автономного экземпляра прервано!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-Установка компонента %1</translation>
+ <source>Installing component %1</source>
+ <translation>Установка компонента %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2036,10 +2092,6 @@ Installing component %1</source>
<translation>Невозможно загрузить метаданные: %1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>Невозможно добавить информацию о временном сервере обновления.</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>Невозможно найти информацию об источниках обновления.</translation>
</message>
@@ -2085,6 +2137,50 @@ Installing component %1</source>
<translation>Ошибка в процессе установки:
%1</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation>Подготовка к распаковке компонента...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation>1 из %2 операции завершены</translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation>Распаковка компонентов...</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation>%1 из %2 были возвращены в предыдущее состояние.</translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation>Возврат в предыдущее состояние завершен.</translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation>%1 из %2 компонентов установлены</translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation>Все компоненты установлены.</translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>Загрузка скриптов компонента...</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>Псевдоним назван именем, которое конфликтует с существующим компонентом &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>Неразрешенные псевдонимы компонентов.</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>Цикличная зависимость между псевдонимами &quot;%1&quot; и &quot;%2&quot; замечена.</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2188,6 +2284,18 @@ Please copy the installer to a local drive</source>
<source>Uninstalling</source>
<translation>Удаление</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>Создать Автономный Установщик.</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>Создание Автономного Установщика для %1</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>Создание Автономного Установщика</translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2231,7 +2339,7 @@ Please copy the installer to a local drive</source>
<translation>Всё готово к удалению</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>Программа установки готова начать удаление %1 с вашего компьютера. &lt;br&gt;&lt;font color=&quot;red&quot;&gt;Директория с программой %2 будет полностью удалена&lt;/font&gt;, включая содержимое этой директории!</translation>
</message>
<message>
@@ -2243,7 +2351,7 @@ Please copy the installer to a local drive</source>
<translation>Готов к обновлению пакетов</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>Программа установки готова к обновлению файлов.</translation>
</message>
<message>
@@ -2255,13 +2363,25 @@ Please copy the installer to a local drive</source>
<translation>Всё готово к установке</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>Программа установки готова начать установку %1 на ваш компьютер.</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>Всё готово к обновлению</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>Создать Автономный Установщик.</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>Готов к установке Автономного Установщика.</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>Вся необходимая информация доступна для создания автономного установщика для выбранных компонентов.</translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2318,7 +2438,7 @@ Please copy the installer to a local drive</source>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>Завершение %1 мастера установки</translation>
</message>
</context>
@@ -2541,6 +2661,14 @@ Please copy the installer to a local drive</source>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation>Убедитесь, что текущий пользователь имеет разрешение на чтение файла «%1», или попробуйте запустить %2 от имени администратора.</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>Неверное значение for &apos;max-concurrent-operations&apos;.</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>Пустое значение параметра &apos;cache-path&apos;.</translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2587,8 +2715,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>Невозможно открыть файл настроек %1 на чтение: %2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>Выберите категории пакетов</translation>
+ <source>Categories</source>
+ <translation>Выберите категорию</translation>
</message>
</context>
<context>
@@ -2725,6 +2853,30 @@ or accept the elevation of access rights if being asked.</source>
<source>The server&apos;s URL that contains a valid repository.</source>
<translation>Адреса серверов, которые содержат рабочие хранилища.</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>Локальный кэш</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>Мета информация с удаленных репозиториев кэшируется на диске для ускорения загрузочного процесса. Вы можете выбрать другую папку для хранения кэша или удалить содержимое существующего кэша.</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>Путь для кэша:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>Удаление содержимого существующей кэш-директории.</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>Очистить кэш</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>Очистка кэша...</translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2795,20 +2947,178 @@ or accept the elevation of access rights if being asked.</source>
<translation>Не удалось открыть архив «%1» для чтения: %2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
- <translation>Ошибка при чтении содержимого архива &quot;%1&quot;: %2</translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>Ошибка извлечения из архива «%1»: %2</translation>
+ </message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
+ <message>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>Не удалось открыть архив для чтения: %1</translation>
</message>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation>Невозможно подготовить файл &quot;%1&quot;</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>Не удалось прочесть заголовок записи: %1 </translation>
</message>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation>Распаковка для архива &quot;%1&quot; отменена.</translation>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>Не удалось записать &quot;%1&quot; на диск: %2</translation>
</message>
+</context>
+<context>
+ <name>QInstaller::LibArchiveArchive</name>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>Ошибка извлечения из архива «%1»: %2</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>Не удалось открыть архив для чтения: %1</translation>
+ </message>
+ <message>
+ <source>Cannot read entry header: %1</source>
+ <translation>Не удалось прочесть заголовок записи: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>Не удалось записать &quot;%1&quot; на диск: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>Не удалось открыть файл &quot;%1&quot; для записи: %2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>Не удалось открыть файл «%1» для чтения: %2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation>Не удалось записать заголовок записи для &quot;%1&quot;: %2</translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation>Выбранные компоненты:</translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation>Компоненты заменены на &quot;%1&quot;:</translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation>Удаление виртуальных компонентов без существующих зависимостей:</translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation>Компоненты зависимостей &quot;%1&quot; удалены:</translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation>Компоненты автозависимостей &quot;%1&quot; удалены:</translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation>Об %1 установщике</translation>
+ </message>
+ <message>
+ <source>About %1 Maintenance Tool</source>
+ <translation>Об %1 Maintenance Tool</translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>Невозможно инициализировать кэш с пустым путем.</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>Невозможно создать директорию &quot;%1&quot; для кэша.</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>Невозможно инициализировать кэш: %1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>Невозможно очистить недействительный кэш. </translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>Невозможно удалить файл-манифест: %1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>Ошибка во время очистки кэша: %1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>Невозможно получить файлы из недействительного кэша.</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>Невозможно получить файл из недействительного кэша.</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>Невозможно зарегестрировать файл для недействительного кэша.</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>Невозможно зарегестрировать недействительный файл.</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>Невозможно зарегестрировать файл с контрольной суммой %1. Файл с идентичной контрольной суммой уже существует в кэше.</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>Ошибка при копировании файла в &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>Невозможно удалить файл из недействительного кэша.</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>Невозможно удалить файл с контрольной суммой %1: файла не существует.</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>Ошибка при удалении директории &quot;%1&quot;: %2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>Ошибка при аннулировании кэша: %1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>Невозможно открыть файл-манифест: %1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>Невозможно записать содержимое для файл-манифеста: %1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>Не удается синхронизировать недействительный кеш.</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>Выбран неизвестный режим регистра.</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>Кэш успешно очищен!</translation>
</message>
</context>
</TS>
diff --git a/src/sdk/translations/ifw_zh_CN.ts b/src/sdk/translations/ifw_zh_CN.ts
index d1471090d..7246914a1 100644
--- a/src/sdk/translations/ifw_zh_CN.ts
+++ b/src/sdk/translations/ifw_zh_CN.ts
@@ -136,6 +136,18 @@
<source>Cannot find missing dependency &quot;%1&quot; for &quot;%2&quot;.</source>
<translation>找不到“%2”的缺失依赖项“%1”。</translation>
</message>
+ <message>
+ <source>Impossible dependency resolution detected. Forced install component &quot;%1&quot; would be uninstalled because its dependency &quot;%2&quot; is marked for uninstallation with reason: &quot;%3&quot;.</source>
+ <translation>检测到无法解决的依赖关系。强制安装组件:“%1”将会被移除,因为它的依赖“%2”被标记为移除,原因:“%3”。</translation>
+ </message>
+ <message>
+ <source>Components selected by alias &quot;%1&quot;:</source>
+ <translation>通过别名“%1”选中的组件:</translation>
+ </message>
+ <message>
+ <source>Recursion detected, component alias &quot;%1&quot; already added.</source>
+ <translation>检测到递归,组件别名“%1”已添加。</translation>
+ </message>
</context>
<context>
<name>Job</name>
@@ -302,6 +314,10 @@
<source>Try again</source>
<translation>重试</translation>
</message>
+ <message>
+ <source>Cannot download %1. Cannot create directory for &quot;%2&quot;</source>
+ <translation>无法下载%1。无法为“%2”创建目录。</translation>
+ </message>
</context>
<context>
<name>KDUpdater::LocalFileDownloader</name>
@@ -484,10 +500,6 @@
<translation>无法读取“%1”</translation>
</message>
<message>
- <source>Parse error in %1 at %2, %3: %4</source>
- <translation>%1 中 %2、%3 的解析错误:%4</translation>
- </message>
- <message>
<source>Root element %1 unexpected, should be &quot;Updates&quot;.</source>
<translation>意外的根元素 %1,应为“Updates”。</translation>
</message>
@@ -739,6 +751,10 @@
<source>Invalid content in &quot;%1&quot;.</source>
<translation>“%1”中的内容无效。</translation>
</message>
+ <message>
+ <source>This may be solved by restarting the application after clearing the cache from:</source>
+ <translation>清空此处缓存之后重启应用程序也许可以解决问题:</translation>
+ </message>
</context>
<context>
<name>QInstaller::Component</name>
@@ -747,18 +763,6 @@
<translation>更新程序模式下组件不得包含子项。</translation>
</message>
<message>
- <source>Cannot open the requested UI file &quot;%1&quot;: %2</source>
- <translation>无法打开请求的 UI 文件“%1”:%2</translation>
- </message>
- <message>
- <source>Cannot load the requested UI file &quot;%1&quot;: %2</source>
- <translation>无法加载请求的 UI 文件“%1”:%2</translation>
- </message>
- <message>
- <source>Cannot open the requested license file &quot;%1&quot;: %2</source>
- <translation>无法打开请求的许可文件“%1”:%2</translation>
- </message>
- <message>
<source>Error</source>
<translation>错误</translation>
</message>
@@ -778,6 +782,30 @@
<source>There was an error loading the selected component. This component cannot be installed.</source>
<translation>加载选中组件的过程中出现错误。这个组件不会被安装。</translation>
</message>
+ <message>
+ <source>Cannot open the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>无法打开请求的 UI 文件“%1”:%2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot load the requested UI file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>无法加载请求的 UI 文件“%1”:%2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
+ <message>
+ <source>Cannot open the requested license file &quot;%1&quot;: %2.
+
+%3 &quot;%4&quot;</source>
+ <translation>无法打开请求的许可文件“%1”:%2.
+
+%3 &quot;%4&quot;</translation>
+ </message>
</context>
<context>
<name>QInstaller::ComponentModel</name>
@@ -825,62 +853,38 @@
<context>
<name>QInstaller::ComponentSelectionPage</name>
<message>
- <source>Alt+A</source>
- <comment>Select default components</comment>
- <translation>Alt+A</translation>
- </message>
- <message>
- <source>Def&amp;ault</source>
- <translation>默认(&amp;A)</translation>
+ <source>Default</source>
+ <translation>默认</translation>
</message>
<message>
<source>Select default components in the tree view.</source>
<translation>在树视图中选择默认组件。</translation>
</message>
<message>
- <source>Alt+R</source>
- <comment>Reset to already installed components</comment>
- <translation>Alt+R</translation>
- </message>
- <message>
- <source>&amp;Reset</source>
- <translation>重置(&amp;R)</translation>
+ <source>Reset</source>
+ <translation>重置</translation>
</message>
<message>
<source>Reset all components to their original selection state in the tree view.</source>
<translation>在树视图中将所有组件重置为其原始选择状态。</translation>
</message>
<message>
- <source>Alt+S</source>
- <comment>Select all components</comment>
- <translation>Alt+S</translation>
- </message>
- <message>
- <source>&amp;Select All</source>
- <translation>全选(&amp;S)</translation>
+ <source>Select All</source>
+ <translation>全选</translation>
</message>
<message>
<source>Select all components in the tree view.</source>
<translation>在树视图中选择所有组件。</translation>
</message>
<message>
- <source>Alt+D</source>
- <comment>Deselect all components</comment>
- <translation>Alt+D</translation>
- </message>
- <message>
- <source>&amp;Deselect All</source>
- <translation>取消全选(&amp;D)</translation>
+ <source>Deselect All</source>
+ <translation>取消全选</translation>
</message>
<message>
<source>Deselect all components in the tree view.</source>
<translation>在树视图中删除所有组件。</translation>
</message>
<message>
- <source>&amp;Browse QBSP files</source>
- <translation>浏览 QBSP 文件(&amp;B)</translation>
- </message>
- <message>
<source>Select a Qt Board Support Package file to install additional content that is not directly available from the online repositories.</source>
<translation>选择 Qt Board Support Package 文件以安装无法直接从在线存储库中获得的附加内容。</translation>
</message>
@@ -913,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>
@@ -924,6 +928,26 @@
<source>Search</source>
<translation>搜索</translation>
</message>
+ <message>
+ <source>Browse &amp;QBSP files</source>
+ <translation>浏览&amp;QBSP文件</translation>
+ </message>
+ <message>
+ <source>Select</source>
+ <translation>选择</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>错误</translation>
+ </message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>创建离线安装程序</translation>
+ </message>
+ <message>
+ <source>Create offline installer from selected components, instead of installing now.</source>
+ <translation>根据选中组件创建离线安装程序,而不是现在就安装。</translation>
+ </message>
</context>
<context>
<name>QInstaller::ConsumeOutputOperation</name>
@@ -1121,14 +1145,6 @@
<translation>下载错误</translation>
</message>
<message>
- <source>Hash verification while downloading failed. This is a temporary error, please retry.</source>
- <translation>下载时的哈希验证失败。 此错误为临时错误,请重试。</translation>
- </message>
- <message>
- <source>Cannot verify Hash</source>
- <translation>无法验证哈希</translation>
- </message>
- <message>
<source>Cannot download archive %1: %2</source>
<translation>无法下载存档 %1:%2</translation>
</message>
@@ -1198,6 +1214,27 @@ Error while loading %2</source>
<source>Total: </source>
<translation>总计:</translation>
</message>
+ <message>
+ <source>Retry count (%1) exceeded</source>
+ <translation>超过重试次数(%1)</translation>
+ </message>
+ <message>
+ <source>Hash verification while downloading failed. This is a temporary error, please retry.
+
+Expected: %1
+Downloaded: %2</source>
+ <translation>下载时的哈希验证失败。 此错误为临时错误,请重试。
+预期的:%1
+已下载:%2</translation>
+ </message>
+ <message>
+ <source>Cannot verify Hash
+Expected: %1
+Downloaded: %2</source>
+ <translation>无法验证哈希
+预期的:%1
+已下载:%2</translation>
+ </message>
</context>
<context>
<name>QInstaller::Downloader</name>
@@ -1336,7 +1373,7 @@ Error while loading %2</source>
<context>
<name>QInstaller::FinishedPage</name>
<message>
- <source>Completing the %1 Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>正在完成 %1 向导</translation>
</message>
<message>
@@ -1344,7 +1381,7 @@ Error while loading %2</source>
<translation>已完成</translation>
</message>
<message>
- <source>Click %1 to exit the %2 Wizard.</source>
+ <source>Click %1 to exit the %2 Setup.</source>
<translation>单击 %1 退出 %2 向导。</translation>
</message>
<message>
@@ -1356,7 +1393,7 @@ Error while loading %2</source>
<translation>立即运行 %1。</translation>
</message>
<message>
- <source>The %1 Wizard failed.</source>
+ <source>The %1 Setup failed.</source>
<translation>%1 向导失败。</translation>
</message>
</context>
@@ -1405,11 +1442,11 @@ Error while loading %2</source>
<context>
<name>QInstaller::IntroductionPage</name>
<message>
- <source>Setup - %1</source>
- <translation>安装程序 - %1</translation>
+ <source>Welcome</source>
+ <translation>欢迎</translation>
</message>
<message>
- <source>Welcome to the %1 Setup Wizard.</source>
+ <source>Welcome to the %1 Setup.</source>
<translation>欢迎使用 %1 安装向导。</translation>
</message>
<message>
@@ -1437,10 +1474,6 @@ Error while loading %2</source>
<translation>无可用更新。</translation>
</message>
<message>
- <source> Only local package management available.</source>
- <translation>仅本地软件包管理可用。</translation>
- </message>
- <message>
<source>&amp;Quit</source>
<translation>退出</translation>
</message>
@@ -1491,10 +1524,6 @@ Error while loading %2</source>
<source>Can not write license file &quot;%1&quot;.</source>
<translation>无法写入许可文件“%1”。</translation>
</message>
- <message>
- <source>No license files found to delete.</source>
- <translation>未找到要删除的许可文件。</translation>
- </message>
</context>
<context>
<name>QInstaller::LineReplaceOperation</name>
@@ -1518,10 +1547,6 @@ Error while loading %2</source>
<translation>缺少包管理器核心引擎。</translation>
</message>
<message>
- <source>Preparing meta information download...</source>
- <translation>正在准备下载元信息...</translation>
- </message>
- <message>
<source>Unpacking compressed repositories. This may take a while...</source>
<translation>解压压缩资料档案库。 这可能需要一些时间...</translation>
</message>
@@ -1558,14 +1583,6 @@ Error while loading %2</source>
<translation>检测到“%1”的校验和不匹配。</translation>
</message>
<message>
- <source>Retrieving meta information from remote repository... %1/%2 </source>
- <translation>正在从远程资料档案库中检索元信息... %1/%2 </translation>
- </message>
- <message>
- <source>Retrieving meta information from remote repository... </source>
- <translation>正在从远程资料档案库中检索元信息... </translation>
- </message>
- <message>
<source>Error while extracting archive &quot;%1&quot;: %2</source>
<translation>提取存档“%1”时出错:%2</translation>
</message>
@@ -1577,6 +1594,40 @@ Error while loading %2</source>
<source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
<translation>不支持的存档“%1”:没有处理程序注册在文件后缀“%2”名下。</translation>
</message>
+ <message>
+ <source>Fetching latest update information...</source>
+ <translation>正在获取最新更新信息……</translation>
+ </message>
+ <message numerus="yes">
+ <source>Updating local cache with %n new items...</source>
+ <translation>
+ <numerusform>正在更新本地缓存中的%n个新项目……</numerusform>
+ </translation>
+ </message>
+ <message>
+ <source>Clearing the cache directory and restarting the application may solve this.</source>
+ <translation>正在清空缓存目录并且重启应用程序也许可以解决这个问题。</translation>
+ </message>
+ <message>
+ <source>Unknown exception during updating cache.</source>
+ <translation>更新缓存过程中的未知异常。</translation>
+ </message>
+ <message>
+ <source>Cannot open extracted file &quot;%1&quot; for reading: %2</source>
+ <translation>无法为了读取%2打开已经提取的文件”%1“</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>无法为了写入%2打开文件“%1”</translation>
+ </message>
+ <message>
+ <source>Retrieving information from remote repositories...</source>
+ <translation>正在从远程存储库检索信息...</translation>
+ </message>
+ <message>
+ <source>Retrieving meta information from remote repository...</source>
+ <translation>正在从远程资料档案库中检索元信息...</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCore</name>
@@ -1585,10 +1636,8 @@ Error while loading %2</source>
<translation>编写维护工具时出错</translation>
</message>
<message>
- <source>
-Downloading packages...</source>
- <translation>
-正在下载包...</translation>
+ <source>Downloading packages...</source>
+ <translation>正在下载包...</translation>
</message>
<message>
<source>Installation canceled by user.</source>
@@ -1697,42 +1746,24 @@ Do you want to continue?</source>
<translation>无法解析所有依赖项。</translation>
</message>
<message>
- <source>Components about to be removed.</source>
- <translation>即将被移除的组件。</translation>
+ <source>Cannot install component %1. Component is installed only as automatic dependency to %2.</source>
+ <translation>无法安装组件 %1。组件仅作为 %2 的自动依赖时安装。</translation>
</message>
<message>
- <source>Cannot install component %1. Component is installed only as automatic dependency to %2.
-</source>
- <translation>无法安装组件 %1。组件仅作为 %2 的自动依赖时安装。
-</translation>
+ <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.</source>
+ <translation>无法安装组件 %1。组件不可选择,这意味着您必须选择其中一个子组件。</translation>
</message>
<message>
- <source>Cannot install component %1. Component is not checkable, meaning you have to select one of the subcomponents.
-</source>
- <translation>无法安装组件 %1。组件不可选择,这意味着您必须选择其中一个子组件。
-</translation>
+ <source>Component %1 already installed</source>
+ <translation>组件 %1 已安装</translation>
</message>
<message>
- <source>Component %1 already installed
-</source>
- <translation>组件 %1 已安装
-</translation>
+ <source>Cannot install %1. Component is virtual.</source>
+ <translation>无法安装 %1。组件是虚拟的。</translation>
</message>
<message>
- <source>Cannot install %1. Component is virtual.
-</source>
- <translation>无法安装 %1。组件是虚拟的。
-</translation>
- </message>
- <message>
- <source>Cannot install %1. Component not found.
-</source>
- <translation>无法安装 %1。未找到组件。
-</translation>
- </message>
- <message>
- <source>Running processes found.</source>
- <translation>已找到正在运行的进程。</translation>
+ <source>Cannot install %1. Component not found.</source>
+ <translation>无法安装 %1。未找到组件。</translation>
</message>
<message>
<source>Cannot elevate access rights while running from command line. Please restart the application as administrator.</source>
@@ -1755,10 +1786,6 @@ Do you want to continue?</source>
<translation>没有足够的磁盘空间来存储所有选定的组件! %1 可用,但至少需要 %2。</translation>
</message>
<message>
- <source>Not enough disk space to store temporary files! %1 are available, while the minimum required is %2.</source>
- <translation>没有足够的磁盘空间来存储临时文件! %1 可用,但至少需要 %2。</translation>
- </message>
- <message>
<source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume&apos;s space available afterwards.</source>
<translation>您选择安装的容量似乎有足够的安装空间,但之后的可用空间将不到该容量的 1%。</translation>
</message>
@@ -1779,15 +1806,41 @@ Do you want to continue?</source>
<translation>需要用户输入,但输出设备并未与任何终端裝置建立关联。</translation>
</message>
<message>
- <source>Cannot install %1. Component is a descendant of a virtual component %2.
-</source>
- <translation>无法安装 %1。组件是虚拟组件 %2 的后代。
-</translation>
+ <source>Cannot install %1. Component is a descendant of a virtual component %2.</source>
+ <translation>无法安装 %1。组件是虚拟组件 %2 的后代。</translation>
</message>
<message>
<source>The estimated installer size %1 would exceed the supported executable size limit of %2. The application may not be able to run.</source>
<translation>预估的安装程序大小 %1 可能超过被支持的可执行程序大小限制 %2。应用程序可能无法运行。</translation>
</message>
+ <message>
+ <source>Components about to be removed:</source>
+ <translation>即将要移除的组件:</translation>
+ </message>
+ <message>
+ <source>Cannot install component %1. There was a problem loading this component, so it is marked unstable and cannot be selected.</source>
+ <translation>无法安装组件 %1。加载这个组件时发生错误,所以它被标记为不稳定并且不能被选择。</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>没有足够的硬盘空间存储临时文件!有%1可用,但是最少需要%2。您可以通过修改安装程序设置中的本地缓存路径来为这些临时文件指定另外一个存储位置。</translation>
+ </message>
+ <message>
+ <source>Cannot resolve components to uninstall.</source>
+ <translation>无法解析要移除的组件。</translation>
+ </message>
+ <message>
+ <source>Cannot select alias %1. There was a problem loading this alias, so it is marked unstable and cannot be selected.</source>
+ <translation>无法选择别名%1。加载这一别名时发生问题,所以它被标识为不稳定,并且无法被选中。</translation>
+ </message>
+ <message>
+ <source>Cannot select %1. Alias is marked virtual, meaning it cannot be selected manually.</source>
+ <translation>无法选择%1。别名被标记为虚拟,意味着它不能被手动选中。</translation>
+ </message>
+ <message>
+ <source>Created installer will use %1 of disk space.</source>
+ <translation>创建的安装程序将使用%1的磁盘空间。</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerCorePrivate</name>
@@ -1904,16 +1957,12 @@ Do you want to continue?</source>
<translation>正在创建维护工具</translation>
</message>
<message>
- <source>
-Installation finished!</source>
- <translation>
-安装已完成!</translation>
+ <source>Installation finished!</source>
+ <translation>安装已完成!</translation>
</message>
<message>
- <source>
-Installation aborted!</source>
- <translation>
-安装已中止!</translation>
+ <source>Installation aborted!</source>
+ <translation>安装已中止!</translation>
</message>
<message>
<source>It is not possible to run that operation from a network location</source>
@@ -1924,16 +1973,12 @@ Installation aborted!</source>
<translation>正在移除未选中的组件...</translation>
</message>
<message>
- <source>
-Update finished!</source>
- <translation>
-更新已完成!</translation>
+ <source>Update finished!</source>
+ <translation>更新已完成!</translation>
</message>
<message>
- <source>
-Update aborted!</source>
- <translation>
-更新已中止!</translation>
+ <source>Update aborted!</source>
+ <translation>更新已中止!</translation>
</message>
<message>
<source>Removal completed successfully.</source>
@@ -1976,10 +2021,8 @@ Update aborted!</source>
<translation>已放弃离线生成!</translation>
</message>
<message>
- <source>
-Installing component %1</source>
- <translation>
-正在安装组件 %1</translation>
+ <source>Installing component %1</source>
+ <translation>正在安装组件 %1</translation>
</message>
<message>
<source>Installer Error</source>
@@ -2026,10 +2069,6 @@ Installing component %1</source>
<translation>无法检索元信息:%1</translation>
</message>
<message>
- <source>Cannot add temporary update source information.</source>
- <translation>无法添加临时更新源信息。</translation>
- </message>
- <message>
<source>Cannot find any update source information.</source>
<translation>找不到任何更新源信息。</translation>
</message>
@@ -2037,6 +2076,50 @@ Installing component %1</source>
<source>Dependency cycle between components &quot;%1&quot; and &quot;%2&quot; detected.</source>
<translation>检测到组件“%1”和“%2”之间的依赖项循环。</translation>
</message>
+ <message>
+ <source>Preparing to unpack components...</source>
+ <translation>正在准备解压组件......</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations completed.</source>
+ <translation>%2 个操作中的 %1 个已经完成。</translation>
+ </message>
+ <message>
+ <source>Unpacking components...</source>
+ <translation>正在解压组件......</translation>
+ </message>
+ <message>
+ <source>%1 of %2 operations rolled back.</source>
+ <translation>%2 个操作中的 %1 个已经回滚。</translation>
+ </message>
+ <message>
+ <source>Rollbacks complete.</source>
+ <translation>回滚完成。</translation>
+ </message>
+ <message>
+ <source>%1 of %2 components installed.</source>
+ <translation>%2 个组件中的 %1 个已经安装。</translation>
+ </message>
+ <message>
+ <source>All components installed.</source>
+ <translation>所有组件已安装。</translation>
+ </message>
+ <message>
+ <source>Loading component scripts...</source>
+ <translation>正在加载组件脚本……</translation>
+ </message>
+ <message>
+ <source>Alias declares name that conflicts with an existing component &quot;%1&quot;</source>
+ <translation>别名声明的名称与已经存在的组件“%1”冲突。</translation>
+ </message>
+ <message>
+ <source>Unresolved component aliases</source>
+ <translation>无法解析的组件别名</translation>
+ </message>
+ <message>
+ <source>Cyclic dependency between aliases &quot;%1&quot; and &quot;%2&quot; detected.</source>
+ <translation>检测到别名“%1”和“%2”之间的依赖项循环。</translation>
+ </message>
</context>
<context>
<name>QInstaller::PackageManagerGui</name>
@@ -2140,6 +2223,18 @@ Please copy the installer to a local drive</source>
<source>Uninstalling</source>
<translation>正在卸载</translation>
</message>
+ <message>
+ <source>&amp;Create Offline Installer</source>
+ <translation>创建离线安装程序(&amp;C)</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer for %1</source>
+ <translation>正在为%1创建离线安装程序</translation>
+ </message>
+ <message>
+ <source>Creating Offline Installer</source>
+ <translation>正在创建离线安装程序</translation>
+ </message>
</context>
<context>
<name>QInstaller::ProxyCredentialsDialog</name>
@@ -2183,7 +2278,7 @@ Please copy the installer to a local drive</source>
<translation>准备卸载</translation>
</message>
<message>
- <source>Setup is now ready to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
+ <source>All required information is now available to begin removing %1 from your computer.&lt;br&gt;&lt;font color=&quot;red&quot;&gt;The program directory %2 will be deleted completely&lt;/font&gt;, including all content in that directory!</source>
<translation>安装程序现已准备好从您的计算机中移除 %1。&lt;br&gt;&lt;font color=&quot;red&quot;&gt;将彻底删除程序目录 %2&lt;/font&gt;,目录内所有内容也将被删除!</translation>
</message>
<message>
@@ -2195,7 +2290,7 @@ Please copy the installer to a local drive</source>
<translation>准备更新包</translation>
</message>
<message>
- <source>Setup is now ready to begin updating your installation.</source>
+ <source>All required information is now available to begin updating your installation.</source>
<translation>安装程序现已准备好安装您的更新。</translation>
</message>
<message>
@@ -2207,13 +2302,25 @@ Please copy the installer to a local drive</source>
<translation>准备安装</translation>
</message>
<message>
- <source>Setup is now ready to begin installing %1 on your computer.</source>
+ <source>All required information is now available to begin installing %1 on your computer.</source>
<translation>安装程序现已准备好在您的计算器中安装 %1。</translation>
</message>
<message>
<source>Ready to Update</source>
<translation>准备更新</translation>
</message>
+ <message>
+ <source>Create Offline Installer</source>
+ <translation>创建离线安装程序</translation>
+ </message>
+ <message>
+ <source>Ready to Create Offline Installer</source>
+ <translation>准备创建离线安装程序</translation>
+ </message>
+ <message>
+ <source>All required information is now available to create an offline installer for selected components.</source>
+ <translation>为所选组件创建离线安装程序的所需信息都已经准备好。</translation>
+ </message>
</context>
<context>
<name>QInstaller::RegisterFileTypeOperation</name>
@@ -2270,7 +2377,7 @@ Please copy the installer to a local drive</source>
<context>
<name>QInstaller::RestartPage</name>
<message>
- <source>Completing the %1 Setup Wizard</source>
+ <source>Finished the %1 Setup</source>
<translation>正在完成 %1 安装向导</translation>
</message>
</context>
@@ -2493,6 +2600,14 @@ Please copy the installer to a local drive</source>
<source>Please make sure that the current user has read access to file &quot;%1&quot; or try running %2 as an administrator.</source>
<translation>请确保当前用户拥有文件“%1”的读取权限,或尝试以管理员身份运行 %2。</translation>
</message>
+ <message>
+ <source>Invalid value for &apos;max-concurrent-operations&apos;.</source>
+ <translation>“max-concurrent-operations”的值无效。</translation>
+ </message>
+ <message>
+ <source>Empty value for option &apos;cache-path&apos;.</source>
+ <translation>“cache-path”选项的值为空。</translation>
+ </message>
</context>
<context>
<name>RemoteClient</name>
@@ -2539,8 +2654,8 @@ or accept the elevation of access rights if being asked.</source>
<translation>无法打开设置文件 %1 进行读取:%2</translation>
</message>
<message>
- <source>Select Categories</source>
- <translation>选择类别</translation>
+ <source>Categories</source>
+ <translation>类别</translation>
</message>
</context>
<context>
@@ -2677,6 +2792,30 @@ or accept the elevation of access rights if being asked.</source>
<source>Deselect All</source>
<translation>取消全选</translation>
</message>
+ <message>
+ <source>Local cache</source>
+ <translation>本地缓存</translation>
+ </message>
+ <message>
+ <source>The meta information from remote repositories is cached to disk to improve loading times. You may select another directory to store the cache or clear the contents of the current cache.</source>
+ <translation>为了缩短加载时间,远程仓库的元信息被缓存到硬盘。您可以选择另外一个目录来存储缓存,或者清空当前缓存的内容。</translation>
+ </message>
+ <message>
+ <source>Path for cache:</source>
+ <translation>缓存的路径:</translation>
+ </message>
+ <message>
+ <source>Deletes the contents of the cache directory</source>
+ <translation>删除缓存目录中的内容</translation>
+ </message>
+ <message>
+ <source>Clear cache</source>
+ <translation>清空缓存</translation>
+ </message>
+ <message>
+ <source>Clearing cache...</source>
+ <translation>正在清空缓存...</translation>
+ </message>
</context>
<context>
<name>UpdateOperation</name>
@@ -2736,7 +2875,7 @@ or accept the elevation of access rights if being asked.</source>
<translation>错误</translation>
</message>
<message>
- <source>Component Information</source>
+ <source>Information</source>
<translation>组件信息</translation>
</message>
</context>
@@ -2746,6 +2885,22 @@ or accept the elevation of access rights if being asked.</source>
<source>Extracting &quot;%1&quot;</source>
<translation>正在提取“%1”</translation>
</message>
+ <message>
+ <source>Unsupported archive &quot;%1&quot;: no handler registered for file suffix &quot;%2&quot;.</source>
+ <translation>不支持的存档“%1”:没有处理程序注册在文件后缀名“%2”下。</translation>
+ </message>
+ <message>
+ <source>Cannot open archive &quot;%1&quot; for reading: %2</source>
+ <translation>无法打开存档“%1”进行读取:%2</translation>
+ </message>
+ <message>
+ <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
+ <translation>读取存档“%1”内容时发生错误:%2</translation>
+ </message>
+ <message>
+ <source>Removing files extracted from &quot;%1&quot;</source>
+ <translation>正在移除从“%1”中提取的文件</translation>
+ </message>
</context>
<context>
<name>QInstaller::QFileDialogProxy</name>
@@ -2765,35 +2920,178 @@ or accept the elevation of access rights if being asked.</source>
<translation>无法打开存档“%1”进行读取:%2</translation>
</message>
<message>
- <source>Error while reading contents of archive &quot;%1&quot;: %2</source>
- <translation>读取存档“%1”内容时发生错误:%2</translation>
+ <source>Error while extracting archive &quot;%1&quot;: %2</source>
+ <translation>提取存档“%1”时出错:%2</translation>
+ </message>
+</context>
+<context>
+ <name>AboutApplicationDialog</name>
+ <message>
+ <source>About %1 installer</source>
+ <translation>关于 %1 安装程序</translation>
</message>
<message>
- <source>Cannot prepare for file &quot;%1&quot;</source>
- <translation>无法为文件“%1“进行准备</translation>
+ <source>About %1 Maintenance Tool</source>
+ <translation>关于 %1 维护工具</translation>
</message>
+</context>
+<context>
+ <name>QInstaller::ExtractWorker</name>
<message>
- <source>Extract for archive &quot;%1&quot; canceled.</source>
- <translation>提取存档”%1“的操作取消了。</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>无法打开存档进行读取:%1</translation>
</message>
<message>
- <source>Error while extracting archive &quot;%1&quot;: %2</source>
- <translation>提取存档“%1”时出错:%2</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>无法读取条目头部:%1</translation>
</message>
<message>
- <source>Could not request administrator privileges required to extract archive &quot;%1&quot;.</source>
- <translation>无法获得提取存档“%1”所需的管理员权限。</translation>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>无法将条目“%1”写入磁盘:%2</translation>
</message>
</context>
<context>
- <name>AboutApplicationDialog</name>
+ <name>QInstaller::LibArchiveArchive</name>
<message>
- <source>About %1 installer</source>
- <translation>关于 %1 安装程序</translation>
+ <source>Cannot open archive for reading: %1</source>
+ <translation>无法打开存档进行读取:%1</translation>
</message>
<message>
- <source>About %1 Maintenance Tool</source>
- <translation>关于 %1 维护工具</translation>
+ <source>Cannot read entry header: %1</source>
+ <translation>无法读取条目头部:%1</translation>
+ </message>
+ <message>
+ <source>Cannot write entry &quot;%1&quot; to disk: %2</source>
+ <translation>无法将条目“%1”写入磁盘:%2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for writing: %2</source>
+ <translation>无法打开文件“%1”进行写入:%2</translation>
+ </message>
+ <message>
+ <source>Cannot open file &quot;%1&quot; for reading: %2</source>
+ <translation>无法打开文件“%1”进行读取:%2</translation>
+ </message>
+ <message>
+ <source>Cannot write entry header for &quot;%1&quot;: %2</source>
+ <translation>无法为“%1”写入条目头部:%2</translation>
+ </message>
+</context>
+<context>
+ <name>UninstallerCalculator</name>
+ <message>
+ <source>Deselected Components:</source>
+ <translation>取消组件选择:</translation>
+ </message>
+ <message>
+ <source>Components replaced by &quot;%1&quot;:</source>
+ <translation>组件被替换为“%1”:</translation>
+ </message>
+ <message>
+ <source>Removing virtual components without existing dependencies:</source>
+ <translation>正在移除没有现存依赖的虚拟组件:</translation>
+ </message>
+ <message>
+ <source>Components dependency &quot;%1&quot; removed:</source>
+ <translation>组件依赖“%1”已移除:</translation>
+ </message>
+ <message>
+ <source>Components autodependency &quot;%1&quot; removed:</source>
+ <translation>组件自动依赖“%1”已移除:</translation>
+ </message>
+</context>
+<context>
+ <name>GenericDataCache</name>
+ <message>
+ <source>Cannot initialize cache with empty path.</source>
+ <translation>无法使用空白路径初始化缓存。</translation>
+ </message>
+ <message>
+ <source>Cannot create directory &quot;%1&quot; for cache.</source>
+ <translation>无法为缓存创建“%1”目录。</translation>
+ </message>
+ <message>
+ <source>Cannot initialize cache: %1</source>
+ <translation>无法初始化缓存:%1</translation>
+ </message>
+ <message>
+ <source>Cannot clear invalidated cache.</source>
+ <translation>无法清空失效的缓存。</translation>
+ </message>
+ <message>
+ <source>Cannot remove manifest file: %1</source>
+ <translation>无法移除清单(manifest)文件:%1</translation>
+ </message>
+ <message>
+ <source>Error while clearing cache: %1</source>
+ <translation>清空缓存时发生错误:%1</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve items from invalidated cache.</source>
+ <translation>无法从失效的缓存中获取项目。</translation>
+ </message>
+ <message>
+ <source>Cannot retrieve item from invalidated cache.</source>
+ <translation>无法从失效的缓存中获取项目。</translation>
+ </message>
+ <message>
+ <source>Cannot register item to invalidated cache.</source>
+ <translation>无法向失效的缓存中注册项目。</translation>
+ </message>
+ <message>
+ <source>Cannot register null item.</source>
+ <translation>无法注册空项目。</translation>
+ </message>
+ <message>
+ <source>Cannot register invalid item with checksum %1</source>
+ <translation>无法注册校验和为%1的无效项目</translation>
+ </message>
+ <message>
+ <source>Cannot register item with checksum %1. An item with the same checksum already exists in cache.</source>
+ <translation>无法注册校验和为%1的项目。缓存中已经存在一个相同校验和的项目。</translation>
+ </message>
+ <message>
+ <source>Error while copying item to path &quot;%1&quot;: %2</source>
+ <translation>复制项目到“%1”路径时发生错误:%2</translation>
+ </message>
+ <message>
+ <source>Cannot remove item from invalidated cache.</source>
+ <translation>无法从失效缓存中移除项目。</translation>
+ </message>
+ <message>
+ <source>Cannot remove item specified by checksum %1: no such item exists.</source>
+ <translation>无法移除通过校验和%1指定的项目:查无此项。</translation>
+ </message>
+ <message>
+ <source>Error while removing directory &quot;%1&quot;: %2</source>
+ <translation>移除“%1”目录时发生错误:%2</translation>
+ </message>
+ <message>
+ <source>Error while invalidating cache: %1</source>
+ <translation>使缓存失效时发生错误:%1</translation>
+ </message>
+ <message>
+ <source>Cannot open manifest file: %1</source>
+ <translation>无法打开清单(manifest)文件:%1</translation>
+ </message>
+ <message>
+ <source>Cannot write contents for manifest file: %1</source>
+ <translation>无法写入清单(manifest)文件的内容:%1</translation>
+ </message>
+ <message>
+ <source>Cannot synchronize invalidated cache.</source>
+ <translation>无法同步失效的缓存。</translation>
+ </message>
+ <message>
+ <source>Unknown register mode selected!</source>
+ <translation>未知的注册模式被选中!</translation>
+ </message>
+</context>
+<context>
+ <name>TabController</name>
+ <message>
+ <source>Cache cleared successfully!</source>
+ <translation>缓存清空成功!</translation>
</message>
</context>
</TS>
diff --git a/tests/auto/installer/appendfileoperation/data/repository/Updates.xml b/tests/auto/installer/appendfileoperation/data/repository/Updates.xml
index a1c8f6aa2..f12d387a1 100644
--- a/tests/auto/installer/appendfileoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/appendfileoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,6 +10,7 @@
<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/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/binaryformat/tst_binaryformat.cpp b/tests/auto/installer/binaryformat/tst_binaryformat.cpp
index 7e0f5ed24..2fcad0b38 100644
--- a/tests/auto/installer/binaryformat/tst_binaryformat.cpp
+++ b/tests/auto/installer/binaryformat/tst_binaryformat.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -26,6 +26,8 @@
**
**************************************************************************/
+#include "../shared/packagemanager.h"
+
#include <binarycontent.h>
#include <binaryformat.h>
#include <errors.h>
@@ -51,8 +53,8 @@ struct Layout : public QInstaller::BinaryLayout
class TestOperation : public KDUpdater::UpdateOperation
{
public:
- explicit TestOperation(const QString &name)
- : KDUpdater::UpdateOperation(nullptr)
+ explicit TestOperation(const QString &name, PackageManagerCore *core = nullptr)
+ : KDUpdater::UpdateOperation(core)
{ setName(name); }
virtual void backup() {}
@@ -392,6 +394,45 @@ private slots:
resource->close();
}
+ void testXmlDocumentParsing()
+ {
+ PackageManagerCore core;
+ core.setValue(scTargetDir, QLatin1String("relocatable_targetdir"));
+
+ TestOperation op(QLatin1String("Operation 3"), &core);
+ QStringList stringListValue = (QStringList() << QLatin1String("list_value1") << QLatin1String("list_value2"));
+ op.setValue(QLatin1String("string_list"), stringListValue);
+
+ const QString stringValue = core.value(scTargetDir) + QLatin1String(", string_value1");
+ op.setValue(QLatin1String("string"), stringValue);
+
+ QVariantMap map;
+ map.insert(QLatin1String("key1"), 1);
+ map.insert(QLatin1String("key2"), QLatin1String("map_value2"));
+ op.setValue(QLatin1String("variant_map"), map);
+
+ QDomDocument document = op.toXml();
+ QVERIFY2(document.toString().contains(QLatin1String("@RELOCATABLE_PATH@")),
+ "TargetDir not replaced with @RELOCATABLE_PATH@");
+
+ op.fromXml(document); // Resets the operation values from written QDomDocuments
+
+ QCOMPARE(op.value(QLatin1String("string_list")), stringListValue);
+ QVERIFY2(!op.value(QLatin1String("string")).toString().contains(QLatin1String("@RELOCATABLE_PATH@")),
+ "@RELOCATABLE@ not replaced with TargetDir");
+ QCOMPARE(op.value(QLatin1String("variant_map")), map);
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ QCOMPARE(op.value(QLatin1String("string_list")).metaType().id(), QMetaType::QStringList);
+ QCOMPARE(op.value(QLatin1String("string")).metaType().id(), QMetaType::QString);
+ QCOMPARE(op.value(QLatin1String("variant_map")).metaType().id(), QMetaType::QVariantMap);
+#else
+ QCOMPARE(op.value(QLatin1String("string_list")).type(), QMetaType::QStringList);
+ QCOMPARE(op.value(QLatin1String("string")).type(), QMetaType::QString);
+ QCOMPARE(op.value(QLatin1String("variant_map")).type(), QMetaType::QVariantMap);
+#endif
+ }
+
void cleanupTestCase()
{
m_manager.clear();
diff --git a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
index 43bd15288..5978b3bb2 100644
--- a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
+++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -125,7 +125,7 @@ private slots:
const QString debugMessage = QString("Exception while loading the component script");
const QRegularExpression re(debugMessage);
QTest::ignoreMessage(QtWarningMsg, re);
- invalidScriptComponent->loadComponentScript(":///data/broken_script.qs");
+ invalidScriptComponent->evaluateComponentScript(":///data/broken_script.qs");
model->reset(components);
testModelState(model, m_checkedComponentsWithBrokenScript, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithBrokenScript);
@@ -145,7 +145,7 @@ private slots:
Component *componentDependingOnMissingDependency = core.componentByName("componentd");
componentDependingOnMissingDependency->addDependency("componentmissingdependency");
- core.componentsToInstallNeedsRecalculation();
+ core.recalculateAllComponents();
model->reset(components);
testModelState(model, m_checkedComponentsWithMissingDependency, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithMissingDependency);
@@ -157,6 +157,7 @@ private:
{
UpdatesInfo updatesInfo;
updatesInfo.setFileName(":///data/updates.xml");
+ updatesInfo.parseFile();
const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo();
QList <Component*> components;
diff --git a/tests/auto/installer/clientserver/BLACKLIST b/tests/auto/installer/clientserver/BLACKLIST
deleted file mode 100644
index a328d26e7..000000000
--- a/tests/auto/installer/clientserver/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[testQProcessWrapper]
-rhel-7.6 ci
diff --git a/tests/auto/installer/clientserver/tst_clientserver.cpp b/tests/auto/installer/clientserver/tst_clientserver.cpp
index ffd96d14e..7b3e65c4a 100644
--- a/tests/auto/installer/clientserver/tst_clientserver.cpp
+++ b/tests/auto/installer/clientserver/tst_clientserver.cpp
@@ -331,8 +331,7 @@ private slots:
RemoteClient::instance().init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Debug,
Protocol::StartAs::User);
- QSettingsWrapper wrapper(static_cast<QSettingsWrapper::Format>(format),
- QSettingsWrapper::UserScope, "digia", "clientserver");
+ QSettingsWrapper wrapper(format, QSettingsWrapper::UserScope, "digia", "clientserver");
QCOMPARE(wrapper.isConnectedToServer(), false);
wrapper.clear();
@@ -370,7 +369,6 @@ private slots:
QCOMPARE(settings.contains("key"), false);
const QDateTime dateTime = QDateTime::currentDateTimeUtc();
- QCOMPARE(settings.iniCodec(), nullptr);
// The wrapper does not support this method, but assume:
// QCOMPARE(wrapper.iniCodec(), QTextCodec::codecForName("UTF-8"));
wrapper.setValue("dateTime", dateTime);
@@ -469,6 +467,9 @@ private slots:
void testQProcessWrapper()
{
+ #ifdef Q_OS_LINUX
+ QSKIP("This test failes in CI redhat");
+ #endif
RemoteServer server;
QString socketName = QUuid::createUuid().toString();
server.init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Production);
diff --git a/tests/auto/installer/cliinterface/tst_cliinterface.cpp b/tests/auto/installer/cliinterface/tst_cliinterface.cpp
deleted file mode 100644
index 8f1784543..000000000
--- a/tests/auto/installer/cliinterface/tst_cliinterface.cpp
+++ /dev/null
@@ -1,602 +0,0 @@
-/**************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Installer Framework.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-**************************************************************************/
-#include "../shared/packagemanager.h"
-#include "../shared/verifyinstaller.h"
-
-#include <component.h>
-#include <packagemanagercore.h>
-
-#include <QLoggingCategory>
-#include <QTest>
-#include <QRegularExpression>
-
-#include <iostream>
-#include <sstream>
-
-using namespace QInstaller;
-
-class tst_CLIInterface : public QObject
-{
- Q_OBJECT
-
-private slots:
- void testListAvailablePackages()
- {
- QString loggingRules = (QLatin1String("ifw.* = false\n"));
-
- QTest::ignoreMessage(QtDebugMsg, "Operations sanity check succeeded.");
-
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
- (m_installDir, ":///data/repository"));
-
- QLoggingCategory::setFilterRules(loggingRules);
- auto func = &PackageManagerCore::listAvailablePackages;
-
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
- " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
- " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
- " <package name=\"C\" displayname=\"C\" version=\"1.0.0-1\"/>\n"
- "</availablepackages>\n"), func, QLatin1String("."), QHash<QString, QString>());
-
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
- " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
- "</availablepackages>\n"), func, QLatin1String("A"), QHash<QString, QString>());
-
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
- " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
- "</availablepackages>\n"), func, QLatin1String("A.*"), QHash<QString, QString>());
-
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
- "</availablepackages>\n"), func, QLatin1String("^B"), QHash<QString, QString>());
-
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
- "</availablepackages>\n"), func, QLatin1String("^B.*"), QHash<QString, QString>());
-
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"C\" displayname=\"C\" version=\"1.0.0-1\"/>\n"
- "</availablepackages>\n"), func, QLatin1String("^C"), QHash<QString, QString>());
-
- // Test with filters
- QHash<QString, QString> searchHash {
- { "Version", "1.0.2" },
- { "DisplayName", "A" }
- };
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
- " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
- "</availablepackages>\n"), func, QString(), searchHash);
-
- searchHash.clear();
- searchHash.insert("Default", "false");
- verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
- "<availablepackages>\n"
- " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
- "</availablepackages>\n"), func, QString(), searchHash);
-
- // Need to change rules here to catch messages
- QLoggingCategory::setFilterRules("ifw.* = true\n");
-
- QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
- core->listAvailablePackages(QLatin1String("C.virt"));
-
- QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
- core->listAvailablePackages(QLatin1String("C.virt.subcomponent"));
- }
-
- void testInstallPackageFails()
- {
- QString loggingRules = (QLatin1String("ifw.* = false\n"
- "ifw.installer.installlog = true\n"));
-
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
- (m_installDir, ":///data/uninstallableComponentsRepository"));
-
- QLoggingCategory::setFilterRules(loggingRules);
-
- QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install component A. Component "
- "is installed only as automatic dependency to autoDep.\n"));
- QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("A")));
-
- QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install component AB. Component "
- "is not checkable, meaning you have to select one of the subcomponents.\n"));
- QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("AB")));
-
- QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install B. Component is virtual.\n"));
- QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("B")));
-
- QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install B.subcomponent. Component "
- "is a descendant of a virtual component B.\n"));
- QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("B.subcomponent")));
-
- QTest::ignoreMessage(QtDebugMsg, "Preparing meta information download...");
- QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Cannot install MissingComponent. "
- "Component not found.\n"));
- QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("MissingComponent")));
- QCOMPARE(PackageManagerCore::Canceled, core->status());
- }
-
- void testUninstallPackageFails()
- {
- QString loggingRules = (QLatin1String("ifw.installer.installog = true\n"));
- PackageManagerCore core;
- core.setPackageManager();
- QString appFilePath = QCoreApplication::applicationFilePath();
- core.setAllowedRunningProcesses(QStringList() << appFilePath);
- QLoggingCategory::setFilterRules(loggingRules);
-
- m_installDir = QInstaller::generateTemporaryFileName();
- QVERIFY(QDir().mkpath(m_installDir));
- QVERIFY(QFile::copy(":/data/componentsFromInstallPackagesRepository.xml", m_installDir + "/components.xml"));
-
- core.setValue(scTargetDir, m_installDir);
- QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall ForcedInstallation component componentE");
- QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
- << "componentE"));
-
- QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall component componentD because it is added as auto dependency to componentA,componentB");
- QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
- << "componentD"));
-
- QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall component MissingComponent. Component not found in install tree.");
- QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
- << "MissingComponent"));
-
- QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall virtual component componentH");
- QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
- << "componentH"));
-
- QCOMPARE(PackageManagerCore::Success, core.status());
- }
-
- void testListInstalledPackages()
- {
- QString loggingRules = (QLatin1String("ifw.* = false\n"));
- PackageManagerCore core;
- core.setPackageManager();
- QLoggingCategory::setFilterRules(loggingRules);
- auto func = &PackageManagerCore::listInstalledPackages;
-
- const QString testDirectory = QInstaller::generateTemporaryFileName();
- QVERIFY(QDir().mkpath(testDirectory));
- QVERIFY(QFile::copy(":/data/components.xml", testDirectory + "/components.xml"));
-
- core.setValue(scTargetDir, testDirectory);
-
- verifyListPackagesMessage(&core, QLatin1String("<?xml version=\"1.0\"?>\n"
- "<localpackages>\n"
- " <package name=\"A\" displayname=\"A Title\" version=\"1.0.2-1\"/>\n"
- " <package name=\"B\" displayname=\"B Title\" version=\"1.0.0-1\"/>\n"
- "</localpackages>\n"), func, QString());
-
- verifyListPackagesMessage(&core, QLatin1String("<?xml version=\"1.0\"?>\n"
- "<localpackages>\n"
- " <package name=\"A\" displayname=\"A Title\" version=\"1.0.2-1\"/>\n"
- "</localpackages>\n"), func, QLatin1String("A"));
-
- QDir dir(testDirectory);
- QVERIFY(dir.removeRecursively());
- }
-
- void testNoDefaultInstallations()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- core->setNoDefaultInstallation(true);
- QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml"
- << "installcontentE.txt");
- core->setNoDefaultInstallation(false);
- }
-
- void testInstallForcedPackageSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentE")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Depends on componentA
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testInstallPackageSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentA")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Depends on componentA
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testUninstallPackageSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentA")));
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentE.txt"
- << "installcontentA.txt" << "installcontent.txt" << "installcontentG.txt");
-
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << QLatin1String("componentA")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentA");
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentG"); //Depends on componentA
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentE.txt");
- }
-
- void testRemoveAllSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentA")));
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentE.txt"
- << "installcontentA.txt" << "installcontent.txt" << "installcontentG.txt");
-
- core->commitSessionOperations();
- core->setUninstaller();
- QCOMPARE(PackageManagerCore::Success, core->removeInstallationSilently());
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentA");
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentE");
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentG");
-
- // On Windows we have to settle for the resources check above as maintenance
- // tool (if it would exists) and target directory are only removed later via
- // started VBScript process. On Unix platforms the target directory should
- // be removed in PackageManagerCorePrivate::runUninstaller().
-#if defined(Q_OS_UNIX)
- QVERIFY(!QDir(m_installDir).exists());
-#endif
- }
-
- void testInstallWithDependencySilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentC")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentB", "1.0.0content.txt"); //Dependency for componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Depends on componentA
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentI", "1.0.0content.txt"); //Virtual, depends on componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentD", "1.0.0content.txt"); //Autodepend on componentA and componentB
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentC.txt"
- << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
- << "installcontentD.txt"<< "installcontentE.txt" << "installcontentG.txt" << "installcontentI.txt");
- }
-
- void testUninstallWithDependencySilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentC")));
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentC.txt"
- << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
- << "installcontentD.txt"<< "installcontentE.txt" << "installcontentG.txt" << "installcontentI.txt");
-
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << QLatin1String("componentC")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentB", "1.0.0content.txt"); //Dependency for componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Depends on componentA
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentD", "1.0.0content.txt"); //Autodepend on componentA and componentB
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentC");
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentI"); //Virtual, depends on componentC
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml"
- << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
- << "installcontentD.txt"<< "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testInstallSubcomponentSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentF.subcomponent2.subsubcomponent2")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentF.subcomponent2.subsubcomponent2", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentF.subcomponent2", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentF", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default install
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentF.txt"
- << "installcontentF_2.txt" << "installcontentF_2_2.txt"
- << "installcontent.txt" << "installcontentA.txt"
- << "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testUninstallSubcomponentSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentF.subcomponent2.subsubcomponent2")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentF.txt"
- << "installcontentF_2.txt" << "installcontentF_2_2.txt"
- << "installcontent.txt" << "installcontentA.txt"
- << "installcontentE.txt" << "installcontentG.txt");
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << QLatin1String("componentF.subcomponent2")));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default install
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentF.subcomponent2.subsubcomponent2");
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentF.subcomponent2");
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentF");
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml"
- << "installcontent.txt" << "installcontentA.txt"
- << "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testInstallDefaultPackagesSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testInstallPackagesWithChangedRepository()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- core->setNoDefaultInstallation(true);
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentC")));
-
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency to componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentB", "1.0.0content.txt"); //Dependency to componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentC", "1.0.0content.txt"); //Selected
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentD", "1.0.0content.txt"); //Autodepend on componentA,componentB
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //AutodependOn componentC
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentI", "1.0.0content.txt"); //Virtual dependency to componentC
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml"
- << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
- << "installcontentC.txt" << "installcontentD.txt" << "installcontentE.txt"
- << "installcontentI.txt");
-
- core->reset();
- core->cancelMetaInfoJob(); //Call cancel to reset metadata so that update repositories are fetched
-
- QSet<Repository> repoList;
- Repository repo = Repository::fromUserInput(":///data/installPackagesDependencyChanged");
- repoList.insert(repo);
- core->settings().setDefaultRepositories(repoList);
-
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << QLatin1String("componentH")));
- // New dependency is added in repository from componentA to componentF, check that it is not installed
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentF");
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml"
- << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
- << "installcontentC.txt" << "installcontentD.txt" << "installcontentE.txt"
- << "installcontentI.txt" << "installcontentH.txt");
- core->setNoDefaultInstallation(false);
- }
-
- void testUnInstallDefaultPackagesSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
-
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << "componentG"));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentG");
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt");
- }
-
- void testUninstallForcedPackagesSilenly()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
- QCOMPARE(PackageManagerCore::Success, core->status());
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << "componentE"));
- QCOMPARE(PackageManagerCore::Success, core->status());
- //Nothing is uninstalled as componentE is forced install and cannot be uninstalled
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
- }
-
- void testUninstallAutodependencyPackagesSilenly()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- << "componentA" << "componentB"));
- QCOMPARE(PackageManagerCore::Success, core->status());
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << "componentD"));
- QCOMPARE(PackageManagerCore::Success, core->status());
- //Nothing is uninstalled as componentD is installed as autodependency to componentA and componentB
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentB", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentD", "1.0.0content.txt");
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default
- VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
- << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt"
- << "installcontentB.txt" << "installcontentD.txt");
- }
-
- void testUninstallVirtualSetVisibleSilently()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
- (m_installDir, ":///data/installPackagesRepository"));
- core->setVirtualComponentsVisible(true);
- QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
- <<"componentH"));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResources(m_installDir, "componentH", "1.0.0content.txt");
-
- core->commitSessionOperations();
- core->setPackageManager();
- QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
- << "componentH"));
- QCOMPARE(PackageManagerCore::Success, core->status());
- VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentH");
- }
-
- void testFileQuery()
- {
- QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit(m_installDir,
- ":///data/filequeryrepository"));
- core->setCommandLineInstance(true);
- core->setFileDialogAutomaticAnswer("ValidDirectory", m_installDir);
-
- QString testFile = qApp->applicationDirPath() + QDir::toNativeSeparators("/test");
- QFile file(testFile);
- QVERIFY(file.open(QIODevice::WriteOnly));
- core->setFileDialogAutomaticAnswer("ValidFile", testFile);
-
- //File dialog launched without ID
- core->setFileDialogAutomaticAnswer("GetExistingDirectory", m_installDir);
- core->setFileDialogAutomaticAnswer("GetExistingFile", testFile);
-
- QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
- QCOMPARE(PackageManagerCore::Success, core->status());
-
- QVERIFY(core->containsFileDialogAutomaticAnswer("ValidFile"));
- core->removeFileDialogAutomaticAnswer("ValidFile");
- QVERIFY(!core->containsFileDialogAutomaticAnswer("ValidFile"));
-
- QVERIFY(file.remove());
- core->deleteLater();
- }
-
- void init()
- {
- m_installDir = QInstaller::generateTemporaryFileName();
- QVERIFY(QDir().mkpath(m_installDir));
- }
-
- void initTestCase()
- {
- qSetGlobalQHashSeed(0); //Ensures the dom document deterministic behavior
- }
-
- void cleanup()
- {
- QDir dir(m_installDir);
- QVERIFY(dir.removeRecursively());
- }
-
-private:
- template <typename Func, typename... Args>
- void verifyListPackagesMessage(PackageManagerCore *core, const QString &message,
- Func func, Args... args)
- {
- std::ostringstream stream;
- std::streambuf *buf = std::cout.rdbuf();
- std::cout.rdbuf(stream.rdbuf());
-
- (core->*func)(std::forward<Args>(args)...);
-
- std::cout.rdbuf(buf);
- QVERIFY(stream && stream.str() == message.toStdString());
- }
-
-private:
- QString m_installDir;
-};
-
-
-QTEST_MAIN(tst_CLIInterface)
-
-#include "tst_cliinterface.moc"
diff --git a/tests/auto/installer/commandlineinstall/commandlineinstall.pro b/tests/auto/installer/commandlineinstall/commandlineinstall.pro
new file mode 100644
index 000000000..891830a97
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/commandlineinstall.pro
@@ -0,0 +1,9 @@
+include(../../qttest.pri)
+
+QT += qml
+
+SOURCES += tst_commandlineinstall.cpp
+
+RESOURCES += \
+ settings.qrc \
+ ../shared/config.qrc
diff --git a/tests/auto/installer/cliinterface/data/components.xml b/tests/auto/installer/commandlineinstall/data/components.xml
index d5102787f..d5102787f 100644
--- a/tests/auto/installer/cliinterface/data/components.xml
+++ b/tests/auto/installer/commandlineinstall/data/components.xml
diff --git a/tests/auto/installer/cliinterface/data/componentsFromInstallPackagesRepository.xml b/tests/auto/installer/commandlineinstall/data/componentsFromInstallPackagesRepository.xml
index 18d6f11c3..18d6f11c3 100644
--- a/tests/auto/installer/cliinterface/data/componentsFromInstallPackagesRepository.xml
+++ b/tests/auto/installer/commandlineinstall/data/componentsFromInstallPackagesRepository.xml
diff --git a/tests/auto/installer/cliinterface/data/config.xml b/tests/auto/installer/commandlineinstall/data/config.xml
index 041ce5062..041ce5062 100644
--- a/tests/auto/installer/cliinterface/data/config.xml
+++ b/tests/auto/installer/commandlineinstall/data/config.xml
diff --git a/tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z b/tests/auto/installer/commandlineinstall/data/filequeryrepository/A/1.0.2-1meta.7z
index 0b0b6ade6..0b0b6ade6 100644
--- a/tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z
+++ b/tests/auto/installer/commandlineinstall/data/filequeryrepository/A/1.0.2-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml b/tests/auto/installer/commandlineinstall/data/filequeryrepository/Updates.xml
index 72b7938d9..7957cde4e 100644
--- a/tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml
+++ b/tests/auto/installer/commandlineinstall/data/filequeryrepository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>4b124046df83fcd12fb7126b795a8b5a62602806</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/Updates.xml b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/Updates.xml
index 72931954a..72931954a 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/Updates.xml
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/Updates.xml
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentA/2.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentA/2.0.0content.7z
index 4ddbe565b..4ddbe565b 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentA/2.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentA/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentB/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentB/1.0.0content.7z
index f84ffcdc5..f84ffcdc5 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentB/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentB/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentC/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentC/1.0.0content.7z
index 9ad11e06f..9ad11e06f 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentC/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentC/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentD/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentD/1.0.0content.7z
index 0c8c52e31..0c8c52e31 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentD/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentD/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentF/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentF/1.0.0content.7z
index 4a04b1394..4a04b1394 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentF/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentF/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentH/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentH/1.0.0content.7z
index e1449ad29..e1449ad29 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesDependencyChanged/componentH/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesDependencyChanged/componentH/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/Updates.xml b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml
index adabceab4..3b5e22cc0 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/Updates.xml
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/Updates.xml
@@ -151,6 +151,7 @@
</PackageUpdate>
<PackageUpdate>
<Name>componentH</Name>
+ <DisplayName>component H</DisplayName>
<Description>This component does not depend on any other component.</Description>
<Version>1.0.0</Version>
<ReleaseDate>2014-08-25</ReleaseDate>
@@ -160,6 +161,7 @@
</PackageUpdate>
<PackageUpdate>
<Name>componentI</Name>
+ <DisplayName>component I</DisplayName>
<Description>This component has an automatic dependency on Component C. If C is marked for installation, this component is also installed.</Description>
<Version>1.0.0</Version>
<ReleaseDate>2014-08-25</ReleaseDate>
@@ -168,4 +170,14 @@
<DownloadableArchives>content.7z</DownloadableArchives>
<Virtual>true</Virtual>
</PackageUpdate>
+ <PackageUpdate>
+ <Name>componentJ</Name>
+ <DisplayName>component J</DisplayName>
+ <Description>This component has post install script.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>50</SortingPriority>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <Script postLoad="True">postLoadscript.js</Script>
+ </PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentA/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentA/1.0.0content.7z
index 4ddbe565b..4ddbe565b 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentA/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentA/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentB/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentB/1.0.0content.7z
index f84ffcdc5..f84ffcdc5 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentB/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentB/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentC/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentC/1.0.0content.7z
index 9ad11e06f..9ad11e06f 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentC/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentC/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentD/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentD/1.0.0content.7z
index 0c8c52e31..0c8c52e31 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentD/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentD/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentE/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentE/1.0.0content.7z
index f5abacf81..f5abacf81 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentE/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentE/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent1/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent1/1.0.0content.7z
index 3aced680f..3aced680f 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent1/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent1/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z
index e5385a163..e5385a163 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z
index 5bc549863..5bc549863 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z
index da9e9f340..da9e9f340 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z
index d0b013706..d0b013706 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z
index 74ab44033..74ab44033 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF/1.0.0content.7z
index 4a04b1394..4a04b1394 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentF/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentF/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentG/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentG/1.0.0content.7z
index 81fc02052..81fc02052 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentG/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentG/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentG/1.0.0meta.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentG/1.0.0meta.7z
index 90bfe33a2..90bfe33a2 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentG/1.0.0meta.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentG/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentH/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentH/1.0.0content.7z
index e1449ad29..e1449ad29 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentH/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentH/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentI/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentI/1.0.0content.7z
index 651a29f94..651a29f94 100644
--- a/tests/auto/installer/cliinterface/data/installPackagesRepository/componentI/1.0.0content.7z
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentI/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z
new file mode 100644
index 000000000..58ff52baa
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z
new file mode 100644
index 000000000..be99410a8
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/data/installPackagesRepository/componentJ/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/cliinterface/data/repository/Updates.xml b/tests/auto/installer/commandlineinstall/data/repository/Updates.xml
index 52707d09d..52707d09d 100644
--- a/tests/auto/installer/cliinterface/data/repository/Updates.xml
+++ b/tests/auto/installer/commandlineinstall/data/repository/Updates.xml
diff --git a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml b/tests/auto/installer/commandlineinstall/data/uninstallableComponentsRepository/Updates.xml
index f0c724db8..f0c724db8 100644
--- a/tests/auto/installer/cliinterface/data/uninstallableComponentsRepository/Updates.xml
+++ b/tests/auto/installer/commandlineinstall/data/uninstallableComponentsRepository/Updates.xml
diff --git a/tests/auto/installer/cliinterface/settings.qrc b/tests/auto/installer/commandlineinstall/settings.qrc
index 824517e1e..992dbfd58 100644
--- a/tests/auto/installer/cliinterface/settings.qrc
+++ b/tests/auto/installer/commandlineinstall/settings.qrc
@@ -14,6 +14,8 @@
<file>data/installPackagesRepository/componentG/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentH/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentI/1.0.0content.7z</file>
+ <file>data/installPackagesRepository/componentJ/1.0.0content.7z</file>
+ <file>data/installPackagesRepository/componentJ/1.0.0meta.7z</file>
<file>data/installPackagesRepository/componentG/1.0.0meta.7z</file>
<file>data/installPackagesRepository/componentF.subcomponent1/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentF.subcomponent2/1.0.0content.7z</file>
diff --git a/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
new file mode 100644
index 000000000..4dfadbad6
--- /dev/null
+++ b/tests/auto/installer/commandlineinstall/tst_commandlineinstall.cpp
@@ -0,0 +1,813 @@
+/**************************************************************************
+**
+** 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 "../shared/verifyinstaller.h"
+
+#include <component.h>
+#include <packagemanagercore.h>
+
+#include <QLoggingCategory>
+#include <QTest>
+#include <QRegularExpression>
+#include <QSignalSpy>
+
+using namespace QInstaller;
+
+typedef QList<QPair<QString, QString> > ComponentResourceHash;
+typedef QPair<QString, QString> ComponentResource;
+
+class tst_CommandLineInstall : public QObject
+{
+ Q_OBJECT
+
+public:
+ void ignoreAvailablePackagesMissingMessages()
+ {
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Searching packages with regular expression:"));
+ QTest::ignoreMessage(QtDebugMsg, "No matching packages found.");
+ }
+
+ void ignoreInstallPackageFailsMessages(const QRegularExpression &regExp)
+ {
+ QTest::ignoreMessage(QtDebugMsg, "Fetching latest update information...");
+ QTest::ignoreMessage(QtDebugMsg, regExp);
+ }
+
+private slots:
+ void testListAvailablePackages()
+ {
+ QString loggingRules = (QLatin1String("ifw.* = false\n"));
+
+ QLoggingCategory::setFilterRules(loggingRules);
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
+ (m_installDir, ":///data/repository"));
+
+ auto func = &PackageManagerCore::listAvailablePackages;
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
+ " <package name=\"C\" displayname=\"C\" version=\"1.0.0-1\"/>\n"
+ "</availablepackages>\n"), func, QLatin1String("."), QHash<QString, QString>());
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
+ "</availablepackages>\n"), func, QLatin1String("A"), QHash<QString, QString>());
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
+ "</availablepackages>\n"), func, QLatin1String("A.*"), QHash<QString, QString>());
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
+ "</availablepackages>\n"), func, QLatin1String("^B"), QHash<QString, QString>());
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
+ "</availablepackages>\n"), func, QLatin1String("^B.*"), QHash<QString, QString>());
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"C\" displayname=\"C\" version=\"1.0.0-1\"/>\n"
+ "</availablepackages>\n"), func, QLatin1String("^C"), QHash<QString, QString>());
+
+ // Test with filters
+ QHash<QString, QString> searchHash {
+ { "Version", "1.0.2" },
+ { "DisplayName", "A" }
+ };
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"AB\" displayname=\"AB\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"A\" displayname=\"A\" version=\"1.0.2-1\"/>\n"
+ "</availablepackages>\n"), func, QString(), searchHash);
+
+ searchHash.clear();
+ searchHash.insert("Default", "false");
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<availablepackages>\n"
+ " <package name=\"B\" displayname=\"B\" version=\"1.0.0-1\"/>\n"
+ "</availablepackages>\n"), func, QString(), searchHash);
+
+ QLoggingCategory::setFilterRules("ifw.* = true\n");
+ ignoreAvailablePackagesMissingMessages();
+ core->listAvailablePackages(QLatin1String("C.virt"));
+
+ ignoreAvailablePackagesMissingMessages();
+ core->listAvailablePackages(QLatin1String("C.virt.subcomponent"));
+ }
+
+ void testInstallPackageFails()
+ {
+ QString loggingRules = (QLatin1String("ifw.* = false\n"
+ "ifw.installer.installlog = true\n"));
+
+ QLoggingCategory::setFilterRules(loggingRules);
+ QTest::ignoreMessage(QtDebugMsg, "Operations sanity check succeeded.");
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Using metadata cache from "));
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
+ (m_installDir, ":///data/uninstallableComponentsRepository"));
+
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install component A. Component "
+ "is installed only as automatic dependency to autoDep.\n"));
+ QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("A")));
+
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install component AB. Component "
+ "is not checkable, meaning you have to select one of the subcomponents.\n"));
+ QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("AB")));
+
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install B. Component is virtual.\n"));
+ QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("B")));
+
+ ignoreInstallPackageFailsMessages(QRegularExpression("Cannot install B.subcomponent. Component "
+ "is a descendant of a virtual component B.\n"));
+ QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("B.subcomponent")));
+
+ QCOMPARE(PackageManagerCore::Canceled, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("MissingComponent")));
+ QCOMPARE(PackageManagerCore::Canceled, core->status());
+ }
+
+ void testUninstallPackageFails()
+ {
+ QString loggingRules = (QLatin1String("ifw.installer.installog = true\n"));
+ PackageManagerCore core;
+ core.setPackageManager();
+ QLoggingCategory::setFilterRules(loggingRules);
+
+ QVERIFY(QFile::copy(":/data/componentsFromInstallPackagesRepository.xml", m_installDir + "/components.xml"));
+
+ core.setValue(scTargetDir, m_installDir);
+ QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall ForcedInstallation component componentE");
+ QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
+ << "componentE"));
+
+ QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall component componentD because it is added as auto dependency to componentA,componentB");
+ QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
+ << "componentD"));
+
+ QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall component MissingComponent. Component not found in install tree.");
+ QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
+ << "MissingComponent"));
+
+ QTest::ignoreMessage(QtWarningMsg, "Cannot uninstall virtual component componentH");
+ QCOMPARE(PackageManagerCore::Success, core.uninstallComponentsSilently(QStringList()
+ << "componentH"));
+
+ QCOMPARE(PackageManagerCore::Success, core.status());
+ }
+
+ void testListInstalledPackages()
+ {
+ QString loggingRules = (QLatin1String("ifw.* = false\n"));
+ PackageManagerCore core;
+ core.setPackageManager();
+ QLoggingCategory::setFilterRules(loggingRules);
+ auto func = &PackageManagerCore::listInstalledPackages;
+
+ const QString testDirectory = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(testDirectory));
+ QVERIFY(QFile::copy(":/data/components.xml", testDirectory + "/components.xml"));
+
+ core.setValue(scTargetDir, testDirectory);
+
+ VerifyInstaller::verifyListPackagesMessage(&core, QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<localpackages>\n"
+ " <package name=\"A\" displayname=\"A Title\" version=\"1.0.2-1\"/>\n"
+ " <package name=\"B\" displayname=\"B Title\" version=\"1.0.0-1\"/>\n"
+ "</localpackages>\n"), func, QString());
+
+ VerifyInstaller::verifyListPackagesMessage(&core, QLatin1String("<?xml version=\"1.0\"?>\n"
+ "<localpackages>\n"
+ " <package name=\"A\" displayname=\"A Title\" version=\"1.0.2-1\"/>\n"
+ "</localpackages>\n"), func, QLatin1String("A"));
+
+ QDir dir(testDirectory);
+ 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");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+
+ /*********** Forced installation **********/
+ ComponentResourceHash componentResources;
+ componentResources.append(ComponentResource("componentA", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+ componentResources.append(ComponentResource("componentG", "1.0.0content.txt")); //Depends on componentA
+
+ QTest::newRow("Forced installation")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentE")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "installcontent.txt"
+ << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
+
+ /*********** Simple installation **********/
+ componentResources.clear();
+ componentResources.append(ComponentResource("componentA", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+ componentResources.append(ComponentResource("componentG", "1.0.0content.txt")); //Depends on componentA
+
+ QTest::newRow("Simple installation")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentA")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "installcontent.txt"
+ << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
+
+ /*********** Install with dependency **********/
+ componentResources.clear();
+ componentResources.append(ComponentResource("componentA", "1.0.0content.txt")); //Dependency for componentC
+ componentResources.append(ComponentResource("componentB", "1.0.0content.txt")); //Dependency for componentC
+ componentResources.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+ componentResources.append(ComponentResource("componentG", "1.0.0content.txt")); //Depends on componentA
+ componentResources.append(ComponentResource("componentI", "1.0.0content.txt")); //Virtual, depends on componentC
+ componentResources.append(ComponentResource("componentD", "1.0.0content.txt")); //Autodepend on componentA and componentB
+
+ QTest::newRow("Install with dependency")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentC")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "installcontentC.txt"
+ << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
+ << "installcontentD.txt"<< "installcontentE.txt" << "installcontentG.txt"
+ << "installcontentI.txt");
+
+ /*********** Install with subcomponents **********/
+ componentResources.clear();
+ componentResources.append(ComponentResource("componentF.subcomponent2.subsubcomponent2", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("componentF.subcomponent2", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("componentF", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("componentA", "1.0.0content.txt")); //Dependency for componentG
+ componentResources.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+ componentResources.append(ComponentResource("componentG", "1.0.0content.txt")); //Default install
+
+ QTest::newRow("Install with subcomponents")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentF.subcomponent2.subsubcomponent2")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "installcontentF.txt"
+ << "installcontentF_2.txt" << "installcontentF_2_2.txt"
+ << "installcontent.txt" << "installcontentA.txt"
+ << "installcontentE.txt" << "installcontentG.txt");
+ }
+
+ void testInstall()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
+ for (const ComponentResource &resource : componentResources)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFiles);
+ }
+
+ void testUninstall_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+ QTest::addColumn<QStringList>("uninstallComponents");
+ QTest::addColumn<PackageManagerCore::Status>("uninstallStatus");
+ QTest::addColumn<ComponentResourceHash>("componentResourcesAfterUninstall");
+ QTest::addColumn<QStringList >("installedFilesAfterUninstall");
+ QTest::addColumn<ComponentResourceHash >("deletedComponentResources");
+ QTest::addColumn<bool >("virtualSetVisible");
+
+ /*********** Uninstallation **********/
+ ComponentResourceHash componentResourcesAfterUninstall;
+ componentResourcesAfterUninstall.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+
+ ComponentResourceHash deletedComponentResources;
+ deletedComponentResources.append(ComponentResource("componentA", "1.0.0content.txt"));
+ deletedComponentResources.append(ComponentResource("componentG", "1.0.0content.txt"));
+
+ ComponentResourceHash componentResourceAfterInstall;
+ componentResourceAfterInstall << deletedComponentResources << componentResourcesAfterUninstall;
+
+ QStringList filesAfterUninstall;
+ filesAfterUninstall << "components.xml" << "installcontentE.txt";
+
+ QStringList filesAfterInstall;
+ filesAfterInstall << filesAfterUninstall << "installcontent.txt"
+ << "installcontentA.txt" << "installcontentG.txt";
+
+ QTest::newRow("Test uninstall")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentA")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterInstall
+ << (QStringList() << "componentA")
+ << PackageManagerCore::Success
+ << componentResourcesAfterUninstall
+ << filesAfterUninstall
+ << deletedComponentResources;
+
+ /*********** Uninstall with dependency **********/
+ componentResourcesAfterUninstall.clear();
+ componentResourcesAfterUninstall.append(ComponentResource("componentA", "1.0.0content.txt"));
+ componentResourcesAfterUninstall.append(ComponentResource("componentB", "1.0.0content.txt"));
+ componentResourcesAfterUninstall.append(ComponentResource("componentE", "1.0.0content.txt"));
+ componentResourcesAfterUninstall.append(ComponentResource("componentG", "1.0.0content.txt"));
+ componentResourcesAfterUninstall.append(ComponentResource("componentD", "1.0.0content.txt"));
+
+ deletedComponentResources.clear();
+ deletedComponentResources.append(ComponentResource("componentC", "1.0.0content.txt")); //Unselected
+ deletedComponentResources.append(ComponentResource("componentI", "1.0.0content.txt")); //Autodepends on componentC
+
+ componentResourceAfterInstall.clear();
+ componentResourceAfterInstall << deletedComponentResources << componentResourcesAfterUninstall;
+
+ filesAfterUninstall.clear();
+ filesAfterUninstall << "components.xml"
+ << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
+ << "installcontentD.txt"<< "installcontentE.txt" << "installcontentG.txt";
+
+ filesAfterInstall.clear();
+ filesAfterInstall << filesAfterUninstall << "installcontentC.txt"
+ << "installcontentI.txt";
+
+
+ QTest::newRow("Uninstall with dependency")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentC")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterInstall
+ << (QStringList() << "componentC")
+ << PackageManagerCore::Success
+ << componentResourcesAfterUninstall
+ << filesAfterUninstall
+ << deletedComponentResources;
+
+ /*********** Uninstall with subcomponents **********/
+ componentResourcesAfterUninstall.clear();
+ componentResourcesAfterUninstall.append(ComponentResource("componentA", "1.0.0content.txt"));
+ componentResourcesAfterUninstall.append(ComponentResource("componentE", "1.0.0content.txt"));
+ componentResourcesAfterUninstall.append(ComponentResource("componentG", "1.0.0content.txt"));
+
+ deletedComponentResources.clear();
+ deletedComponentResources.append(ComponentResource("componentF.subcomponent2.subsubcomponent2", "1.0.0content.txt"));
+ deletedComponentResources.append(ComponentResource("componentF.subcomponent2", "1.0.0content.txt"));
+ deletedComponentResources.append(ComponentResource("componentF", "1.0.0content.txt"));
+
+ componentResourceAfterInstall.clear();
+ componentResourceAfterInstall << deletedComponentResources << componentResourcesAfterUninstall;
+
+ filesAfterInstall.clear();
+ filesAfterInstall << "components.xml" << "installcontentF.txt"
+ << "installcontentF_2.txt" << "installcontentF_2_2.txt"
+ << "installcontent.txt" << "installcontentA.txt"
+ << "installcontentE.txt" << "installcontentG.txt";
+
+ filesAfterUninstall.clear();
+ filesAfterUninstall << "components.xml"
+ << "installcontent.txt" << "installcontentA.txt"
+ << "installcontentE.txt" << "installcontentG.txt";
+
+ QTest::newRow("Uninstall with subcomponents")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentF.subcomponent2.subsubcomponent2")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterInstall
+ << (QStringList() << "componentF.subcomponent2")
+ << PackageManagerCore::Success
+ << componentResourcesAfterUninstall
+ << filesAfterUninstall
+ << deletedComponentResources;
+
+ /*********** Uninstall forced packages **********/
+ componentResourceAfterInstall.clear();
+ componentResourceAfterInstall.append(ComponentResource("componentA", "1.0.0content.txt")); //ComponentG depends
+ componentResourceAfterInstall.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+ componentResourceAfterInstall.append(ComponentResource("componentG", "1.0.0content.txt")); //Default
+
+ componentResourcesAfterUninstall.clear();
+ componentResourcesAfterUninstall = componentResourceAfterInstall; //forced packages cannot be uninstalled
+
+ deletedComponentResources.clear();
+
+ filesAfterInstall.clear();
+ filesAfterInstall << "components.xml" << "installcontent.txt"
+ << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt";
+
+ filesAfterUninstall.clear();
+ filesAfterUninstall = filesAfterInstall;
+
+ QTest::newRow("Uninstall forced packages")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentG")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterInstall
+ << (QStringList() << "componentE")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterUninstall
+ << deletedComponentResources;
+
+ /*********** Uninstall autodependency packages **********/
+ componentResourceAfterInstall.clear();
+ componentResourceAfterInstall.append(ComponentResource("componentA", "1.0.0content.txt")); //ComponentG depends
+ componentResourceAfterInstall.append(ComponentResource("componentB", "1.0.0content.txt")); //Dependency for componentC
+ componentResourceAfterInstall.append(ComponentResource("componentD", "1.0.0content.txt")); //Autodepend on componentA and componentB
+ componentResourceAfterInstall.append(ComponentResource("componentE", "1.0.0content.txt")); //ForcedInstall
+ componentResourceAfterInstall.append(ComponentResource("componentG", "1.0.0content.txt")); //Default
+
+ componentResourcesAfterUninstall.clear();
+ componentResourcesAfterUninstall = componentResourceAfterInstall; //autodependency packages cannot be uninstalled
+
+ deletedComponentResources.clear();
+
+ filesAfterInstall.clear();
+ filesAfterInstall << "components.xml" << "installcontent.txt"
+ << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt"
+ << "installcontentB.txt" << "installcontentD.txt";
+ filesAfterUninstall.clear();
+ filesAfterUninstall = filesAfterInstall;
+
+ QTest::newRow("Uninstall autodependency packages")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentA" << "componentB")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterInstall
+ << (QStringList() << "componentD")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterUninstall
+ << deletedComponentResources;
+ }
+
+ void testUninstall()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+ QFETCH(QStringList, uninstallComponents);
+ QFETCH(PackageManagerCore::Status, uninstallStatus);
+ QFETCH(ComponentResourceHash, componentResourcesAfterUninstall);
+ QFETCH(QStringList, installedFilesAfterUninstall);
+ QFETCH(ComponentResourceHash, deletedComponentResources);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
+ for (const ComponentResource &resource : componentResources)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFiles);
+ core->commitSessionOperations();
+ core->setPackageManager();
+
+ QCOMPARE(uninstallStatus, core->uninstallComponentsSilently(QStringList()
+ << uninstallComponents));
+ QCOMPARE(uninstallStatus, core->status());
+ for (const ComponentResource &resource : componentResourcesAfterUninstall)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ for (const ComponentResource &resource : deletedComponentResources)
+ VerifyInstaller::verifyInstallerResourceFileDeletion(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFilesAfterUninstall);
+ }
+
+ void addToExistingInstall_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+ QTest::addColumn<QString>("newRepository");
+ QTest::addColumn<QStringList>("installNewComponents");
+ QTest::addColumn<PackageManagerCore::Status>("reinstallStatus");
+ QTest::addColumn<ComponentResourceHash>("componentResourcesAfterReinstall");
+ QTest::addColumn<QStringList >("installedFilesAfterReinstall");
+ QTest::addColumn<ComponentResourceHash >("deletedComponentResources");
+
+ /*********** New dependency in repository **********/
+ ComponentResourceHash componentResourceAfterInstall;
+ componentResourceAfterInstall.append(ComponentResource("componentA", "1.0.0content.txt")); //ComponentC depends on
+ componentResourceAfterInstall.append(ComponentResource("componentB", "1.0.0content.txt")); //ComponentC depends on
+ componentResourceAfterInstall.append(ComponentResource("componentC", "1.0.0content.txt")); //Selected
+ componentResourceAfterInstall.append(ComponentResource("componentD", "1.0.0content.txt")); //Autodepend on componentA,componentB
+ componentResourceAfterInstall.append(ComponentResource("componentE", "1.0.0content.txt")); //Forced
+ componentResourceAfterInstall.append(ComponentResource("componentG", "1.0.0content.txt")); //Default
+ componentResourceAfterInstall.append(ComponentResource("componentI", "1.0.0content.txt")); //Autodepend componentC
+
+ ComponentResourceHash componentResourcesAfterReinstall;
+ componentResourcesAfterReinstall = componentResourceAfterInstall;
+ componentResourcesAfterReinstall.append(ComponentResource("componentH", "1.0.0content.txt"));
+
+ ComponentResourceHash deletedComponentResources;
+ // New dependency is added in repository from componentA to componentF, check that it is not installed
+ deletedComponentResources.append(ComponentResource("componentF", "1.0.0content.txt"));
+
+ QStringList filesAfterInstall;
+ filesAfterInstall << "components.xml"
+ << "installcontent.txt" << "installcontentA.txt" << "installcontentB.txt"
+ << "installcontentC.txt" << "installcontentD.txt" << "installcontentE.txt"
+ << "installcontentG.txt" << "installcontentI.txt";
+
+ QStringList filesAfterReinstall;
+ filesAfterReinstall = filesAfterInstall;
+ filesAfterReinstall << "installcontentH.txt";
+
+ QTest::newRow("New dependency in repository")
+ << ":///data/installPackagesRepository"
+ << (QStringList() << "componentC")
+ << PackageManagerCore::Success
+ << componentResourceAfterInstall
+ << filesAfterInstall
+ << ":///data/installPackagesDependencyChanged"
+ << (QStringList() << "componentH")
+ << PackageManagerCore::Success
+ << componentResourcesAfterReinstall
+ << filesAfterReinstall
+ << deletedComponentResources;
+ }
+
+ void addToExistingInstall()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+ QFETCH(QString, newRepository);
+ QFETCH(QStringList, installNewComponents);
+ QFETCH(PackageManagerCore::Status, reinstallStatus);
+ QFETCH(ComponentResourceHash, componentResourcesAfterReinstall);
+ QFETCH(QStringList, installedFilesAfterReinstall);
+ QFETCH(ComponentResourceHash, deletedComponentResources);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+ QCOMPARE(status, core->installSelectedComponentsSilently(installComponents));
+ QCOMPARE(status, core->status());
+
+ for (const ComponentResource &resource : componentResources)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFiles);
+
+ core->reset();
+ core->cancelMetaInfoJob(); //Call cancel to reset metadata so that update repositories are fetched
+
+ QSet<Repository> repoList;
+ Repository repo = Repository::fromUserInput(newRepository);
+ repoList.insert(repo);
+ core->settings().setDefaultRepositories(repoList);
+
+ QCOMPARE(reinstallStatus, core->installSelectedComponentsSilently(installNewComponents));
+
+ for (const ComponentResource &resource : deletedComponentResources)
+ VerifyInstaller::verifyInstallerResourceFileDeletion(m_installDir, resource.first, resource.second);
+ for (const ComponentResource &resource : componentResourcesAfterReinstall)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFilesAfterReinstall);
+ }
+
+ void testInstallDefaultPackagesSilently()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepository"));
+ QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
+ QCOMPARE(PackageManagerCore::Success, core->status());
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default
+ VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontent.txt"
+ << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt");
+ }
+
+
+ void testNoDefaultInstallations()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepository"));
+ core->setNoDefaultInstallation(true);
+ QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
+ QCOMPARE(PackageManagerCore::Success, core->status());
+ VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml"
+ << "installcontentE.txt");
+ core->setNoDefaultInstallation(false);
+ }
+
+ void testRemoveAllSilently()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepository"));
+ QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
+ << QLatin1String("componentA")));
+ VerifyInstaller::verifyFileExistence(m_installDir, QStringList() << "components.xml" << "installcontentE.txt"
+ << "installcontentA.txt" << "installcontent.txt" << "installcontentG.txt");
+
+ core->commitSessionOperations();
+ core->setUninstaller();
+ QCOMPARE(PackageManagerCore::Success, core->removeInstallationSilently());
+ QCOMPARE(PackageManagerCore::Success, core->status());
+ VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentA");
+ VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentE");
+ VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentG");
+
+ // On Windows we have to settle for the resources check above as maintenance
+ // tool (if it would exists) and target directory are only removed later via
+ // started VBScript process. On Unix platforms the target directory should
+ // be removed in PackageManagerCorePrivate::runUninstaller().
+#if defined(Q_OS_UNIX)
+ QVERIFY(!QDir(m_installDir).exists());
+#endif
+ }
+
+ void testUninstallVirtualSetVisibleSilently()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepository"));
+ core->setVirtualComponentsVisible(true);
+ QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList()
+ <<"componentH"));
+ QCOMPARE(PackageManagerCore::Success, core->status());
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentH", "1.0.0content.txt");
+
+ core->commitSessionOperations();
+ core->setPackageManager();
+ QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList()
+ << "componentH"));
+ QCOMPARE(PackageManagerCore::Success, core->status());
+ VerifyInstaller::verifyInstallerResourcesDeletion(m_installDir, "componentH");
+ }
+
+ void testFileQuery()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit(m_installDir,
+ ":///data/filequeryrepository"));
+ core->setCommandLineInstance(true);
+ core->setFileDialogAutomaticAnswer("ValidDirectory", m_installDir);
+
+ QString testFile = qApp->applicationDirPath() + QDir::toNativeSeparators("/test");
+ QFile file(testFile);
+ QVERIFY(file.open(QIODevice::WriteOnly));
+ core->setFileDialogAutomaticAnswer("ValidFile", testFile);
+
+ //File dialog launched without ID
+ core->setFileDialogAutomaticAnswer("GetExistingDirectory", m_installDir);
+ core->setFileDialogAutomaticAnswer("GetExistingFile", testFile);
+
+ QCOMPARE(PackageManagerCore::Success, core->installDefaultComponentsSilently());
+ QCOMPARE(PackageManagerCore::Success, core->status());
+
+ QVERIFY(core->containsFileDialogAutomaticAnswer("ValidFile"));
+ core->removeFileDialogAutomaticAnswer("ValidFile");
+ QVERIFY(!core->containsFileDialogAutomaticAnswer("ValidFile"));
+
+ QVERIFY(file.remove());
+ }
+
+ void testPostScript()
+ {
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepository"));
+ QCOMPARE(PackageManagerCore::Success, core->installSelectedComponentsSilently(QStringList() << QLatin1String("componentJ")));
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentJ", "1.0.0content.txt"); //Selected
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentA", "1.0.0content.txt"); //Dependency for componentG
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentE", "1.0.0content.txt"); //ForcedInstall
+ VerifyInstaller::verifyInstallerResources(m_installDir, "componentG", "1.0.0content.txt"); //Default
+
+ //componentJ is extracted to "extractToAnotherPath" -folder in post install script
+ bool fileExists = QFileInfo::exists(m_installDir + QDir::separator() + "extractToAnotherPath" + QDir::separator() + "installcontentJ.txt");
+ QVERIFY2(fileExists, QString("File \"%1\" does not exist.").arg("installcontentJ.txt").toLatin1());
+ }
+
+ void init()
+ {
+ m_installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(m_installDir));
+ }
+
+ void initTestCase()
+ {
+ qSetGlobalQHashSeed(0); //Ensures the dom document deterministic behavior
+ }
+
+ void cleanup()
+ {
+ QDir dir(m_installDir);
+ QVERIFY(dir.removeRecursively());
+ }
+
+private:
+ QString m_installDir;
+};
+
+
+QTEST_MAIN(tst_CommandLineInstall)
+
+#include "tst_commandlineinstall.moc"
diff --git a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml
index 9e01f1800..de6e66525 100644
--- a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml
+++ b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml
@@ -160,4 +160,20 @@
<ForcedUpdate>true</ForcedUpdate>
<DownloadableArchives>content.7z</DownloadableArchives>
</PackageUpdate>
+ <PackageUpdate>
+ <Name>qt.tools.qtcreator</Name>
+ <DisplayName>Component qtcreator. Depends on virtual component</DisplayName>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <Dependencies>qt.tools.qtcreator.enterprise.plugins</Dependencies>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>qt.tools.qtcreator.enterprise.plugins</Name>
+ <DisplayName>enterprise plugin component</DisplayName>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <Virtual>true</Virtual>
+ </PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z
new file mode 100644
index 000000000..5c7c4f37d
--- /dev/null
+++ b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z
new file mode 100644
index 000000000..e5e4178a4
--- /dev/null
+++ b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml
new file mode 100644
index 000000000..f55998c48
--- /dev/null
+++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml
@@ -0,0 +1,32 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>qt.tools.qtcreator</Name>
+ <DisplayName>Component qtcreator. Dependency removed</DisplayName>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>qt.tools.qtcreator_gui</Name>
+ <DisplayName>Component K. Autodepends on componentJ</DisplayName>
+ <Description>Component K. Autodepends on componentJ</Description>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <AutoDependOn>qt.tools.qtcreator</AutoDependOn>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>qt.tools.qtcreator_gui.enterprise.plugins</Name>
+ <DisplayName>enterprise plugins, replaces another component</DisplayName>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2018-03-14</ReleaseDate>
+ <Virtual>true</Virtual>
+ <Replaces>qt.tools.qtcreator.enterprise.plugins</Replaces>
+ <AutoDependOn>qt.tools.qtcreator</AutoDependOn>
+ <UpdateFile UncompressedSize="99" OS="Any" CompressedSize="305"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z
new file mode 100644
index 000000000..f2b69fc13
--- /dev/null
+++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z
new file mode 100644
index 000000000..03d191cb5
--- /dev/null
+++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z
new file mode 100644
index 000000000..515c3a5cf
--- /dev/null
+++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/commandlineupdate/settings.qrc b/tests/auto/installer/commandlineupdate/settings.qrc
index d398abe30..c8f328d6b 100644
--- a/tests/auto/installer/commandlineupdate/settings.qrc
+++ b/tests/auto/installer/commandlineupdate/settings.qrc
@@ -17,6 +17,8 @@
<file>data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z</file>
<file>data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z</file>
+ <file>data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z</file>
+ <file>data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z</file>
<file>data/installPackagesRepositoryUpdate/Updates.xml</file>
<file>data/installPackagesRepositoryUpdate/componentA/1.0.0content.7z</file>
<file>data/installPackagesRepositoryUpdate/componentB/2.0.0content.7z</file>
@@ -52,5 +54,9 @@
<file>data/repositoryWithDependencyToEssential/Updates.xml</file>
<file>data/repositoryWithDependencyToEssential/componentAutoDependOnA/1.0content.7z</file>
<file>data/repositoryWithDependencyToEssential/componentA/3.0.0content.7z</file>
+ <file>data/repositoryUpdateWithReplacements/Updates.xml</file>
+ <file>data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z</file>
+ <file>data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z</file>
+ <file>data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp b/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp
index 04b11d427..0faeecf74 100644
--- a/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp
+++ b/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp
@@ -60,8 +60,6 @@ private slots:
void testUpdate_data()
{
- QTest::addColumn<QString>("installDir");
- QTest::addColumn<PackageManagerCore *>("core");
QTest::addColumn<QString>("repository");
QTest::addColumn<QStringList>("installComponents");
QTest::addColumn<PackageManagerCore::Status>("status");
@@ -86,12 +84,7 @@ private slots:
ComponentResourceHash deletedComponentResources;
deletedComponentResources.append(ComponentResource("componentA", "1.0.0content.txt"));
- QString installDir = QInstaller::generateTemporaryFileName();
- PackageManagerCore *core = PackageManager::getPackageManagerWithInit(installDir);
-
QTest::newRow("Update essential packages")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList() << "componentA")
<< PackageManagerCore::Success
@@ -107,15 +100,11 @@ private slots:
<< deletedComponentResources;
/*********** Update essential with autodependon**********/
- installDir = QInstaller::generateTemporaryFileName();
- core = PackageManager::getPackageManagerWithInit(installDir);
componentResourcesAfterUpdate.clear();
componentResourcesAfterUpdate.append(ComponentResource("componentA", "3.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentAutoDependOnA", "1.0content.txt"));
QTest::newRow("Update essential with autodependon")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList() << "componentA")
<< PackageManagerCore::Success
@@ -137,8 +126,6 @@ private slots:
componentResources.append(ComponentResource("componentH", "1.0.0content.txt"));
componentResources.append(ComponentResource("componentE", "1.0.0content.txt"));
- installDir = QInstaller::generateTemporaryFileName();
- core = PackageManager::getPackageManagerWithInit(installDir);
componentResourcesAfterUpdate.clear();
componentResourcesAfterUpdate.append(ComponentResource("componentH", "2.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentE", "1.0.0content.txt"));
@@ -147,8 +134,6 @@ private slots:
deletedComponentResources.append(ComponentResource("componentH", "1.0.0content.txt"));
QTest::newRow("Update force update packages")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList() << "componentH")
<< PackageManagerCore::Success
@@ -171,8 +156,6 @@ private slots:
componentResources.append(ComponentResource("componentB", "1.0.0content.txt"));
componentResources.append(ComponentResource("componentD", "1.0.0content.txt"));
- installDir = QInstaller::generateTemporaryFileName();
- core = PackageManager::getPackageManagerWithInit(installDir);
componentResourcesAfterUpdate.clear();
componentResourcesAfterUpdate.append(ComponentResource("componentA", "1.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentB", "2.0.0content.txt"));
@@ -185,8 +168,6 @@ private slots:
deletedComponentResources.append(ComponentResource("componentE", "1.0.0content.txt"));
QTest::newRow("Update packages")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList() << "componentC" << "componentH")
<< PackageManagerCore::Success
@@ -211,8 +192,6 @@ private slots:
componentResources.append(ComponentResource("componentE", "1.0.0content.txt"));
componentResources.append(ComponentResource("componentG", "1.0.0content.txt"));
- installDir = QInstaller::generateTemporaryFileName();
- core = PackageManager::getPackageManagerWithInit(installDir);
componentResourcesAfterUpdate.clear();
componentResourcesAfterUpdate.append(ComponentResource("componentA", "1.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentB", "1.0.0content.txt"));
@@ -225,8 +204,6 @@ private slots:
deletedComponentResources.append(ComponentResource("componentG", "1.0.0content.txt"));
QTest::newRow("Update two packages")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList()<< "componentA" << "componentB" << "componentE" << "componentG")
<< PackageManagerCore::Success
@@ -260,8 +237,6 @@ private slots:
componentResources.append(ComponentResource("componentG", "1.0.0content.txt"));
componentResources.append(ComponentResource("componentH", "1.0.0content.txt"));
- installDir = QInstaller::generateTemporaryFileName();
- core = PackageManager::getPackageManagerWithInit(installDir);
componentResourcesAfterUpdate.clear();
componentResourcesAfterUpdate.append(ComponentResource("componentA", "1.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentB", "2.0.0content.txt"));
@@ -287,8 +262,6 @@ private slots:
deletedComponentResources.append(ComponentResource("componentG", "1.0.0content.txt"));
QTest::newRow("Update all packages")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList() << "componentA" << "componentB" << "componentC" << "componentD" << "componentE"
<< "componentF" << "componentG" << "componentH")
@@ -318,13 +291,11 @@ private slots:
componentResources.append(ComponentResource("componentE", "1.0.0content.txt"));
componentResources.append(ComponentResource("componentG", "1.0.0content.txt"));
- installDir = QInstaller::generateTemporaryFileName();
- core = PackageManager::getPackageManagerWithInit(installDir);
componentResourcesAfterUpdate.clear();
componentResourcesAfterUpdate.append(ComponentResource("componentA", "1.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentB", "2.0.0content.txt"));
componentResourcesAfterUpdate.append(ComponentResource("componentD", "2.0.0content.txt"));//AutodepenOn componentA,componentB
- componentResourcesAfterUpdate.append(ComponentResource("componentE", "2.0.0content.txt"));//ForcedInstall
+ componentResourcesAfterUpdate.append(ComponentResource("componentE", "1.0.0content.txt"));//ForcedInstall, not updated without user selection
componentResourcesAfterUpdate.append(ComponentResource("componentG", "1.0.0content.txt"));
deletedComponentResources.clear();
@@ -332,8 +303,6 @@ private slots:
deletedComponentResources.append(ComponentResource("componentD", "1.0.0content.txt"));
QTest::newRow("Update packages with AutoDependOn")
- << installDir
- << core
<< ":///data/installPackagesRepository"
<< (QStringList()<< "componentA" << "componentB" << "componentE" << "componentG")
<< PackageManagerCore::Success
@@ -347,14 +316,42 @@ private slots:
<< componentResourcesAfterUpdate
<< (QStringList() << "components.xml" << "installcontent.txt" << "installcontentA.txt"
<< "installcontentD_update.txt" << "installcontentB_update.txt"
- << "installcontentE_update.txt" << "installcontentG.txt")
+ << "installcontentE.txt" << "installcontentG.txt")
<< deletedComponentResources;
+
+ /*********** Update packages with replacements **********/
+ componentResources.clear();
+ componentResources.append(ComponentResource("qt.tools.qtcreator", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("qt.tools.qtcreator.enterprise.plugins", "1.0.0content.txt"));
+ componentResources.append(ComponentResource("componentE", "1.0.0content.txt"));
+
+ componentResourcesAfterUpdate.clear();
+ componentResourcesAfterUpdate.append(ComponentResource("qt.tools.qtcreator", "2.0.0content.txt"));
+ componentResourcesAfterUpdate.append(ComponentResource("qt.tools.qtcreator_gui", "2.0.0content.txt"));
+ componentResourcesAfterUpdate.append(ComponentResource("qt.tools.qtcreator_gui.enterprise.plugins", "2.0.0content.txt"));
+ componentResourcesAfterUpdate.append(ComponentResource("componentE", "1.0.0content.txt"));
+
+ deletedComponentResources.clear();
+ deletedComponentResources.append(ComponentResource("qt.tools.qtcreator.enterprise.plugins", "1.0.0content.txt"));
+
+ QTest::newRow("Update packages with replacements")
+ << ":///data/installPackagesRepository"
+ << (QStringList()<< "qt.tools.qtcreator")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt"
+ << "installcontent.txt" << "qtcreator.txt" << "plugins.txt")
+ << ":///data/repositoryUpdateWithReplacements"
+ << (QStringList() << "qt.tools.qtcreator")
+ << PackageManagerCore::Success
+ << componentResourcesAfterUpdate
+ << (QStringList() << "components.xml" << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt"
+ << "installcontent.txt" << "gui.txt" << "qtcreator2.txt" << "gui_plugins.txt")
+ << deletedComponentResources;
}
void testUpdate()
{
- QFETCH(QString, installDir);
- QFETCH(PackageManagerCore *, core);
QFETCH(QString, repository);
QFETCH(QStringList, installComponents);
QFETCH(PackageManagerCore::Status, status);
@@ -367,12 +364,13 @@ private slots:
QFETCH(QStringList, installedFilesAfterUpdate);
QFETCH(ComponentResourceHash, deletedComponentResources);
- setRepository(repository, core);
+ PackageManagerCore *core = PackageManager::getPackageManagerWithInit(m_installDir, repository);
+
QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
for (const ComponentResource &resource : componentResources) {
- VerifyInstaller::verifyInstallerResources(installDir, resource.first, resource.second);
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
}
- VerifyInstaller::verifyFileExistence(installDir, installedFiles);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFiles);
core->commitSessionOperations();
core->setPackageManager();
@@ -380,25 +378,38 @@ private slots:
QCOMPARE(updateStatus, core->updateComponentsSilently(updateComponents));
for (const ComponentResource &resource : componentResourcesAfterUpdate) {
- VerifyInstaller::verifyInstallerResources(installDir, resource.first, resource.second);
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
}
for (const ComponentResource &resource : deletedComponentResources) {
- VerifyInstaller::verifyInstallerResourceFileDeletion(installDir, resource.first, resource.second);
+ VerifyInstaller::verifyInstallerResourceFileDeletion(m_installDir, resource.first, resource.second);
}
- VerifyInstaller::verifyFileExistence(installDir, installedFilesAfterUpdate);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFilesAfterUpdate);
delete core;
}
void testUpdateNoUpdatesForSelectedPackage()
{
- QString installDir = QInstaller::generateTemporaryFileName();
- PackageManagerCore *core = PackageManager::getPackageManagerWithInit(installDir);
- setRepository(":///data/installPackagesRepositoryUpdate", core);
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/installPackagesRepositoryUpdate"));
// No updates available for component so nothing to do
QCOMPARE(PackageManagerCore::Canceled, core->updateComponentsSilently(QStringList()
<< "componentInvalid"));
- delete core;
}
+
+ void init()
+ {
+ m_installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(m_installDir));
+ }
+
+ void cleanup()
+ {
+ QDir dir(m_installDir);
+ QVERIFY(dir.removeRecursively());
+ }
+
+private:
+ QString m_installDir;
};
diff --git a/tests/auto/installer/componentalias/componentalias.pro b/tests/auto/installer/componentalias/componentalias.pro
new file mode 100644
index 000000000..efbc0268f
--- /dev/null
+++ b/tests/auto/installer/componentalias/componentalias.pro
@@ -0,0 +1,8 @@
+include(../../qttest.pri)
+
+QT -= gui
+QT += testlib
+
+SOURCES = tst_componentalias.cpp
+
+RESOURCES += settings.qrc
diff --git a/tests/auto/installer/componentalias/data/aliases-optional.xml b/tests/auto/installer/componentalias/data/aliases-optional.xml
new file mode 100644
index 000000000..bc16013b7
--- /dev/null
+++ b/tests/auto/installer/componentalias/data/aliases-optional.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Aliases>
+ <Alias>
+ <Name>set-A</Name>
+ <DisplayName>Installation A (optional component requirement)</DisplayName>
+ <Description>Installs component A</Description>
+ <Version>2.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>A</RequiredComponents>
+ <OptionalComponents>component-nonexistent,B</OptionalComponents>
+ </Alias>
+ <Alias>
+ <Name>set-full</Name>
+ <DisplayName>Full installation (optional alias requirement)</DisplayName>
+ <Description>Installs all components</Description>
+ <Version>2.0.0</Version>
+ <Virtual>false</Virtual>
+ <OptionalAliases>set-A,set-nonexistent</OptionalAliases>
+ </Alias>
+ <Alias>
+ <Name>set-optional-broken</Name>
+ <DisplayName>Optionally requires broken alias</DisplayName>
+ <Description>Optionally requires broken alias</Description>
+ <Version>2.0.0</Version>
+ <Virtual>false</Virtual>
+ <OptionalAliases>set-broken</OptionalAliases>
+ </Alias>
+ <Alias>
+ <Name>set-broken</Name>
+ <DisplayName>Requires non-existent alias</DisplayName>
+ <Description>Requires non-existent alias</Description>
+ <Version>2.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set-nonexistent</RequiredAliases>
+ </Alias>
+</Aliases>
diff --git a/tests/auto/installer/componentalias/data/aliases-priority.xml b/tests/auto/installer/componentalias/data/aliases-priority.xml
new file mode 100644
index 000000000..f90d006b2
--- /dev/null
+++ b/tests/auto/installer/componentalias/data/aliases-priority.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Aliases>
+ <Alias>
+ <Name>set-A</Name>
+ <DisplayName>Installation A (priority)</DisplayName>
+ <Description>Installs component A</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>A</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-B</Name>
+ <DisplayName>Virtual installation B (priority)</DisplayName>
+ <Description>Installs component B</Description>
+ <Version>1.0.0</Version>
+ <Virtual>true</Virtual>
+ <RequiredComponents>B</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-D</Name>
+ <DisplayName>Installation D (Unstable) (priority)</DisplayName>
+ <Description>Installs missing component D</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>D</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-full</Name>
+ <DisplayName>Full installation (priority)</DisplayName>
+ <Description>Installs all components</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set-A,set-B</RequiredAliases>
+ <RequiredComponents>C</RequiredComponents>
+ </Alias>
+</Aliases>
diff --git a/tests/auto/installer/componentalias/data/aliases-versions.xml b/tests/auto/installer/componentalias/data/aliases-versions.xml
new file mode 100644
index 000000000..993e14673
--- /dev/null
+++ b/tests/auto/installer/componentalias/data/aliases-versions.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Aliases>
+ <Alias>
+ <Name>set-A</Name>
+ <DisplayName>Installation A (updated)</DisplayName>
+ <Description>Installs component A</Description>
+ <Version>2.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>A</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-B</Name>
+ <DisplayName>Virtual installation B (updated)</DisplayName>
+ <Description>Installs component B</Description>
+ <Version>1.1.0</Version>
+ <Virtual>true</Virtual>
+ <RequiredComponents>B</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-D</Name>
+ <DisplayName>Installation D (Unstable) (updated)</DisplayName>
+ <Description>Installs missing component D</Description>
+ <Version>1.2.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>D</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-full</Name>
+ <DisplayName>Full installation (updated)</DisplayName>
+ <Description>Installs all components</Description>
+ <Version>3.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set-A,set-B</RequiredAliases>
+ <RequiredComponents>C</RequiredComponents>
+ </Alias>
+</Aliases>
diff --git a/tests/auto/installer/componentalias/data/aliases.json b/tests/auto/installer/componentalias/data/aliases.json
new file mode 100644
index 000000000..4c78e20d2
--- /dev/null
+++ b/tests/auto/installer/componentalias/data/aliases.json
@@ -0,0 +1,38 @@
+{
+ "alias-packages": [
+ {
+ "Description": "Installs component A",
+ "DisplayName": "Installation A (JSON)",
+ "Name": "set-A-json",
+ "RequiredComponents": [
+ "A"
+ ],
+ "Version": "1.0.0",
+ "Virtual": false
+ },
+ {
+ "Description": "Installs component B",
+ "DisplayName": "Virtual installation B (JSON)",
+ "Name": "set-B-json",
+ "RequiredComponents": [
+ "B"
+ ],
+ "Version": "1.0.0",
+ "Virtual": true
+ },
+ {
+ "Description": "Installs all components",
+ "DisplayName": "Full installation (JSON)",
+ "Name": "set-full-json",
+ "RequiredAliases": [
+ "set-A-json",
+ "set-B-json"
+ ],
+ "RequiredComponents": [
+ "C"
+ ],
+ "Version": "1.0.0",
+ "Virtual": false
+ }
+ ]
+}
diff --git a/tests/auto/installer/componentalias/data/repository/Updates.xml b/tests/auto/installer/componentalias/data/repository/Updates.xml
new file mode 100644
index 000000000..e128590dc
--- /dev/null
+++ b/tests/auto/installer/componentalias/data/repository/Updates.xml
@@ -0,0 +1,43 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component A</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>C.subcomponent</Name>
+ <DisplayName>Subcomponent of C</DisplayName>
+ <Description>Example subcomponent</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>C.subcomponent.subcomponent</Name>
+ <DisplayName>Subcomponent of subcomponent component</DisplayName>
+ <Description>Example subcomponent of subcomponent</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/componentalias/metadata/installer-config/aliases.xml b/tests/auto/installer/componentalias/metadata/installer-config/aliases.xml
new file mode 100644
index 000000000..278ae918b
--- /dev/null
+++ b/tests/auto/installer/componentalias/metadata/installer-config/aliases.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Aliases>
+ <Alias>
+ <Name>set-A</Name>
+ <DisplayName>Installation A</DisplayName>
+ <Description>Installs component A</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>A</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-B</Name>
+ <DisplayName>Virtual installation B</DisplayName>
+ <Description>Installs component B</Description>
+ <Version>1.0.0</Version>
+ <Virtual>true</Virtual>
+ <RequiredComponents>B</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-D</Name>
+ <DisplayName>Installation D (Unstable)</DisplayName>
+ <Description>Installs missing component D</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredComponents>D</RequiredComponents>
+ </Alias>
+ <Alias>
+ <Name>set-E</Name>
+ <DisplayName>Installation E (Requires unstable)</DisplayName>
+ <Description>Installs set E</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set-D</RequiredAliases>
+ </Alias>
+ <Alias>
+ <Name>set-F</Name>
+ <DisplayName>Installation F (Requires alias that refers another unstable)</DisplayName>
+ <Description>Installs set F</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set-E</RequiredAliases>
+ </Alias>
+ <Alias>
+ <Name>set-full</Name>
+ <DisplayName>Full installation</DisplayName>
+ <Description>Installs all components</Description>
+ <Version>1.0.0</Version>
+ <Virtual>false</Virtual>
+ <RequiredAliases>set-A,set-B</RequiredAliases>
+ <RequiredComponents>C</RequiredComponents>
+ </Alias>
+</Aliases>
diff --git a/tests/auto/installer/componentalias/metadata/installer-config/config.xml b/tests/auto/installer/componentalias/metadata/installer-config/config.xml
new file mode 100644
index 000000000..9e2b5af55
--- /dev/null
+++ b/tests/auto/installer/componentalias/metadata/installer-config/config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Installer>
+ <Name>test</Name>
+ <Version>1.0.0</Version>
+ <AliasDefinitionsFile>aliases.xml</AliasDefinitionsFile>
+</Installer>
diff --git a/tests/auto/installer/componentalias/settings.qrc b/tests/auto/installer/componentalias/settings.qrc
new file mode 100644
index 000000000..771586edd
--- /dev/null
+++ b/tests/auto/installer/componentalias/settings.qrc
@@ -0,0 +1,11 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/repository/Updates.xml</file>
+ <file>data/aliases.json</file>
+ <file>data/aliases-priority.xml</file>
+ <file>data/aliases-versions.xml</file>
+ <file>data/aliases-optional.xml</file>
+ <file>metadata/installer-config/config.xml</file>
+ <file>metadata/installer-config/aliases.xml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/componentalias/tst_componentalias.cpp b/tests/auto/installer/componentalias/tst_componentalias.cpp
new file mode 100644
index 000000000..f6742af1f
--- /dev/null
+++ b/tests/auto/installer/componentalias/tst_componentalias.cpp
@@ -0,0 +1,269 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "../shared/packagemanager.h"
+#include "../shared/verifyinstaller.h"
+
+#include <packagemanagercore.h>
+#include <componentalias.h>
+
+#include <QTest>
+#include <QLoggingCategory>
+
+using namespace QInstaller;
+
+class tst_ComponentAlias : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void init()
+ {
+ m_installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(m_installDir));
+ }
+
+ void cleanup()
+ {
+ QDir dir(m_installDir);
+ QVERIFY(dir.removeRecursively());
+ }
+
+ void testSearchAlias()
+ {
+ QString loggingRules = (QLatin1String("ifw.* = false\n"));
+
+ QLoggingCategory::setFilterRules(loggingRules);
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
+ (m_installDir, ":///data/repository"));
+
+ core->setCommandLineInstance(true);
+
+ auto listMethod = &PackageManagerCore::listAvailableAliases;
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("\n"
+ "Name: set-A\n"
+ "Display name: Installation A\n"
+ "Description: Installs component A\n"
+ "Version: 1.0.0\n"
+ "Components: A\n"
+ "Required aliases: \n"
+ "Optional components: \n"
+ "Optional aliases: \n"
+ "========================================\n"
+ "Name: set-full\n"
+ "Display name: Full installation\n"
+ "Description: Installs all components\n"
+ "Version: 1.0.0\n"
+ "Components: C\n"
+ "Required aliases: set-A,set-B\n"
+ "Optional components: \n"
+ "Optional aliases: \n"), listMethod, QString());
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("\n"
+ "Name: set-A\n"
+ "Display name: Installation A\n"
+ "Description: Installs component A\n"
+ "Version: 1.0.0\n"
+ "Components: A\n"
+ "Required aliases: \n"
+ "Optional components: \n"
+ "Optional aliases: \n"), listMethod, QLatin1String("A"));
+ }
+
+ void testAliasSourceWithPriority()
+ {
+ QString loggingRules = (QLatin1String("ifw.* = false\n"));
+
+ QLoggingCategory::setFilterRules(loggingRules);
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
+ (m_installDir, ":///data/repository"));
+
+ core->setCommandLineInstance(true);
+ core->addAliasSource(AliasSource(AliasSource::SourceFileFormat::Xml,
+ ":///data/aliases-priority.xml", 1));
+
+ auto listMethod = &PackageManagerCore::listAvailableAliases;
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("\n"
+ "Name: set-A\n"
+ "Display name: Installation A (priority)\n"
+ "Description: Installs component A\n"
+ "Version: 1.0.0\n"
+ "Components: A\n"
+ "Required aliases: \n"
+ "Optional components: \n"
+ "Optional aliases: \n"
+ "========================================\n"
+ "Name: set-full\n"
+ "Display name: Full installation (priority)\n"
+ "Description: Installs all components\n"
+ "Version: 1.0.0\n"
+ "Components: C\n"
+ "Required aliases: set-A,set-B\n"
+ "Optional components: \n"
+ "Optional aliases: \n"), listMethod, QString());
+ }
+
+ void testAliasSourceWithVersionCompare()
+ {
+ QString loggingRules = (QLatin1String("ifw.* = false\n"));
+
+ QLoggingCategory::setFilterRules(loggingRules);
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManager
+ (m_installDir, ":///data/repository"));
+
+ core->setCommandLineInstance(true);
+ core->addAliasSource(AliasSource(AliasSource::SourceFileFormat::Xml,
+ ":///data/aliases-versions.xml", -1));
+
+ auto listMethod = &PackageManagerCore::listAvailableAliases;
+
+ VerifyInstaller::verifyListPackagesMessage(core.get(), QLatin1String("\n"
+ "Name: set-A\n"
+ "Display name: Installation A (updated)\n"
+ "Description: Installs component A\n"
+ "Version: 2.0.0\n"
+ "Components: A\n"
+ "Required aliases: \n"
+ "Optional components: \n"
+ "Optional aliases: \n"
+ "========================================\n"
+ "Name: set-full\n"
+ "Display name: Full installation (updated)\n"
+ "Description: Installs all components\n"
+ "Version: 3.0.0\n"
+ "Components: C\n"
+ "Required aliases: set-A,set-B\n"
+ "Optional components: \n"
+ "Optional aliases: \n"), listMethod, QString());
+ }
+
+ void testInstallAlias_data()
+ {
+ QTest::addColumn<AliasSource>("additionalSource");
+ QTest::addColumn<QStringList>("selectedAliases");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<QStringList>("installedComponents");
+
+ QTest::newRow("Simple alias")
+ << AliasSource()
+ << (QStringList() << "set-A")
+ << PackageManagerCore::Success
+ << (QStringList() << "A");
+
+ QTest::newRow("Alias with dependencies")
+ << AliasSource()
+ << (QStringList() << "set-full")
+ << PackageManagerCore::Success
+ << (QStringList() << "A" << "B" << "C" << "C.subcomponent" << "C.subcomponent.subcomponent");
+
+ QTest::newRow("Alias with dependencies (JSON source)")
+ << AliasSource(AliasSource::SourceFileFormat::Json, ":///data/aliases.json", -1)
+ << (QStringList() << "set-full-json")
+ << PackageManagerCore::Success
+ << (QStringList() << "A" << "B" << "C" << "C.subcomponent" << "C.subcomponent.subcomponent");
+
+ QTest::newRow("Alias with optional components (existent and non-existent)")
+ << AliasSource(AliasSource::SourceFileFormat::Xml, ":///data/aliases-optional.xml", -1)
+ << (QStringList() << "set-A")
+ << PackageManagerCore::Success
+ << (QStringList() << "A" << "B");
+
+ QTest::newRow("Alias with optional aliases (existent and non-existent)")
+ << AliasSource(AliasSource::SourceFileFormat::Xml, ":///data/aliases-optional.xml", -1)
+ << (QStringList() << "set-full")
+ << PackageManagerCore::Success
+ << (QStringList() << "A" << "B");
+
+ QTest::newRow("Alias with optional broken alias (will not install)")
+ << AliasSource(AliasSource::SourceFileFormat::Xml, ":///data/aliases-optional.xml", -1)
+ << (QStringList() << "set-optional-broken")
+ << PackageManagerCore::Canceled
+ << QStringList();
+ }
+
+ void testInstallAlias()
+ {
+ QFETCH(AliasSource, additionalSource);
+ QFETCH(QStringList, selectedAliases);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(QStringList, installedComponents);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/repository"));
+
+ core->setCommandLineInstance(true);
+
+ if (!additionalSource.filename.isEmpty())
+ core->addAliasSource(additionalSource);
+
+ QCOMPARE(core->installSelectedComponentsSilently(selectedAliases), status);
+
+ for (const QString &component : installedComponents)
+ QVERIFY(core->componentByName(component)->isInstalled());
+ }
+
+ void testInstallAliasFails_data()
+ {
+ QTest::addColumn<QStringList>("selectedAliases");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+
+ QTest::newRow("Virtual alias")
+ << (QStringList() << "set-B")
+ << PackageManagerCore::Canceled;
+
+ QTest::newRow("Unstable alias")
+ << (QStringList() << "set-D")
+ << PackageManagerCore::Canceled;
+
+ QTest::newRow("Nested reference to unstable alias")
+ << (QStringList() << "set-F")
+ << PackageManagerCore::Canceled;
+ }
+
+ void testInstallAliasFails()
+ {
+ QFETCH(QStringList, selectedAliases);
+ QFETCH(PackageManagerCore::Status, status);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, ":///data/repository"));
+
+ core->setCommandLineInstance(true);
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(selectedAliases));
+ }
+
+private:
+ QString m_installDir;
+};
+
+QTEST_GUILESS_MAIN(tst_ComponentAlias)
+
+#include "tst_componentalias.moc"
diff --git a/tests/auto/installer/componentmodel/data/updates.xml b/tests/auto/installer/componentmodel/data/updates.xml
index 3a6139446..cc2da0f8e 100644
--- a/tests/auto/installer/componentmodel/data/updates.xml
+++ b/tests/auto/installer/componentmodel/data/updates.xml
@@ -8,6 +8,8 @@
<DisplayName xml:lang="ru_RU">Корневая компонента</DisplayName>
<DisplayName xml:lang="de_DE">Wurzel Komponente</DisplayName>
<Description>Install this example.</Description>
+ <Description xml:lang="ru_RU">Установите этот пример.</Description>
+ <Description xml:lang="de_DE">Installieren Sie dieses Beispiel.</Description>
<Version>0.1.0-1</Version>
<ReleaseDate>2010-09-21</ReleaseDate>
<Default>true</Default>
diff --git a/tests/auto/installer/componentmodel/tst_componentmodel.cpp b/tests/auto/installer/componentmodel/tst_componentmodel.cpp
index 463485d96..276292233 100644
--- a/tests/auto/installer/componentmodel/tst_componentmodel.cpp
+++ b/tests/auto/installer/componentmodel/tst_componentmodel.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -59,6 +59,12 @@ static const QMap<QString, QString> rootComponentDisplayNames = {
{"de_de", QString::fromUtf8("Wurzel Komponente")}
};
+static const QMap<QString, QString> rootComponentDescriptions = {
+ {"", QLatin1String("Install this example.")},
+ {"ru_ru", QString::fromUtf8("Установите этот пример.")},
+ {"de_de", QString::fromUtf8("Installieren Sie dieses Beispiel.")}
+};
+
class tst_ComponentModel : public QObject
{
Q_OBJECT
@@ -367,11 +373,19 @@ private slots:
{
QStringList localesToTest = { "en_US", "ru_RU", "de_DE", "fr_FR" };
foreach (const QString &localeToTest, localesToTest) {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ QLocale::setDefault(QLocale(localeToTest));
+#else
QLocale::setDefault(localeToTest);
+#endif
QString expectedName = rootComponentDisplayNames.contains(localeToTest.toLower())
? rootComponentDisplayNames[localeToTest.toLower()]
: rootComponentDisplayNames[QString()];
+ QString expectedDescription = rootComponentDescriptions.contains(localeToTest.toLower())
+ ? rootComponentDescriptions[localeToTest.toLower()]
+ : rootComponentDescriptions[QString()];
+
setPackageManagerOptions(NoFlags);
QList<Component*> rootComponents = loadComponents();
@@ -384,6 +398,9 @@ private slots:
const QModelIndex root = model.indexFromComponentName(vendorProduct);
QCOMPARE(model.data(root, Qt::DisplayRole).toString(), expectedName);
+ Component *comp = model.componentFromIndex(root);
+ QCOMPARE(comp->value("Description"), expectedDescription);
+
qDeleteAll(rootComponents);
}
}
@@ -513,6 +530,7 @@ private:
{
UpdatesInfo updatesInfo;
updatesInfo.setFileName(":///data/updates.xml");
+ updatesInfo.parseFile();
const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo();
QHash<QString, Component*> components;
@@ -529,6 +547,7 @@ private:
component->setValue("Virtual", info.data.value("Virtual").toString());
component->setValue("DisplayName", info.data.value("DisplayName").toString());
component->setValue("Checkable", info.data.value("Checkable").toString());
+ component->setValue("Description", info.data.value("Description").toString());
QString forced = info.data.value("ForcedInstallation", scFalse).toString().toLower();
if (m_core.noForceInstallation())
diff --git a/tests/auto/installer/consumeoutputoperationtest/data/repository/Updates.xml b/tests/auto/installer/consumeoutputoperationtest/data/repository/Updates.xml
index 6b1856d51..1c3caf7be 100644
--- a/tests/auto/installer/consumeoutputoperationtest/data/repository/Updates.xml
+++ b/tests/auto/installer/consumeoutputoperationtest/data/repository/Updates.xml
@@ -10,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>4da14562d6515590d145678d21990faa817832bb</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/cliinterface/cliinterface.pro b/tests/auto/installer/contentsha1check/contentsha1check.pro
index db2a3138a..dd659bffa 100644
--- a/tests/auto/installer/cliinterface/cliinterface.pro
+++ b/tests/auto/installer/contentsha1check/contentsha1check.pro
@@ -2,7 +2,7 @@ include(../../qttest.pri)
QT += qml
-SOURCES += tst_cliinterface.cpp
+SOURCES += tst_contentsha1check.cpp
RESOURCES += \
settings.qrc \
diff --git a/tests/auto/installer/contentsha1check/data/config.xml b/tests/auto/installer/contentsha1check/data/config.xml
new file mode 100644
index 000000000..041ce5062
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/config.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Installer>
+ <Name>Your application</Name>
+ <Version>1.2.3</Version>
+ <MaintenanceToolName></MaintenanceToolName>
+ <MaintenanceToolIniFile></MaintenanceToolIniFile>
+ <TargetConfigurationFile></TargetConfigurationFile>
+</Installer>
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z
new file mode 100644
index 000000000..9109d284f
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1
new file mode 100644
index 000000000..564e8290b
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1
@@ -0,0 +1 @@
+eb5a464ab1a33bd1484e9b8f22b2c5f97abdfdf6 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z
new file mode 100644
index 000000000..947979354
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1
new file mode 100644
index 000000000..0f2144f0c
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1
@@ -0,0 +1 @@
+7e592e4b96adcefc77f2613100a3bd5e8835cce0 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7z
new file mode 100644
index 000000000..c14f55e4a
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/B/1.0.0-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml
new file mode 100644
index 000000000..1d2fb780b
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithchecksumcheck/Updates.xml
@@ -0,0 +1,27 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component A</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="74" CompressedSize="215" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>dec2797a059da9303fec87cc0c1dfb0866afeb8f</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile UncompressedSize="74" CompressedSize="215" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>2370e0b7dae861088c056d2de40c7ab7051bda13</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z
new file mode 100644
index 000000000..793ce161c
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1
new file mode 100644
index 000000000..641396e0f
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1
@@ -0,0 +1 @@
+2c185d45cb84cec7a71e317f8cfc64dd23094c32 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7z
new file mode 100644
index 000000000..4feab5c34
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/E/1.0.2-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z
new file mode 100644
index 000000000..f53a705dd
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1 b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1
new file mode 100644
index 000000000..daf89acba
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1
@@ -0,0 +1 @@
+d33a5fb638047372e9793b48d6c5ff85da560595 \ No newline at end of file
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7z
new file mode 100644
index 000000000..9931c0a7f
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/F/1.0.0-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml
new file mode 100644
index 000000000..bda013684
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithinvalidchecksum/Updates.xml
@@ -0,0 +1,27 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <PackageUpdate>
+ <Name>E</Name>
+ <DisplayName>E</DisplayName>
+ <Description>Example component E, invalid checksum</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile CompressedSize="215" OS="Any" UncompressedSize="74"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>db7e010425aaaaaaeebc6281a9d4c91e5666fd8f</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>F</Name>
+ <DisplayName>F</DisplayName>
+ <Description>Example component F</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile CompressedSize="215" OS="Any" UncompressedSize="74"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>b69b864cef5d0aecb496273374dd24bb8cba83bd</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7z
new file mode 100644
index 000000000..f96bfa9a3
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7z
new file mode 100644
index 000000000..976c57b43
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/C/1.0.2-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7z
new file mode 100644
index 000000000..015670af0
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1content.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7z b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7z
new file mode 100644
index 000000000..0b36609d7
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/D/1.0.0-1meta.7z
Binary files differ
diff --git a/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml
new file mode 100644
index 000000000..ae2ba911a
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/data/repositorywithnochecksumcheck/Updates.xml
@@ -0,0 +1,27 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile OS="Any" UncompressedSize="74" CompressedSize="215"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>f82e1d1bbfd252715ace26db8c62595252e28a3b</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>D</Name>
+ <DisplayName>D</DisplayName>
+ <Description>Example component D</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile OS="Any" UncompressedSize="74" CompressedSize="215"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>402f299ec90f215db390b150c9429101344cf1ea</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentsha1check/settings.qrc b/tests/auto/installer/contentsha1check/settings.qrc
new file mode 100644
index 000000000..e150ea61e
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/settings.qrc
@@ -0,0 +1,18 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/config.xml</file>
+ <file>data/repositorywithchecksumcheck/Updates.xml</file>
+ <file>data/repositorywithchecksumcheck/A/1.0.2-1content.7z</file>
+ <file>data/repositorywithchecksumcheck/A/1.0.2-1content.7z.sha1</file>
+ <file>data/repositorywithchecksumcheck/B/1.0.0-1content.7z</file>
+ <file>data/repositorywithchecksumcheck/B/1.0.0-1content.7z.sha1</file>
+ <file>data/repositorywithnochecksumcheck/Updates.xml</file>
+ <file>data/repositorywithnochecksumcheck/C/1.0.2-1content.7z</file>
+ <file>data/repositorywithnochecksumcheck/D/1.0.0-1content.7z</file>
+ <file>data/repositorywithinvalidchecksum/Updates.xml</file>
+ <file>data/repositorywithinvalidchecksum/E/1.0.2-1content.7z</file>
+ <file>data/repositorywithinvalidchecksum/E/1.0.2-1content.7z.sha1</file>
+ <file>data/repositorywithinvalidchecksum/F/1.0.0-1content.7z</file>
+ <file>data/repositorywithinvalidchecksum/F/1.0.0-1content.7z.sha1</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/contentsha1check/tst_contentsha1check.cpp b/tests/auto/installer/contentsha1check/tst_contentsha1check.cpp
new file mode 100644
index 000000000..e587d3011
--- /dev/null
+++ b/tests/auto/installer/contentsha1check/tst_contentsha1check.cpp
@@ -0,0 +1,181 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+#include "../shared/packagemanager.h"
+#include "../shared/verifyinstaller.h"
+
+#include <component.h>
+#include <packagemanagercore.h>
+
+#include <QLoggingCategory>
+#include <QTest>
+#include <QMessageBox>
+
+using namespace QInstaller;
+
+typedef QList<QPair<QString, QString> > ComponentResourceHash;
+typedef QPair<QString, QString> ComponentResource;
+
+static QStringList expectedMessages;
+
+void downloadingArchiveOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ Q_UNUSED(type)
+ Q_UNUSED(context)
+ QByteArray localMsg = msg.toLocal8Bit();
+ if (!msg.startsWith("Downloading archive"))
+ return;
+ if (expectedMessages.contains(msg))
+ expectedMessages.removeOne(msg);
+}
+
+class tst_ContentSha1Check : public QObject
+{
+ Q_OBJECT
+
+private slots:
+
+ void testInstall_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+ QTest::addColumn<QStringList >("expectedDownloadingArchiveMessages");
+
+ /*********** Install with checksum check **********/
+ ComponentResourceHash componentResources;
+ componentResources.append(ComponentResource("A", "1.0.2-1content.txt"));
+ componentResources.append(ComponentResource("B", "1.0.0-1content.txt"));
+
+ QTest::newRow("Check checksum")
+ << ":///data/repositorywithchecksumcheck"
+ << (QStringList() << "A" << "B")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "A.txt" << "B.txt")
+ << (QStringList() << "Downloading archive \"1.0.2-1content.7z.sha1\" for component A."
+ << "Downloading archive \"1.0.2-1content.7z\" for component A."
+ << "Downloading archive \"1.0.0-1content.7z.sha1\" for component B."
+ << "Downloading archive \"1.0.0-1content.7z\" for component B.");
+
+ /*********** Install with and without checksum check **********/
+ componentResources.clear();
+ componentResources.append(ComponentResource("C", "1.0.2-1content.txt"));
+ componentResources.append(ComponentResource("D", "1.0.0-1content.txt"));
+
+ QTest::newRow("Without checksum check")
+ << ":///data/repositorywithnochecksumcheck"
+ << (QStringList() << "C" << "D")
+ << PackageManagerCore::Success
+ << componentResources
+ << (QStringList() << "components.xml" << "C.txt" << "D.txt")
+ << (QStringList() << "Downloading archive \"1.0.2-1content.7z\" for component C."
+ << "Downloading archive \"1.0.0-1content.7z\" for component D.");
+
+ }
+
+ void testInstallWithInvalidChecksum_data()
+ {
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QStringList>("installComponents");
+ QTest::addColumn<PackageManagerCore::Status>("status");
+ QTest::addColumn<ComponentResourceHash>("componentResources");
+ QTest::addColumn<QStringList >("installedFiles");
+
+ /*********** Install with checksum check **********/
+ ComponentResourceHash componentResources;
+
+ QTest::newRow("Invalid checksum")
+ << ":///data/repositorywithinvalidchecksum"
+ << (QStringList() << "E" << "F")
+ << PackageManagerCore::Failure
+ << componentResources
+ << (QStringList());
+ }
+
+ void testInstall()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+ QFETCH(QStringList, expectedDownloadingArchiveMessages);
+
+ expectedMessages = expectedDownloadingArchiveMessages;
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+ qInstallMessageHandler(downloadingArchiveOutput);
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
+ for (const ComponentResource &resource : componentResources)
+ VerifyInstaller::verifyInstallerResources(m_installDir, resource.first, resource.second);
+ VerifyInstaller::verifyFileExistence(m_installDir, installedFiles);
+
+ QVERIFY(expectedMessages.isEmpty());
+ }
+
+ void testInstallWithInvalidChecksum()
+ {
+ QFETCH(QString, repository);
+ QFETCH(QStringList, installComponents);
+ QFETCH(PackageManagerCore::Status, status);
+ QFETCH(ComponentResourceHash, componentResources);
+ QFETCH(QStringList, installedFiles);
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_installDir, repository));
+ core->setMessageBoxAutomaticAnswer("DownloadError", QMessageBox::Cancel);
+ core->setMessageBoxAutomaticAnswer("installationError", QMessageBox::Ok);
+
+ QCOMPARE(status, core->installSelectedComponentsSilently(QStringList() << installComponents));
+ QVERIFY(!QDir().exists(m_installDir));
+ }
+
+ void init()
+ {
+ m_installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(m_installDir));
+ }
+
+ void cleanup()
+ {
+ QDir dir(m_installDir);
+ QVERIFY(dir.removeRecursively());
+ }
+
+private:
+ QString m_installDir;
+ QStringList m_expectedMessages;
+};
+
+
+QTEST_MAIN(tst_ContentSha1Check)
+
+#include "tst_contentsha1check.moc"
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml
new file mode 100644
index 000000000..fe5dc4dec
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/Updates.xml
@@ -0,0 +1,28 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>componentA</Name>
+ <DisplayName>Component A</DisplayName>
+ <Description>Component A.</Description>
+ <Version>0.1.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+ <UpdateFile CompressedSize="283" UncompressedSize="101" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>5</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentEssential</Name>
+ <DisplayName>Component Essential</DisplayName>
+ <Description>Component Essential</Description>
+ <Essential>true</Essential>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile UncompressedSize="101" CompressedSize="283" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7z
new file mode 100644
index 000000000..bdbabc7fd
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentA/0.1.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z
new file mode 100644
index 000000000..9c86042c0
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml
new file mode 100644
index 000000000..f76c03faa
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/Updates.xml
@@ -0,0 +1,27 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>componentA</Name>
+ <DisplayName>Component A</DisplayName>
+ <Description>This component does not depend on any other component.</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>100</SortingPriority>
+ <UpdateFile OS="Any" CompressedSize="283" UncompressedSize="101"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <ContentSha1>10</ContentSha1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>componentEssential</Name>
+ <DisplayName>Component Essential</DisplayName>
+ <Description>Component Essential</Description>
+ <Essential>true</Essential>
+ <Version>2.0.0</Version>
+ <ReleaseDate>2014-08-25</ReleaseDate>
+ <SortingPriority>80</SortingPriority>
+ <UpdateFile UncompressedSize="101" CompressedSize="283" OS="Any"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7z
new file mode 100644
index 000000000..46a9f1d1e
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentA/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7z b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7z
new file mode 100644
index 000000000..435f260ee
--- /dev/null
+++ b/tests/auto/installer/contentshaupdate/data/repositoryWithEssential/componentEssential/2.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/contentshaupdate/settings.qrc b/tests/auto/installer/contentshaupdate/settings.qrc
index a5d045f46..e3426be92 100644
--- a/tests/auto/installer/contentshaupdate/settings.qrc
+++ b/tests/auto/installer/contentshaupdate/settings.qrc
@@ -5,10 +5,16 @@
<file>data/repository/componentB/1.0.0content.7z</file>
<file>data/repository/componentC/1.0.0content.7z</file>
<file>data/repository/componentD/1.0.0content.7z</file>
+ <file>data/repositoryWithEssential/Updates.xml</file>
+ <file>data/repositoryWithEssential/componentA/1.0.0content.7z</file>
+ <file>data/repositoryWithEssential/componentEssential/2.0.0content.7z</file>
<file>data/repositoryUpdate/Updates.xml</file>
<file>data/repositoryUpdate/componentA/0.1.0content.7z</file>
<file>data/repositoryUpdate/componentB/0.1.0content.7z</file>
<file>data/repositoryUpdate/componentC/2.0.0content.7z</file>
<file>data/repositoryUpdate/componentD/2.0.0content.7z</file>
+ <file>data/repositoryUpdateWithEssential/Updates.xml</file>
+ <file>data/repositoryUpdateWithEssential/componentA/0.1.0content.7z</file>
+ <file>data/repositoryUpdateWithEssential/componentEssential/1.0.0content.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp b/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
index d8d2f5377..5d282fe38 100644
--- a/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
+++ b/tests/auto/installer/contentshaupdate/tst_contentshaupdate.cpp
@@ -58,34 +58,50 @@ private slots:
void updateWithContentSha1_data()
{
+ QTest::addColumn<QString>("repository");
+ QTest::addColumn<QString>("repositoryForUpdate");
QTest::addColumn<QString>("component");
QTest::addColumn<QString>("content");
QTest::addColumn<QString>("updatedContent");
QTest::addColumn<PackageManagerCore::Status>("expectedStatusAfterInstall");
QTest::addColumn<PackageManagerCore::Status>("expectedStatusAfterUpdate");
- QTest::newRow("ContentSha1Change") << "componentA" << "1.0.0content.txt" << "0.1.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Success;
- QTest::newRow("NewContentSha1") << "componentB" << "1.0.0content.txt" << "0.1.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Success;
- QTest::newRow("SameContentSha1") << "componentC" << "1.0.0content.txt" << "1.0.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Canceled;
- QTest::newRow("Sha1RemovedFromRepo") << "componentD" << "1.0.0content.txt" << "2.0.0content.txt" << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("Sha1UpdateForEssential")
+ << ":///data/repositoryWithEssential" << ":///data/repositoryUpdateWithEssential"
+ << "componentEssential" << "2.0.0content.txt" << "1.0.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::EssentialUpdated;
+ QTest::newRow("ContentSha1Change")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentA" << "1.0.0content.txt" << "0.1.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("NewContentSha1")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentB" << "1.0.0content.txt" << "0.1.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Success;
+ QTest::newRow("SameContentSha1")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentC" << "1.0.0content.txt" << "1.0.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Canceled;
+ QTest::newRow("Sha1RemovedFromRepo")
+ << ":///data/repository" << ":///data/repositoryUpdate" << "componentD" << "1.0.0content.txt" << "2.0.0content.txt"
+ << PackageManagerCore::Success << PackageManagerCore::Success;
}
void updateWithContentSha1()
{
+ QFETCH(QString, repository);
+ QFETCH(QString, repositoryForUpdate);
QFETCH(QString, component);
QFETCH(QString, content);
QFETCH(QString, updatedContent);
QFETCH(PackageManagerCore::Status, expectedStatusAfterInstall);
QFETCH(PackageManagerCore::Status, expectedStatusAfterUpdate);
- setRepository(":///data/repository");
+ setRepository(repository);
QCOMPARE(expectedStatusAfterInstall, core->installSelectedComponentsSilently(QStringList() << component));
QCOMPARE(expectedStatusAfterInstall, core->status());
VerifyInstaller::verifyInstallerResources(m_installDir, component, content);
core->commitSessionOperations();
core->setPackageManager();
- setRepository(":///data/repositoryUpdate");
+ setRepository(repositoryForUpdate);
QCOMPARE(expectedStatusAfterUpdate, core->updateComponentsSilently(QStringList()));
VerifyInstaller::verifyInstallerResources(m_installDir, component, updatedContent);
}
diff --git a/tests/auto/installer/copydirectoryoperation/data/repository/Updates.xml b/tests/auto/installer/copydirectoryoperation/data/repository/Updates.xml
index a1c8f6aa2..fb8c9cf1c 100644
--- a/tests/auto/installer/copydirectoryoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/copydirectoryoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,6 +10,7 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>4fd4dd023111fcbbdd1221032c2984d249f4cad1</SHA1>
<DownloadableArchives>content.7z</DownloadableArchives>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/copydirectoryoperation/data/xmloperationrepository/Updates.xml b/tests/auto/installer/copydirectoryoperation/data/xmloperationrepository/Updates.xml
index 90b26a387..b495f3307 100644
--- a/tests/auto/installer/copydirectoryoperation/data/xmloperationrepository/Updates.xml
+++ b/tests/auto/installer/copydirectoryoperation/data/xmloperationrepository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -8,6 +9,7 @@
<Version>1.0.2-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
+ <SHA1>4fd4dd023111fcbbdd1221032c2984d249f4cad1</SHA1>
<DownloadableArchives>content.7z</DownloadableArchives>
<Operations>
<Operation name="Mkdir">
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/createdesktopentryoperation/data/repository/Updates.xml b/tests/auto/installer/createdesktopentryoperation/data/repository/Updates.xml
index 77b5a9956..ad6c49c81 100644
--- a/tests/auto/installer/createdesktopentryoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/createdesktopentryoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>0746c8292e3799aac4a534a0a1a58d42ef24ed43</SHA1>
</PackageUpdate>
</Updates>
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/createshortcutoperation/data/repository/Updates.xml b/tests/auto/installer/createshortcutoperation/data/repository/Updates.xml
index 0826afae8..d1909d355 100644
--- a/tests/auto/installer/createshortcutoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/createshortcutoperation/data/repository/Updates.xml
@@ -5,7 +5,7 @@
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
- <Description>Example component A</Description>
+ <Description>Example component for CreateShortcutOperation</Description>
<Version>1.0.2-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
diff --git a/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp b/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp
index 313cdf677..a08567d64 100644
--- a/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp
+++ b/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp
@@ -53,10 +53,9 @@ private:
{
QInstaller::init();
QScopedPointer<PackageManagerCore> core(new PackageManagerCore(BinaryContent::MagicInstallerMarker,
- QList<OperationBlob> (), QString(), Protocol::DefaultAuthorizationKey,
+ QList<OperationBlob> (), QString(), QString(), Protocol::DefaultAuthorizationKey,
Protocol::Mode::Production, QHash<QString, QString>(), true));
- core->setAllowedRunningProcesses(QStringList() << QCoreApplication::applicationFilePath());
core->disableWriteMaintenanceTool();
core->setAutoConfirmCommand();
QSet<Repository> repoList;
@@ -70,7 +69,7 @@ private:
core->installDefaultComponentsSilently();
QSettingsWrapper user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Explorer\\User Shell Folders"), QSettingsWrapper::NativeFormat);
+ "CurrentVersion\\Explorer\\User Shell Folders"), QSettings::NativeFormat);
//Replace %USERS% from "Programs"
const QString programs = replaceWindowsEnvironmentVariables(user.value(QLatin1String("Programs"), QString()).toString());
const QString startMenuDir = programs + QDir::separator() + "Qt Installer Framework Unit Test";
diff --git a/tests/auto/installer/deleteoperation/data/repository/Updates.xml b/tests/auto/installer/deleteoperation/data/repository/Updates.xml
index 77b5a9956..5a8b28f38 100644
--- a/tests/auto/installer/deleteoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/deleteoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>e8b8ce98862d463c855609ed8e139eda17092cc6</SHA1>
</PackageUpdate>
</Updates>
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/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp b/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp
index aa2849559..ef185a5cf 100644
--- a/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp
+++ b/tests/auto/installer/elevatedexecuteoperation/tst_elevatedexecuteoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -46,12 +46,12 @@ private slots:
m_core.setValue(QLatin1String("QMAKE_BINARY"), QUOTE(QMAKE_BINARY));
m_core.setValue(QLatin1String("QMAKE_BINARY_OLD"), QLatin1String("FAKE_QMAKE"));
ElevatedExecuteOperation operation(&m_core);
- operation.setArguments(QStringList() << QLatin1String("UNDOEXECUTE") << QLatin1String("FAKE_QMAKE"));
+ operation.setArguments(QStringList() << QLatin1String("UNDOEXECUTE") << QLatin1String("FAKE_QMAKE") << QLatin1String("-v"));
- QTest::ignoreMessage(QtDebugMsg, "\"FAKE_QMAKE\" started, arguments: \"\"");
- QString message = "Failed to run undo operation \"Execute\" for component . Trying again with arguments %1";
+ QTest::ignoreMessage(QtDebugMsg, "\"FAKE_QMAKE\" started, arguments: \"-v\"");
+ QString message = "Failed to run undo operation \"Execute\" for component . Trying again with arguments %1, -v";
QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(QUOTE(QMAKE_BINARY))));
- message = "\"%1\" started, arguments: \"\"";
+ message = "\"%1\" started, arguments: \"-v\"";
QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(QUOTE(QMAKE_BINARY))));
QCOMPARE(operation.undoOperation(), true);
diff --git a/tests/auto/installer/environmentvariableoperation/data/repository/Updates.xml b/tests/auto/installer/environmentvariableoperation/data/repository/Updates.xml
index 6b1856d51..5bcd58c69 100644
--- a/tests/auto/installer/environmentvariableoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/environmentvariableoperation/data/repository/Updates.xml
@@ -10,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>4b7b52af2d838389a7404c553da74705fc106493</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/extractarchiveoperationtest/data.qrc b/tests/auto/installer/extractarchiveoperationtest/data.qrc
index 974c0c6f7..87f648568 100644
--- a/tests/auto/installer/extractarchiveoperationtest/data.qrc
+++ b/tests/auto/installer/extractarchiveoperationtest/data.qrc
@@ -2,6 +2,7 @@
<qresource prefix="/">
<file>data/valid.7z</file>
<file>data/invalid.7z</file>
+ <file>data/subdirs.7z</file>
<file>data/xmloperationrepository/Updates.xml</file>
<file>data/xmloperationrepository/A/1.0.0content.7z</file>
<file>data/xmloperationrepository/A/1.0.0content1.tar.gz</file>
@@ -10,5 +11,8 @@
<file>data/xmloperationrepository/A/1.0.0content4.zip</file>
<file>data/xmloperationrepository/A/1.0.0anothercontent.7z</file>
<file>data/xmloperationrepository/A/1.0.0default.7z</file>
+ <file>data/installerbaserepository/Updates.xml</file>
+ <file>data/installerbaserepository/A/1.0.0content.7z</file>
+ <file>data/installerbaserepository/A/1.0.0installerbase.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7z b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7z
new file mode 100644
index 000000000..585f58296
--- /dev/null
+++ b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7z
Binary files differ
diff --git a/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7z b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7z
new file mode 100644
index 000000000..c3b6aec9c
--- /dev/null
+++ b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7z
Binary files differ
diff --git a/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml
new file mode 100644
index 000000000..887300c97
--- /dev/null
+++ b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml
@@ -0,0 +1,13 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>InstallerBase</DisplayName>
+ <Description>Example component InstallerBase</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <DownloadableArchives>installerbase.7z,content.7z</DownloadableArchives>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/extractarchiveoperationtest/data/subdirs.7z b/tests/auto/installer/extractarchiveoperationtest/data/subdirs.7z
new file mode 100644
index 000000000..7f93fe106
--- /dev/null
+++ b/tests/auto/installer/extractarchiveoperationtest/data/subdirs.7z
Binary files differ
diff --git a/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp b/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp
index 8ceaa76c7..a8a65e983 100644
--- a/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp
+++ b/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp
@@ -28,6 +28,7 @@
#include "../shared/packagemanager.h"
+#include "concurrentoperationrunner.h"
#include "init.h"
#include "extractarchiveoperation.h"
@@ -86,6 +87,57 @@ private slots:
QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::UserDefinedError);
}
+ void testConcurrentExtractWithCompetingData()
+ {
+ // Suppress warnings about already deleted installerResources file
+ qInstallMessageHandler(silentTestMessageHandler);
+
+ const QString testDirectory = generateTemporaryFileName()
+ + "/subdir1/subdir2/subdir3/subdir4/subdir5/";
+
+ QStringList created7zList;
+
+ OperationList operations;
+ for (int i = 0; i < 100; ++i) {
+ ExtractArchiveOperation *op = new ExtractArchiveOperation(nullptr);
+ // We add the same data multiple times, and extract to same directory.
+ // Can't open the same archive multiple times however so it needs to
+ // be copied to unique files.
+ const QString new7zPath = generateTemporaryFileName() + ".7z";
+ QFile old7z(":///data/subdirs.7z");
+ QVERIFY(old7z.copy(new7zPath));
+
+ op->setArguments(QStringList() << new7zPath << testDirectory);
+ operations.append(op);
+ }
+ ConcurrentOperationRunner runner(&operations, Operation::Backup);
+
+ const QHash<Operation *, bool> backupResults = runner.run();
+ const OperationList backupOperations = backupResults.keys();
+
+ for (auto *operation : backupOperations)
+ QVERIFY2((backupResults.value(operation) && operation->error() == Operation::NoError),
+ operation->errorString().toLatin1());
+
+ runner.setType(Operation::Perform);
+ const QHash<Operation *, bool> results = runner.run();
+ const OperationList performedOperations = results.keys();
+
+ for (auto *operation : performedOperations)
+ QVERIFY2((results.value(operation) && operation->error() == Operation::NoError),
+ operation->errorString().toLatin1());
+
+ for (auto *operation : operations)
+ QVERIFY(operation->undoOperation());
+
+ qDeleteAll(operations);
+
+ for (const QString &archive : created7zList)
+ QFile::remove(archive);
+
+ QDir().rmdir(testDirectory);
+ }
+
void testExtractArchiveFromXML()
{
m_testDirectory = QInstaller::generateTemporaryFileName();
@@ -125,6 +177,39 @@ private slots:
QVERIFY(dir.removeRecursively());
}
+ void testInstallerBaseBinaryExtraction()
+ {
+ m_testDirectory = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(m_testDirectory));
+ QVERIFY(QDir(m_testDirectory).exists());
+
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit
+ (m_testDirectory, ":///data/installerbaserepository"));
+ core->setInstallerBaseBinary(m_testDirectory + QDir::separator() + "testInstallerBase.txt");
+ core->installDefaultComponentsSilently();
+
+ QFile contentResourceFile(m_testDirectory + QDir::separator() + "installerResources" + QDir::separator() + "A" + QDir::separator() + "1.0.0content.txt");
+ QVERIFY2(contentResourceFile.open(QIODevice::ReadOnly | QIODevice::Text), "Could not open content resource file for reading.");
+ QTextStream fileStream(&contentResourceFile);
+ QString line = fileStream.readLine();
+ QVERIFY2(!line.isEmpty(), "Content not written to resource file.");
+ contentResourceFile.close();
+
+ QFile installerBaseResourceFile(m_testDirectory + QDir::separator() + "installerResources" + QDir::separator() + "A" + QDir::separator() + "1.0.0installerbase.txt");
+ QVERIFY2(installerBaseResourceFile.open(QIODevice::ReadOnly | QIODevice::Text), "Could not open installerbase resource file for reading.");
+ QTextStream fileStream2(&installerBaseResourceFile);
+ line = installerBaseResourceFile.readLine();
+ QVERIFY2(line.isEmpty(), "Installerbase falsly written to resource file.");
+ installerBaseResourceFile.close();
+
+ core->setPackageManager();
+ core->commitSessionOperations();
+
+ QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList() << "A"));
+ QDir dir(m_testDirectory);
+ QVERIFY(dir.removeRecursively());
+ }
+
private:
QString m_testDirectory;
};
diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro
index eb5fa6d2e..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 \
@@ -25,10 +26,11 @@ SUBDIRS += \
factory \
replaceoperation \
brokeninstaller \
- cliinterface \
+ commandlineinstall \
linereplaceoperation \
metadatajob \
appendfileoperation \
+ prependfileoperation \
simplemovefileoperation \
deleteoperation \
copydirectoryoperation \
@@ -41,7 +43,10 @@ SUBDIRS += \
treename \
createoffline \
contentshaupdate \
- componentreplace
+ componentreplace \
+ metadatacache \
+ contentsha1check \
+ componentalias
CONFIG(libarchive) {
SUBDIRS += libarchivearchive
diff --git a/tests/auto/installer/installiconsoperation/data/repository/Updates.xml b/tests/auto/installer/installiconsoperation/data/repository/Updates.xml
index a1c8f6aa2..201e23c2d 100644
--- a/tests/auto/installer/installiconsoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/installiconsoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -10,5 +11,6 @@
<Default>true</Default>
<Script>script.qs</Script>
<DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>13c9e7e67c26e7fbf49cc30887d87140b65e11b5</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/licenseagreement/data/repository/Updates.xml b/tests/auto/installer/licenseagreement/data/repository/Updates.xml
index 2afd2a741..c57918162 100644
--- a/tests/auto/installer/licenseagreement/data/repository/Updates.xml
+++ b/tests/auto/installer/licenseagreement/data/repository/Updates.xml
@@ -9,6 +9,7 @@
<Version>1.0.2-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
+ <SHA1>f46c677db8bc779d70d0c72fae264a321caea6f8</SHA1>
<Licenses>
<License name="GNU GENERAL PUBLIC LICENSE Version 3" file="gpl3.txt"/>
</Licenses>
diff --git a/tests/auto/installer/messageboxhandler/data/invalidoperation/Updates.xml b/tests/auto/installer/messageboxhandler/data/invalidoperation/Updates.xml
index 77b5a9956..f92211497 100644
--- a/tests/auto/installer/messageboxhandler/data/invalidoperation/Updates.xml
+++ b/tests/auto/installer/messageboxhandler/data/invalidoperation/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>c8eb49045188859100716ab084452b32fca38d5d</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/messageboxhandler/data/messagebox/Updates.xml b/tests/auto/installer/messageboxhandler/data/messagebox/Updates.xml
index 77b5a9956..0781dcfc5 100644
--- a/tests/auto/installer/messageboxhandler/data/messagebox/Updates.xml
+++ b/tests/auto/installer/messageboxhandler/data/messagebox/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>ad3157ed059e3369c094e154319de1d255865de5</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp b/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp
index 464e1eef7..5db79cc55 100644
--- a/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp
+++ b/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp
@@ -93,9 +93,8 @@ private slots:
QInstaller::init(); //This will eat debug output
core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> (),
- QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production,
+ QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production,
QHash<QString, QString>(), true);
- core->setAllowedRunningProcesses(QStringList() << QCoreApplication::applicationFilePath());
core->disableWriteMaintenanceTool();
core->setAutoConfirmCommand();
m_installDir = QInstaller::generateTemporaryFileName();
@@ -196,7 +195,7 @@ private slots:
setRepository(":///data/missingarchive");
core->autoAcceptMessageBoxes();
core->installSelectedComponentsSilently(QStringList () << "C");
- QCOMPARE(PackageManagerCore::Canceled, core->status());
+ QCOMPARE(PackageManagerCore::Failure, core->status()); // Fails after retrying
}
void messageBoxFromScriptDefaultAnswer()
diff --git a/tests/auto/installer/metadatacache/data/existing-cache/manifest.json b/tests/auto/installer/metadatacache/data/existing-cache/manifest.json
new file mode 100644
index 000000000..7f30e0fc0
--- /dev/null
+++ b/tests/auto/installer/metadatacache/data/existing-cache/manifest.json
@@ -0,0 +1,7 @@
+{
+ "items": [
+ "placeholder_sha1"
+ ],
+ "type": "Metadata",
+ "version": "1.2.0"
+}
diff --git a/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml
new file mode 100644
index 000000000..b0b30c9d4
--- /dev/null
+++ b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml
@@ -0,0 +1,19 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>true</Checksum>
+ <RepositoryUpdate>
+ <Repository action="remove" url="../repository"/>
+ </RepositoryUpdate>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.0-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <UpdateFile CompressedSize="222" OS="Any" UncompressedSize="72"/>
+ <DownloadableArchives>content.7z</DownloadableArchives>
+ <SHA1>5b3939da1af492382c68388fc796837e4c36b876</SHA1>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt
new file mode 100644
index 000000000..7224edd52
--- /dev/null
+++ b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt
@@ -0,0 +1 @@
+/example-repository
diff --git a/tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt b/tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt
new file mode 100644
index 000000000..1ffdcc8df
--- /dev/null
+++ b/tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt
@@ -0,0 +1 @@
+Example license file
diff --git a/tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml b/tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml
new file mode 100644
index 000000000..03e54572d
--- /dev/null
+++ b/tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml
@@ -0,0 +1,17 @@
+<Updates>
+ <ApplicationName>{AnyApplication}</ApplicationName>
+ <ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
+ <PackageUpdate>
+ <Name>A</Name>
+ <DisplayName>A</DisplayName>
+ <Description>Example component A</Description>
+ <Version>1.0.2-1</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>true</Default>
+ <SHA1>f46c677db8bc779d70d0c72fae264a321caea6f8</SHA1>
+ <Licenses>
+ <License name="Example license" file="example-license.txt"/>
+ </Licenses>
+ </PackageUpdate>
+</Updates>
diff --git a/tests/auto/installer/metadatacache/metadatacache.pro b/tests/auto/installer/metadatacache/metadatacache.pro
new file mode 100644
index 000000000..df1542dc7
--- /dev/null
+++ b/tests/auto/installer/metadatacache/metadatacache.pro
@@ -0,0 +1,8 @@
+include(../../qttest.pri)
+
+QT += qml
+
+SOURCES += tst_metadatacache.cpp
+
+RESOURCES += \
+ settings.qrc
diff --git a/tests/auto/installer/metadatacache/settings.qrc b/tests/auto/installer/metadatacache/settings.qrc
new file mode 100644
index 000000000..96383fdcb
--- /dev/null
+++ b/tests/auto/installer/metadatacache/settings.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/local-temp-repository/Updates.xml</file>
+ <file>data/local-temp-repository/A/example-license.txt</file>
+ <file>data/existing-cache/manifest.json</file>
+ <file>data/existing-cache/placeholder_sha1/repository.txt</file>
+ <file>data/existing-cache/placeholder_sha1/Updates.xml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/installer/metadatacache/tst_metadatacache.cpp b/tests/auto/installer/metadatacache/tst_metadatacache.cpp
new file mode 100644
index 000000000..ce97de9da
--- /dev/null
+++ b/tests/auto/installer/metadatacache/tst_metadatacache.cpp
@@ -0,0 +1,381 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 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 <errors.h>
+#include <fileutils.h>
+#include <metadatacache.h>
+#include <metadata.h>
+#include <repository.h>
+
+#include <QCryptographicHash>
+#include <QDir>
+#include <QFileInfo>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QObject>
+#include <QTest>
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+using namespace QInstaller;
+
+static const QByteArray scPlaceholderSha1("placeholder_sha1");
+
+class tst_metadatacache : public QObject
+{
+ Q_OBJECT
+
+private:
+ void copyExistingCacheFromResourceTree()
+ {
+ try {
+ QInstaller::copyDirectoryContents(":/data/existing-cache/", m_cachePath);
+
+ // We need to modify the test data here because the checksums of
+ // files may differ on Windows and Unix platforms.
+ QVERIFY(QDir().rename(m_cachePath + QDir::separator() + scPlaceholderSha1,
+ m_cachePath + QDir::separator() + m_oldMetadataItemChecksum));
+
+ QFile manifestFile(m_cachePath + QDir::separator() + "manifest.json");
+ // The file lost the write bit after copying from resource
+ QInstaller::setDefaultFilePermissions(&manifestFile, QInstaller::NonExecutable);
+ QVERIFY2(manifestFile.open(QIODevice::ReadWrite), qPrintable(manifestFile.errorString()));
+
+ const QByteArray manifestData = manifestFile.readAll();
+ QJsonDocument manifestJsonDoc(QJsonDocument::fromJson(manifestData));
+ QJsonObject docJsonObject = manifestJsonDoc.object();
+
+ QJsonArray itemsJsonArray;
+ itemsJsonArray.append(QJsonValue(QLatin1String(m_oldMetadataItemChecksum)));
+
+ docJsonObject.insert(QLatin1String("items"), itemsJsonArray);
+ manifestJsonDoc.setObject(docJsonObject);
+
+ manifestFile.seek(0);
+ QVERIFY(manifestFile.write(manifestJsonDoc.toJson()) != -1);
+
+ } catch (const Error &e) {
+ QVERIFY2(false, QString::fromLatin1("Error while copying item to path %1: %2")
+ .arg(m_cachePath, e.message()).toLatin1());
+ }
+ }
+
+ QStringList itemsFromManifest(const QString &manifestPath)
+ {
+ QFile manifestFile(manifestPath);
+ if (!manifestFile.open(QIODevice::ReadOnly))
+ return QStringList();
+
+ const QByteArray manifestData = manifestFile.readAll();
+ const QJsonDocument manifestJsonDoc(QJsonDocument::fromJson(manifestData));
+ const QJsonObject docJsonObject = manifestJsonDoc.object();
+ const QJsonArray itemsJsonArray = docJsonObject.value(QLatin1String("items")).toArray();
+
+ QStringList items;
+ for (const auto &itemJsonValue : itemsJsonArray)
+ items << itemJsonValue.toString();
+
+ return items;
+ }
+
+ QByteArray checksumFromUpdateFile(const QString &directory)
+ {
+ QFile updateFile(directory + QDir::separator() + QLatin1String("Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly))
+ return QByteArray();
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&updateFile);
+ return hash.result().toHex();
+ }
+
+private slots:
+ void init()
+ {
+ m_cachePath = generateTemporaryFileName();
+ }
+
+ void cleanup()
+ {
+ if (QFileInfo::exists(m_cachePath))
+ QInstaller::removeDirectory(m_cachePath, true);
+ }
+
+ void initTestCase()
+ {
+ m_newMetadataItemChecksum = checksumFromUpdateFile(":/data/local-temp-repository");
+ m_oldMetadataItemChecksum = checksumFromUpdateFile(":/data/existing-cache/"
+ + QLatin1String(scPlaceholderSha1));
+
+ QVERIFY(!m_newMetadataItemChecksum.isEmpty());
+ QVERIFY(!m_oldMetadataItemChecksum.isEmpty());
+
+ qInstallMessageHandler(silentTestMessageHandler);
+ }
+
+ void testRegisterItemToEmptyCache()
+ {
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+
+ QVERIFY(cache.registerItem(metadata));
+ metadata = cache.itemByChecksum(m_newMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+ QVERIFY(!QFileInfo::exists(m_cachePath + "/manifest.json"));
+ QVERIFY(cache.sync());
+ QVERIFY(itemsFromManifest(m_cachePath + "/manifest.json").contains(QLatin1String(m_newMetadataItemChecksum)));
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testRegisterItemToExistingCache()
+ {
+ copyExistingCacheFromResourceTree();
+
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+ QVERIFY(itemsFromManifest(m_cachePath + "/manifest.json").contains(QLatin1String(m_oldMetadataItemChecksum)));
+
+ QVERIFY(cache.registerItem(metadata));
+ metadata = cache.itemByChecksum(m_newMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+ QVERIFY(cache.sync());
+ const QStringList manifestItems = itemsFromManifest(m_cachePath + "/manifest.json");
+ QVERIFY(manifestItems.contains(QLatin1String(m_oldMetadataItemChecksum)));
+ QVERIFY(manifestItems.contains(QLatin1String(m_newMetadataItemChecksum)));
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testRegisterItemFails()
+ {
+ // 1. Test fail due to invalidated cache
+ MetadataCache cache;
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+
+ QVERIFY(!cache.registerItem(metadata));
+ QCOMPARE(cache.errorString(), "Cannot register item to invalidated cache.");
+
+ delete metadata;
+ metadata = nullptr;
+
+ // 2. Test fail due to null metadata
+ cache.setPath(m_cachePath);
+ cache.setType("Metadata");
+ cache.setVersion(QUOTE(IFW_CACHE_FORMAT_VERSION));
+ QVERIFY(cache.initialize());
+
+ QVERIFY(!cache.registerItem(metadata));
+ QCOMPARE(cache.errorString(), "Cannot register null item.");
+
+ // 3. Test fail due to invalid metadata
+ metadata = new Metadata;
+ QVERIFY(!cache.registerItem(metadata));
+ QCOMPARE(cache.errorString(), "Cannot register invalid item with checksum ");
+
+ // 4. Test fail due to duplicate metadata item
+ metadata->setPath(":/data/local-temp-repository/");
+ QVERIFY(cache.registerItem(metadata));
+ QVERIFY(cache.itemByChecksum(m_newMetadataItemChecksum)->isValid());
+ QVERIFY(!cache.registerItem(metadata));
+ QCOMPARE(cache.errorString(), QString::fromLatin1("Cannot register item with checksum "
+ "%1. An item with the same checksum already exists in cache.")
+ .arg(QString::fromLatin1(m_newMetadataItemChecksum)).toLatin1());
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testInitializeExistingCache()
+ {
+ copyExistingCacheFromResourceTree();
+
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = cache.itemByChecksum(m_oldMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testInitializeForeignCache_data()
+ {
+ QTest::addColumn<QString>("type");
+ QTest::addColumn<QString>("version");
+
+ QTest::newRow("Type mismatch") << "MyCacheableType" << QUOTE(IFW_CACHE_FORMAT_VERSION);
+ QTest::newRow("Version mismatch") << "Metadata" << "0.9.1";
+ }
+
+ void testInitializeForeignCache()
+ {
+ QFETCH(QString, type);
+ QFETCH(QString, version);
+
+ copyExistingCacheFromResourceTree();
+
+ MetadataCache cache;
+ cache.setType(type);
+ cache.setVersion(version);
+ cache.setPath(m_cachePath);
+ QVERIFY(cache.initialize());
+
+ QVERIFY(cache.isValid());
+ QVERIFY(!cache.itemByChecksum(m_oldMetadataItemChecksum));
+
+ QVERIFY(cache.clear());
+ // The 'foreign' entry prevents removing the directory
+ QVERIFY(QFileInfo::exists(m_cachePath));
+ }
+
+ void testInitializeCacheFails()
+ {
+ MetadataCache cache;
+ QVERIFY(!cache.initialize());
+ QCOMPARE(cache.errorString(), "Cannot initialize cache with empty path.");
+ }
+
+ void testRemoveItemFromCache()
+ {
+ copyExistingCacheFromResourceTree();
+
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = cache.itemByChecksum(m_oldMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+ QVERIFY(cache.removeItem(m_oldMetadataItemChecksum));
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testRemoveItemFails()
+ {
+ copyExistingCacheFromResourceTree();
+
+ MetadataCache cache(m_cachePath);
+ QVERIFY(!cache.removeItem("12345"));
+ QCOMPARE(cache.errorString(), "Cannot remove item specified by checksum 12345: no such item exists.");
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testRetrieveItemFromCache()
+ {
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+
+ QVERIFY(cache.registerItem(metadata));
+ metadata = cache.itemByChecksum(m_newMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+
+ metadata = cache.itemByPath(metadata->path());
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testRetrieveItemFails()
+ {
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+ const QString metadataPath = metadata->path();
+
+ QVERIFY(cache.registerItem(metadata));
+ QVERIFY(cache.clear());
+
+ QVERIFY(!cache.itemByChecksum(m_newMetadataItemChecksum));
+ QVERIFY(!cache.itemByPath(metadataPath));
+ QCOMPARE(cache.errorString(), "Cannot retrieve item from invalidated cache.");
+
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testItemObsoletesOther()
+ {
+ copyExistingCacheFromResourceTree();
+
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+
+ QVERIFY(cache.registerItem(metadata));
+ metadata->setRepository(Repository(QUrl("file:///example-repository"), true));
+ metadata->setPersistentRepositoryPath(QUrl("file:///example-repository"));
+ QVERIFY(metadata->isActive());
+
+ metadata = cache.itemByChecksum(m_newMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+
+ metadata = cache.itemByChecksum(m_oldMetadataItemChecksum);
+ QVERIFY(metadata);
+ QVERIFY(metadata->isValid());
+
+ Metadata *obsolete = cache.obsoleteItems().first();
+ QVERIFY(!obsolete->isActive());
+ QCOMPARE(obsolete->checksum(), m_oldMetadataItemChecksum);
+
+ QVERIFY(cache.clear());
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+ void testClearCacheFails()
+ {
+ MetadataCache cache(m_cachePath);
+ Metadata *metadata = new Metadata(":/data/local-temp-repository/");
+
+ QVERIFY(cache.registerItem(metadata));
+ QVERIFY(cache.clear());
+ QVERIFY(!cache.clear());
+ QCOMPARE(cache.errorString(), "Cannot clear invalidated cache.");
+
+ QVERIFY(!QFileInfo::exists(m_cachePath));
+ }
+
+private:
+ QString m_cachePath;
+ QByteArray m_newMetadataItemChecksum;
+ QByteArray m_oldMetadataItemChecksum;
+};
+
+QTEST_MAIN(tst_metadatacache)
+
+#include "tst_metadatacache.moc"
diff --git a/tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z b/tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z
new file mode 100644
index 000000000..df1f72b51
--- /dev/null
+++ b/tests/auto/installer/metadatajob/data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z
Binary files differ
diff --git a/tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7z b/tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7z
new file mode 100644
index 000000000..6afaae497
--- /dev/null
+++ b/tests/auto/installer/metadatajob/data/repositoryZipped/repositoryZipped.7z
Binary files differ
diff --git a/tests/auto/installer/metadatajob/settings.qrc b/tests/auto/installer/metadatajob/settings.qrc
index 6e56a7854..5df3befd9 100644
--- a/tests/auto/installer/metadatajob/settings.qrc
+++ b/tests/auto/installer/metadatajob/settings.qrc
@@ -3,5 +3,7 @@
<file>data/repository/Updates.xml</file>
<file>data/repositoryActionAdd/Updates.xml</file>
<file>data/repositoryActionRemove/Updates.xml</file>
+ <file>data/repositoryZipped/repositoryZipped.7z</file>
+ <file>data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/metadatajob/tst_metadatajob.cpp b/tests/auto/installer/metadatajob/tst_metadatajob.cpp
index 54609ab95..cb974e7ad 100644
--- a/tests/auto/installer/metadatajob/tst_metadatajob.cpp
+++ b/tests/auto/installer/metadatajob/tst_metadatajob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -97,6 +97,49 @@ private slots:
metadata.waitForFinished();
QCOMPARE(metadata.metadata().count(), 1);
}
+
+ void testZippedRepository_data()
+ {
+ QTest::addColumn<QStringList>("repositories");
+ QTest::addColumn<int>("metacount");
+ QTest::addColumn<bool>("allowUnstable");
+
+ QStringList repositories;
+ repositories << ":///data/repositoryZipped/repositoryZipped.7z";
+ QTest::newRow("7z repository") << repositories << 1 << true;
+
+ repositories.clear();
+ repositories << ":///data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z";
+ QTest::newRow("7z with invalid meta sha1") << repositories << 0 << true;
+
+ repositories.clear();
+ repositories << ":///data/repositoryZipped/repositoryZipped.7z" << ":///data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z";
+ QTest::newRow("7z with one valid repository") << repositories << 1 << true;
+
+ repositories.clear();
+ repositories << ":///data/repository7zInvalidMetaSha1/repository7zInvalidMetaSha1.7z" << ":///data/repositoryZipped/repositoryZipped.7z";
+ QTest::newRow("7z with one valid repository") << repositories << 0 << false;
+ }
+
+ void testZippedRepository()
+ {
+ QFETCH(QStringList, repositories);
+ QFETCH(int, metacount);
+ QFETCH(bool, allowUnstable);
+
+ PackageManagerCore core;
+ core.setInstaller();
+ core.setTemporaryRepositories(repositories, false, true);
+ core.settings().setAllowUnstableComponents(allowUnstable);
+
+ MetadataJob metadata;
+ metadata.setPackageManagerCore(&core);
+ metadata.addDownloadType(DownloadType::CompressedPackage);
+ metadata.setAutoDelete(true);
+ metadata.start();
+ metadata.waitForFinished();
+ QCOMPARE(metadata.metadata().count(), metacount);
+ }
};
diff --git a/tests/auto/installer/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/data/repository/Updates.xml b/tests/auto/installer/moveoperation/data/repository/Updates.xml
index 6b1856d51..5cdad4e45 100644
--- a/tests/auto/installer/moveoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/moveoperation/data/repository/Updates.xml
@@ -10,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>badc75810d399a35bae6f6b2cd8acfc1d5b1ccd2</SHA1>
</PackageUpdate>
</Updates>
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/packagemanagercore/tst_packagemanagercore.cpp b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
index aa74d8dbe..410bfb01c 100644
--- a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
+++ b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -260,7 +260,6 @@ private slots:
root->setInstalled();
child1->setInstalled();
child2->setUninstalled();
- core.componentsToInstallNeedsRecalculation();
core.calculateComponentsToInstall();
QCOMPARE(core.requiredDiskSpace(), 250ULL);
}
@@ -288,58 +287,12 @@ private slots:
QVERIFY(QDir().rmdir(testDirectory));
}
- void testAllowRunningProcess()
- {
- #ifdef Q_OS_MACOS
- QSKIP("In macOS the app path and maintenancetool differ, not possible to test running processes.");
- #endif
- PackageManagerCore core;
- core.setPackageManager();
- const QString testDirectory = QInstaller::generateTemporaryFileName();
- QVERIFY(QDir().mkpath(testDirectory));
- core.setValue(scTargetDir, testDirectory);
-
- QString appFilePath = QCoreApplication::applicationFilePath();
- core.setAllowedRunningProcesses(QStringList() << appFilePath);
- const QString warningMessage = QString("Failure to read packages from ");
- const QRegularExpression re(warningMessage);
- QTest::ignoreMessage(QtWarningMsg, re);
- QTest::ignoreMessage(QtDebugMsg, "No updates available.");
-
- QCOMPARE(PackageManagerCore::Canceled, core.updateComponentsSilently(QStringList()));
- QVERIFY(QDir().rmdir(testDirectory));
- }
-
- void testDisallowRunningProcess()
- {
- #ifdef Q_OS_MACOS
- QSKIP("In macOS the app path and maintenancetool differ, not possible to test running processes.");
- #endif
- PackageManagerCore core;
- core.setPackageManager();
- const QString testDirectory = QInstaller::generateTemporaryFileName();
- QVERIFY(QDir().mkpath(testDirectory));
- core.setValue(scTargetDir, testDirectory);
-
- const QString warningMessageUp = QString("Unable to update components. Please stop these processes: ");
- const QRegularExpression reUp(warningMessageUp);
- QTest::ignoreMessage(QtWarningMsg, reUp);
- QVERIFY_EXCEPTION_THROWN(core.updateComponentsSilently(QStringList()), Error);
-
- const QString warningMessageRm = QString("Unable to remove components. Please stop these processes: ");
- const QRegularExpression reRm(warningMessageRm);
- QTest::ignoreMessage(QtWarningMsg, reRm);
- QVERIFY_EXCEPTION_THROWN(core.removeInstallationSilently(), Error);
-
- QVERIFY(QDir().rmdir(testDirectory));
- }
-
void testCoreDataValues()
{
QHash<QString, QString> userValues;
PackageManagerCore *core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> (),
- QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production,
+ QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production,
userValues, true);
QCOMPARE(core->value("AllUsers"), QLatin1String(""));
QCOMPARE(core->value("ProductName"), QLatin1String("Unit Test Application"));
@@ -361,7 +314,7 @@ private slots:
userValues.insert("RootDir", "Overwritten RootDir");
PackageManagerCore *core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> (),
- QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production,
+ QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production,
userValues, true);
QCOMPARE(core->value("AllUsers"), QLatin1String("true"));
QCOMPARE(core->value("ProductName"), QLatin1String("Overwritten ProductName"));
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/registerfiletypeoperation/data/repository/Updates.xml b/tests/auto/installer/registerfiletypeoperation/data/repository/Updates.xml
index 0826afae8..6435ae0f7 100644
--- a/tests/auto/installer/registerfiletypeoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/registerfiletypeoperation/data/repository/Updates.xml
@@ -5,7 +5,7 @@
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
- <Description>Example component A</Description>
+ <Description>Example component for RegisterFileTypeOperation</Description>
<Version>1.0.2-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
diff --git a/tests/auto/installer/replaceoperation/data/repository/Updates.xml b/tests/auto/installer/replaceoperation/data/repository/Updates.xml
index a0ade298c..7585b57a4 100644
--- a/tests/auto/installer/replaceoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/replaceoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,6 +10,7 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>edb7672270729f9d34396cb70e6afd12fec90d2b</SHA1>
<UpdateFile CompressedSize="225" UncompressedSize="75" OS="Any"/>
<DownloadableArchives>content.7z</DownloadableArchives>
</PackageUpdate>
diff --git a/tests/auto/installer/repository/data/compressedRepository/compressedRepository.7z b/tests/auto/installer/repository/data/compressedRepository/compressedRepository.7z
index fa75f2979..335685bb0 100644
--- a/tests/auto/installer/repository/data/compressedRepository/compressedRepository.7z
+++ b/tests/auto/installer/repository/data/compressedRepository/compressedRepository.7z
Binary files differ
diff --git a/tests/auto/installer/repository/data/repository/A/1.0.2meta.7z b/tests/auto/installer/repository/data/repository/A/1.0.2meta.7z
new file mode 100644
index 000000000..6631280a7
--- /dev/null
+++ b/tests/auto/installer/repository/data/repository/A/1.0.2meta.7z
Binary files differ
diff --git a/tests/auto/installer/repository/data/repository/B/1.0.0meta.7z b/tests/auto/installer/repository/data/repository/B/1.0.0meta.7z
new file mode 100644
index 000000000..16f3a6c42
--- /dev/null
+++ b/tests/auto/installer/repository/data/repository/B/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/repository/data/repository/C/1.0.0meta.7z b/tests/auto/installer/repository/data/repository/C/1.0.0meta.7z
new file mode 100644
index 000000000..37c8fffc4
--- /dev/null
+++ b/tests/auto/installer/repository/data/repository/C/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/repository/data/repository/Updates.xml b/tests/auto/installer/repository/data/repository/Updates.xml
index 6fdfec9e5..f541882c5 100644
--- a/tests/auto/installer/repository/data/repository/Updates.xml
+++ b/tests/auto/installer/repository/data/repository/Updates.xml
@@ -1,13 +1,33 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
<Description>Example component A</Description>
<Version>1.0.2</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
- <Default>true</Default>
+ <Default>false</Default>
<DownloadableArchives>content.7z</DownloadableArchives>
+ <Script postLoad="true">script.qs</Script>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
+ <Script>script.qs</Script>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
+ <Script postLoad="false">script.qs</Script>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/repository/settings.qrc b/tests/auto/installer/repository/settings.qrc
index ff628b482..92db7e3e1 100644
--- a/tests/auto/installer/repository/settings.qrc
+++ b/tests/auto/installer/repository/settings.qrc
@@ -2,6 +2,9 @@
<qresource prefix="/">
<file>data/repository/Updates.xml</file>
<file>data/repository/A/1.0.2content.7z</file>
+ <file>data/repository/A/1.0.2meta.7z</file>
+ <file>data/repository/B/1.0.0meta.7z</file>
+ <file>data/repository/C/1.0.0meta.7z</file>
<file>data/compressedRepository/compressedRepository.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/repository/tst_repository.cpp b/tests/auto/installer/repository/tst_repository.cpp
index db538ba5d..112098130 100644
--- a/tests/auto/installer/repository/tst_repository.cpp
+++ b/tests/auto/installer/repository/tst_repository.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,6 +39,8 @@
using namespace QInstaller;
+typedef QList<QPair<QString, QString>> SettingsPairList;
+
class tst_Repository : public QObject
{
Q_OBJECT
@@ -168,7 +170,7 @@ private slots:
categories.insert(category);
settings.setRepositoryCategories(categories);
- QHash<QString, QPair<Repository, Repository>> update;
+ QMultiHash<QString, QPair<Repository, Repository>> update;
// non-empty update
update.insert(QLatin1String("replace"), qMakePair(original, replacement));
@@ -210,6 +212,102 @@ private slots:
QVERIFY(dir.removeRecursively());
core->deleteLater();
}
+
+ void testPostLoadScriptFromRepository_data()
+ {
+ QTest::addColumn<QStringList>("installComponent");
+ QTest::addColumn<bool>("repositoryPostLoadSetting");
+ QTest::addColumn<SettingsPairList>("expectedSettingsBeforeInstall");
+ QTest::addColumn<SettingsPairList>("expectedSettingsAfterInstall");
+ QTest::addColumn<QStringList>("unexpectedSettings");
+
+ // component A has postLoad = true in component.xml
+ // component B has no postLoad attribute
+ // component C has postLoad = false in component.xml
+
+ SettingsPairList expectedSettingsListAfterInstall;
+ expectedSettingsListAfterInstall.append(QPair<QString, QString>("componentAKey", "componentAValue"));
+
+ SettingsPairList expectedSettingsListBeforeInstall;
+ expectedSettingsListBeforeInstall.append(QPair<QString, QString>("componentBKey", "componentBValue"));
+ expectedSettingsListBeforeInstall.append(QPair<QString, QString>("componentCKey", "componentCValue"));
+
+ QTest::newRow("noRepoPostLoadComponentA")
+ << (QStringList() << "A")
+ << false
+ << expectedSettingsListBeforeInstall
+ << expectedSettingsListAfterInstall
+ << (QStringList());
+
+ // Component B is installed so values from component A and component C should not be set
+ expectedSettingsListAfterInstall.clear();
+ expectedSettingsListAfterInstall.append(QPair<QString, QString>("componentBKey", "componentBValue"));
+ QTest::newRow("noRepoPostLoadComponentB")
+ << (QStringList() << "B")
+ << false
+ << expectedSettingsListBeforeInstall
+ << expectedSettingsListAfterInstall
+ << (QStringList() << "componentAValue" << "componentCValue");
+
+ // PostLoad is set to whole repository. Since only A is installed,
+ // values from component B and component C values are not set
+ expectedSettingsListBeforeInstall.clear();
+ expectedSettingsListAfterInstall.clear();
+ expectedSettingsListAfterInstall.append(QPair<QString, QString>("componentAKey", "componentAValue"));
+ QTest::newRow("repoPostLoadComponentA") << (QStringList() << "A")
+ << true
+ << expectedSettingsListBeforeInstall
+ << expectedSettingsListAfterInstall
+ << (QStringList() << "componentBValue" << "componentCValue");
+
+ // PostLoad is set to whole repository. Since only B is installed,
+ // values from component C and component A are not set.
+ expectedSettingsListAfterInstall.clear();
+ expectedSettingsListAfterInstall.append(QPair<QString, QString>("componentBKey", "componentBValue"));
+ QTest::newRow("repoPostLoadComponentB") << (QStringList() << "B")
+ << true
+ << expectedSettingsListBeforeInstall
+ << expectedSettingsListAfterInstall
+ << (QStringList() << "componentCValue" << "componentAValue");
+
+ // PostLoad is set to whole repository. Since component C has its postload = false,
+ // value is set to component C before install
+ expectedSettingsListBeforeInstall.clear();
+ expectedSettingsListAfterInstall.clear();
+ expectedSettingsListBeforeInstall.append(QPair<QString, QString>("componentCKey", "componentCValue"));
+ QTest::newRow("repoPostLoadComponentC") << (QStringList() << "C")
+ << true
+ << expectedSettingsListBeforeInstall
+ << expectedSettingsListAfterInstall
+ << (QStringList() << "componentAValue" << "componentBValue");
+ }
+
+ void testPostLoadScriptFromRepository()
+ {
+ QFETCH(QStringList, installComponent);
+ QFETCH(bool, repositoryPostLoadSetting);
+ QFETCH(SettingsPairList, expectedSettingsBeforeInstall);
+ QFETCH(SettingsPairList, expectedSettingsAfterInstall);
+ QFETCH(QStringList, unexpectedSettings);
+
+ QString installDir = QInstaller::generateTemporaryFileName();
+ QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit(installDir));
+ QSet<Repository> repoList;
+ Repository repo = Repository::fromUserInput(":///data/repository");
+ repo.setPostLoadComponentScript(repositoryPostLoadSetting);
+ repoList.insert(repo);
+ core->settings().setDefaultRepositories(repoList);
+ QVERIFY(core->fetchRemotePackagesTree());
+
+ for (const QPair<QString, QString> settingValue : expectedSettingsBeforeInstall)
+ QCOMPARE(core->value(settingValue.first), settingValue.second);
+
+ core->installSelectedComponentsSilently(installComponent);
+ for (const QPair<QString, QString> settingValue : expectedSettingsAfterInstall)
+ QCOMPARE(core->value(settingValue.first), settingValue.second);
+ for (const QString unexpectedSetting : unexpectedSettings)
+ QVERIFY2(!core->containsValue(unexpectedSetting), "Core contains unexpected value");
+ }
};
QTEST_MAIN(tst_Repository)
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"
diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
index 105bcf5d7..86017a229 100644
--- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp
+++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -221,13 +221,6 @@ private slots:
.hasProperty(QLatin1String("getExistingDirectory")), true);
QCOMPARE(global.property(QLatin1String("QFileDialog"))
.hasProperty(QLatin1String("getOpenFileName")), true);
-
- QCOMPARE(global.hasProperty(QLatin1String("InstallerProxy")), true);
- QCOMPARE(global.property(QLatin1String("InstallerProxy"))
- .hasProperty(QLatin1String("componentByName")), true);
- QCOMPARE(global.property(QLatin1String("InstallerProxy"))
- .hasProperty(QLatin1String("components")), true);
-
QCOMPARE(global.hasProperty(QLatin1String("QDesktopServices")), true);
QCOMPARE(global.property(QLatin1String("QDesktopServices"))
.hasProperty(QLatin1String("openUrl")), true);
@@ -249,6 +242,8 @@ private slots:
QJSValue sinfo = global.property(QLatin1String("systemInfo"));
QCOMPARE(sinfo.property(QLatin1String("currentCpuArchitecture")).toString(),
QSysInfo::currentCpuArchitecture());
+ QCOMPARE(sinfo.property(QLatin1String("buildCpuArchitecture")).toString(),
+ QSysInfo::buildCpuArchitecture());
QCOMPARE(sinfo.property(QLatin1String("kernelType")).toString(), QSysInfo::kernelType());
QCOMPARE(sinfo.property(QLatin1String("kernelVersion")).toString(),
QSysInfo::kernelVersion());
@@ -388,13 +383,24 @@ private slots:
QCOMPARE(result.isError(), false);
}
+ void loadSimpleComponentScript_data()
+ {
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("postLoad");
+ QTest::newRow("Pre component script") << ":///data/component1.qs" << false;
+ QTest::newRow("Post component script") << ":///data/component1.qs" << true;
+ }
+
void loadSimpleComponentScript()
{
- try {
- // ignore retranslateUi which is called by loadComponentScript
+ QFETCH(QString, path);
+ QFETCH(bool, postLoad);
+
+ try {
+ // ignore retranslateUi which is called by evaluateComponentScript
setExpectedScriptOutput("Component constructor - OK");
setExpectedScriptOutput("retranslateUi - OK");
- m_component->loadComponentScript(":///data/component1.qs");
+ m_component->evaluateComponentScript(path, postLoad);
setExpectedScriptOutput("retranslateUi - OK");
m_component->languageChanged();
@@ -422,8 +428,19 @@ private slots:
}
}
+ void loadBrokenComponentScript_data()
+ {
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("postLoad");
+ QTest::newRow("Pre component script") << ":///data/component2.qs" << false;
+ QTest::newRow("Post component script") << ":///data/component2.qs" << true;
+ }
+
void loadBrokenComponentScript()
{
+ QFETCH(QString, path);
+ QFETCH(bool, postLoad);
+
Component *testComponent = new Component(&m_core);
testComponent->setValue(scName, "broken.component");
@@ -433,21 +450,32 @@ private slots:
try {
// ignore Output from script
setExpectedScriptOutput("script function: Component");
- testComponent->loadComponentScript(":///data/component2.qs");
+ testComponent->evaluateComponentScript(path, postLoad);
} catch (const Error &error) {
const QString debugMessage(
- QString("create Error-Exception: \"Exception while loading the component script \"%1\": "
- "ReferenceError: broken is not defined\"").arg(QDir::toNativeSeparators(":///data/component2.qs")));
+ QString("Exception while loading the component script \"%1\": "
+ "ReferenceError: broken is not defined on line number: 33").arg(QDir::toNativeSeparators(":///data/component2.qs")));
QVERIFY2(debugMessage.contains(error.message()), "(ReferenceError: broken is not defined)");
}
}
+ void loadComponentUserInterfaces_data()
+ {
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("postLoad");
+ QTest::newRow("Pre component script") << ":///data/userinterface.qs" << false;
+ QTest::newRow("Post component script") << ":///data/userinterface.qs" << true;
+ }
+
void loadComponentUserInterfaces()
{
- try {
+ QFETCH(QString, path);
+ QFETCH(bool, postLoad);
+ try {
setExpectedScriptOutput("checked: false");
+ TestGui testGui(&m_core);
m_component->loadUserInterfaces(QDir(":///data"), QStringList() << QLatin1String("form.ui"));
- m_component->loadComponentScript(":///data/userinterface.qs");
+ m_component->evaluateComponentScript(path, postLoad);
} catch (const Error &error) {
QFAIL(qPrintable(error.message()));
}
@@ -591,7 +619,7 @@ private slots:
try {
m_core.setPackageManager();
Component *component = m_core.componentByName("component.test.addOperation");
- component->loadComponentScript(":///data/addOperation.qs");
+ component->evaluateComponentScript(":///data/addOperation.qs");
setExpectedScriptOutput("Component::createOperations()");
component->createOperations();
diff --git a/tests/auto/installer/settings/data/full_config.xml b/tests/auto/installer/settings/data/full_config.xml
index 304dd21f4..bccb6f7ca 100644
--- a/tests/auto/installer/settings/data/full_config.xml
+++ b/tests/auto/installer/settings/data/full_config.xml
@@ -37,6 +37,7 @@ File should contain all elements we allow in a config.xml
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
<AllowSpaceInPath>true</AllowSpaceInPath>
<AllowNonAsciiCharacters>true</AllowNonAsciiCharacters>
+ <AllowRepositoriesForOfflineInstaller>true</AllowRepositoriesForOfflineInstaller>
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
<DisableCommandLineInterface>true</DisableCommandLineInterface>
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
diff --git a/tests/auto/installer/settings/tst_settings.cpp b/tests/auto/installer/settings/tst_settings.cpp
index d120f5680..15dcc98a1 100644
--- a/tests/auto/installer/settings/tst_settings.cpp
+++ b/tests/auto/installer/settings/tst_settings.cpp
@@ -109,6 +109,7 @@ void tst_Settings::loadTutorialConfig()
QCOMPARE(settings.repositorySettingsPageVisible(), true);
QCOMPARE(settings.allowSpaceInPath(), true);
QCOMPARE(settings.allowNonAsciiCharacters(), false);
+ QCOMPARE(settings.allowRepositoriesForOfflineInstaller(), true);
QCOMPARE(settings.disableAuthorizationFallback(), false);
QCOMPARE(settings.disableCommandLineInterface(), false);
QCOMPARE(settings.createLocalRepository(), false);
diff --git a/tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7z b/tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7z
new file mode 100644
index 000000000..211bc0e0a
--- /dev/null
+++ b/tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7z b/tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7z
new file mode 100644
index 000000000..a990fdbf1
--- /dev/null
+++ b/tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7z
Binary files differ
diff --git a/tests/auto/installer/settingsoperation/data/repository/Updates.xml b/tests/auto/installer/settingsoperation/data/repository/Updates.xml
index 77b5a9956..3e88f849d 100644
--- a/tests/auto/installer/settingsoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/settingsoperation/data/repository/Updates.xml
@@ -1,13 +1,33 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
<Description>Example component A</Description>
<Version>1.0.2-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
- <Default>true</Default>
+ <Default>false</Default>
+ <Script>script.qs</Script>
+ <SHA1>5dc8a98f591998de0c555e194e228fa740a15632</SHA1>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>B</Name>
+ <DisplayName>B</DisplayName>
+ <Description>Example component B</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
+ <Script>script.qs</Script>
+ </PackageUpdate>
+ <PackageUpdate>
+ <Name>C</Name>
+ <DisplayName>C</DisplayName>
+ <Description>Example component C</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>2015-01-01</ReleaseDate>
+ <Default>false</Default>
<Script>script.qs</Script>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/settingsoperation/settings.qrc b/tests/auto/installer/settingsoperation/settings.qrc
index d030220ab..8da639ad6 100644
--- a/tests/auto/installer/settingsoperation/settings.qrc
+++ b/tests/auto/installer/settingsoperation/settings.qrc
@@ -2,5 +2,7 @@
<qresource prefix="/">
<file>data/repository/Updates.xml</file>
<file>data/repository/A/1.0.2-1meta.7z</file>
+ <file>data/repository/B/1.0.0meta.7z</file>
+ <file>data/repository/C/1.0.0meta.7z</file>
</qresource>
</RCC>
diff --git a/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp b/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp
index 9b08d4f6f..2da0870c7 100644
--- a/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp
+++ b/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp
@@ -177,6 +177,7 @@ private slots:
QFile testFile(testFilePath);
QVERIFY2(testFile.open(QIODevice::WriteOnly | QIODevice::Text), contentFile.errorString().toLatin1());
+ m_cleanupFilePaths << testFilePath;
QTextStream out(&testFile);
@@ -244,6 +245,7 @@ private slots:
QFile testFile(testFilePath);
QVERIFY2(testFile.open(QIODevice::WriteOnly | QIODevice::Text), contentFile.errorString()
.toLatin1());
+ m_cleanupFilePaths << testFilePath;
QTextStream out(&testFile);
@@ -293,7 +295,7 @@ private slots:
QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(),
QStringList() << "value1" << "value2" << "value3");
- core->installDefaultComponentsSilently();
+ core->installSelectedComponentsSilently(QStringList() << "A");
QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(),
QStringList() << "value1" << "value2" << "value3" << "valueFromScript");
@@ -309,6 +311,68 @@ private slots:
core->deleteLater();
}
+ void testPerformingFromCLIWithPlaceholders()
+ {
+ QString installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(installDir));
+ PackageManagerCore *core = PackageManager::getPackageManagerWithInit
+ (installDir, ":///data/repository");
+
+ core->installSelectedComponentsSilently(QStringList() << "B");
+ // Path is set in component constructor in install script
+ const QString settingsFile = core->value("SettingsPathFromVariable");
+ QSettings testSettings(QDir(m_testSettingsDirPath).filePath(settingsFile),
+ QSettings::IniFormat);
+
+ QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(),
+ QStringList() << "ValueFromPlaceholder");
+
+ core->commitSessionOperations();
+ core->setPackageManager();
+ core->uninstallComponentsSilently(QStringList() << "B");
+
+ // Settings file is removed as it is empty
+ QVERIFY(!QFile::exists(settingsFile));
+ QDir dir(installDir);
+ QVERIFY(dir.removeRecursively());
+ core->deleteLater();
+ }
+
+ void testPerformingFromCLIWithSettingsFileMoved()
+ {
+ QString installDir = QInstaller::generateTemporaryFileName();
+ QVERIFY(QDir().mkpath(installDir));
+ PackageManagerCore *core = PackageManager::getPackageManagerWithInit
+ (installDir, ":///data/repository");
+
+
+ core->installSelectedComponentsSilently(QStringList() << "C");
+
+ const QString settingsFileName = qApp->applicationDirPath() + "/oldTestSettings.ini";
+ QSettings testSettings(QDir(m_testSettingsDirPath).filePath(settingsFileName),
+ QSettings::IniFormat);
+
+ QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(),
+ QStringList() << "valueFromScript");
+
+ QFile settingsFile(settingsFileName);
+ // Move the settings path to new location. Script has set values
+ // ComponentCSettingsPath (@InstallerDirPath@/newTestSettings.ini) and
+ // ComponentCSettingsPath_OLD (@InstallerDirPath@/oldTestSettings.ini) so
+ // UNDO operation should delete the moved newTestSettings.ini file instead.
+ QVERIFY2(settingsFile.rename(qApp->applicationDirPath() + "/newTestSettings.ini"), "Could not move settings file.");
+ core->commitSessionOperations();
+ core->setPackageManager();
+ core->uninstallComponentsSilently(QStringList() << "C");
+
+ // Settings file is removed in uninstall as it is empty
+ QVERIFY2(!QFile::exists(qApp->applicationDirPath() + "/newTestSettings.ini"), "Settings file not deleted correctly");
+
+ QDir dir(installDir);
+ QVERIFY(dir.removeRecursively());
+ core->deleteLater();
+ }
+
// called after all tests
void cleanupTestCase()
{
diff --git a/tests/auto/installer/shared/packagemanager.h b/tests/auto/installer/shared/packagemanager.h
index a95dd4442..9948be00a 100644
--- a/tests/auto/installer/shared/packagemanager.h
+++ b/tests/auto/installer/shared/packagemanager.h
@@ -61,7 +61,6 @@ struct PackageManager
PackageManagerCore *core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> ());
QString appFilePath = QCoreApplication::applicationFilePath();
- core->setAllowedRunningProcesses(QStringList() << appFilePath);
core->disableWriteMaintenanceTool();
core->setAutoConfirmCommand();
QSet<Repository> repoList;
diff --git a/tests/auto/installer/shared/verifyinstaller.h b/tests/auto/installer/shared/verifyinstaller.h
index 4d70c577b..e19d3fa0a 100644
--- a/tests/auto/installer/shared/verifyinstaller.h
+++ b/tests/auto/installer/shared/verifyinstaller.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#ifndef VERIFYINSTALLER_H
#define VERIFYINSTALLER_H
+#include <packagemanagercore.h>
+
#include <QString>
#include <QStringList>
#include <QCryptographicHash>
@@ -36,6 +38,9 @@
#include <QDir>
#include <QtTest/QTest>
+#include <iostream>
+#include <sstream>
+
struct VerifyInstaller
{
static void verifyInstallerResources(const QString &installDir, const QString &componentName, const QString &fileName)
@@ -114,5 +119,21 @@ struct VerifyInstaller
}
}
}
+
+ template <typename Func, typename... Args>
+ static void verifyListPackagesMessage(QInstaller::PackageManagerCore *core, const QString &message,
+ Func func, Args... args)
+ {
+ std::ostringstream stream;
+ std::streambuf *buf = std::cout.rdbuf();
+ std::cout.rdbuf(stream.rdbuf());
+
+ (core->*func)(std::forward<Args>(args)...);
+
+ std::cout.rdbuf(buf);
+ QVERIFY(stream && stream.tellp() == message.size());
+ for (const QString &line : message.split(QLatin1String("\n")))
+ QVERIFY(stream.str().find(line.toStdString()) != std::string::npos);
+ }
};
#endif
diff --git a/tests/auto/installer/simplemovefileoperation/data/repository/Updates.xml b/tests/auto/installer/simplemovefileoperation/data/repository/Updates.xml
index 77b5a9956..6df6a436b 100644
--- a/tests/auto/installer/simplemovefileoperation/data/repository/Updates.xml
+++ b/tests/auto/installer/simplemovefileoperation/data/repository/Updates.xml
@@ -1,6 +1,7 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
+ <Checksum>false</Checksum>
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
@@ -9,5 +10,6 @@
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
<Script>script.qs</Script>
+ <SHA1>8f2df84c9eada2570c02a7df573288e5cb6644e2</SHA1>
</PackageUpdate>
</Updates>
diff --git a/tests/auto/installer/solver/tst_solver.cpp b/tests/auto/installer/solver/tst_solver.cpp
index 8b5d1fdcf..4548af3f1 100644
--- a/tests/auto/installer/solver/tst_solver.cpp
+++ b/tests/auto/installer/solver/tst_solver.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,7 +39,7 @@
using namespace QInstaller;
typedef QMap<Component *, QStringList> ComponentToStringList;
-typedef QList<QPair<Component *, UninstallerCalculator::UninstallReasonType>> UninstallReasonList;
+typedef QList<QPair<Component *, CalculatorBase::Resolution>> UninstallReasonList;
Q_DECLARE_METATYPE(UninstallReasonList)
@@ -48,7 +48,7 @@ public:
Data() {}
explicit Data(const QString &data)
: m_data(data) {}
- inline uint qHash(const Data &test);
+ inline hashValue qHash(const Data &test);
QString data() const { return m_data; }
bool operator==(const Data &rhs) const { return m_data == rhs.m_data; }
const Data &operator=(const Data &rhs) { if (this != &rhs) { m_data = rhs.m_data; } return *this; }
@@ -56,7 +56,7 @@ public:
private:
QString m_data;
};
-inline uint qHash(const Data &data)
+inline hashValue qHash(const Data &data)
{
return qHash(data.data());
}
@@ -149,7 +149,7 @@ private slots:
QTest::addColumn<PackageManagerCore *>("core");
QTest::addColumn<QList<Component *> >("selectedComponents");
QTest::addColumn<QList<Component *> >("expectedResult");
- QTest::addColumn<QList<int> >("installReason");
+ QTest::addColumn<QList<CalculatorBase::Resolution> >("installReason");
QTest::addColumn<AutoDependencyHash >("autodependencyHash");
PackageManagerCore *core = new PackageManagerCore();
@@ -176,11 +176,11 @@ private slots:
QTest::newRow("Installer resolved") << core
<< (QList<Component *>() << componentB)
<< (QList<Component *>() << componentB_NewVersion << componentAB << componentB << componentB_Auto)
- << (QList<int>()
- << InstallerCalculator::Dependent
- << InstallerCalculator::Dependent
- << InstallerCalculator::Resolved
- << InstallerCalculator::Automatic)
+ << (QList<CalculatorBase::Resolution>()
+ << CalculatorBase::Resolution::Dependent
+ << CalculatorBase::Resolution::Dependent
+ << CalculatorBase::Resolution::Resolved
+ << CalculatorBase::Resolution::Automatic)
<< autodependencyHash;
}
@@ -189,18 +189,17 @@ private slots:
QFETCH(PackageManagerCore *, core);
QFETCH(QList<Component *> , selectedComponents);
QFETCH(QList<Component *> , expectedResult);
- QFETCH(QList<int>, installReason);
+ QFETCH(QList<CalculatorBase::Resolution>, installReason);
QFETCH(AutoDependencyHash, autodependencyHash);
- InstallerCalculator calc(core, core->components(PackageManagerCore::ComponentType::AllNoReplacements)
- , autodependencyHash);
- calc.appendComponentsToInstall(selectedComponents);
- QList<Component *> result = calc.orderedComponentsToInstall();
+ InstallerCalculator calc(core, autodependencyHash);
+ calc.solve(selectedComponents);
+ QList<Component *> result = calc.resolvedComponents();
QCOMPARE(result.count(), expectedResult.count());
for (int i = 0; i < result.count(); i++) {
QCOMPARE(result.at(i), expectedResult.at(i));
- QCOMPARE((int)calc.installReasonType(result.at(i)), installReason.at(i));
+ QCOMPARE(calc.resolutionType(result.at(i)), installReason.at(i));
}
delete core;
}
@@ -236,11 +235,11 @@ private slots:
QFETCH(QList<Component *> , selectedComponents);
QFETCH(QList<Component *> , expectedResult);
- InstallerCalculator calc(core, core->components(PackageManagerCore::ComponentType::AllNoReplacements), QHash<QString, QStringList>());
+ InstallerCalculator calc(core, QHash<QString, QStringList>());
QTest::ignoreMessage(QtWarningMsg, "Cannot find missing dependency \"B->=2.0.0\" for \"A\".");
- calc.appendComponentsToInstall(selectedComponents);
+ calc.solve(selectedComponents);
- QList<Component *> result = calc.orderedComponentsToInstall();
+ QList<Component *> result = calc.resolvedComponents();
QCOMPARE(result.count(), expectedResult.count());
delete core;
}
@@ -249,8 +248,7 @@ private slots:
{
QTest::addColumn<PackageManagerCore *>("core");
QTest::addColumn<QList<Component *> >("selectedToUninstall");
- QTest::addColumn<QList<Component *> >("installedComponents");
- QTest::addColumn<QSet<Component *> >("expectedResult");
+ QTest::addColumn<QList<Component *> >("expectedResult");
QTest::addColumn<UninstallReasonList >("uninstallReasons");
QTest::addColumn<LocalDependencyHash >("dependencyHash");
@@ -274,12 +272,11 @@ private slots:
QHash<QString, QStringList> dependencyComponentHash;
dependencyComponentHash.insert(QLatin1String("A.B"), QStringList() << QLatin1String("B"));
- uninstallReasonList.append(qMakePair(componentAB, UninstallerCalculator::Selected));
- uninstallReasonList.append(qMakePair(componentB, UninstallerCalculator::Dependent));
+ uninstallReasonList.append(qMakePair(componentAB, CalculatorBase::Resolution::Selected));
+ uninstallReasonList.append(qMakePair(componentB, CalculatorBase::Resolution::Dependent));
QTest::newRow("Uninstaller resolved") << core
<< (QList<Component *>() << componentAB)
- << (QList<Component *>() << componentA << componentB)
- << (QSet<Component *>() << componentAB << componentB)
+ << (QList<Component *>() << componentB << componentAB)
<< uninstallReasonList
<< dependencyComponentHash;
@@ -300,12 +297,11 @@ private slots:
dependencyComponentHash.insert(QLatin1String("A"), QStringList() << QLatin1String("B"));
- uninstallReasonList.append(qMakePair(compA, UninstallerCalculator::Selected));
- uninstallReasonList.append(qMakePair(compB, UninstallerCalculator::Dependent));
+ uninstallReasonList.append(qMakePair(compA, CalculatorBase::Resolution::Selected));
+ uninstallReasonList.append(qMakePair(compB, CalculatorBase::Resolution::Dependent));
QTest::newRow("Cascade dependencies") << core
<< (QList<Component *>() << compA)
- << (QList<Component *>() << compB)
- << (QSet<Component *>() << compA << compB)
+ << (QList<Component *>() << compB << compA)
<< (uninstallReasonList)
<< dependencyComponentHash;
}
@@ -314,16 +310,15 @@ private slots:
{
QFETCH(PackageManagerCore *, core);
QFETCH(QList<Component *> , selectedToUninstall);
- QFETCH(QList<Component *> , installedComponents);
- QFETCH(QSet<Component *> , expectedResult);
+ QFETCH(QList<Component *> , expectedResult);
QFETCH(UninstallReasonList, uninstallReasons);
QFETCH(LocalDependencyHash, dependencyHash);
- UninstallerCalculator calc(installedComponents, core, QHash<QString, QStringList>(), dependencyHash, QStringList());
- calc.appendComponentsToUninstall(selectedToUninstall);
- QSet<Component *> result = calc.componentsToUninstall();
+ UninstallerCalculator calc(core, QHash<QString, QStringList>(), dependencyHash, QStringList());
+ calc.solve(selectedToUninstall);
+ QList<Component *> result = calc.resolvedComponents();
for (auto pair : uninstallReasons) {
- UninstallerCalculator::UninstallReasonType type = calc.uninstallReasonType(pair.first);
+ CalculatorBase::Resolution type = calc.resolutionType(pair.first);
QCOMPARE(pair.second, type);
}
QCOMPARE(result.count(), expectedResult.count());
diff --git a/tests/auto/installer/treename/tst_treename.cpp b/tests/auto/installer/treename/tst_treename.cpp
index 3d42126eb..2c8be6ddd 100644
--- a/tests/auto/installer/treename/tst_treename.cpp
+++ b/tests/auto/installer/treename/tst_treename.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -394,7 +394,7 @@ void tst_TreeName::remotePackageConflictsLocal()
QHash<QString, QString> params;
params.insert(scTargetDir, qApp->applicationDirPath());
PackageManagerCore core(BinaryContent::MagicPackageManagerMarker, QList<OperationBlob>(),
- QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, params);
+ QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, params);
core.settings().setAllowUnstableComponents(true);
core.settings().setDefaultRepositories(QSet<Repository>()
diff --git a/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml b/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml
index dfae8c8bd..edb6515bd 100644
--- a/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml
+++ b/tests/auto/tools/repotest/test_package_versions/repository_1/Updates.xml
@@ -5,7 +5,7 @@
<PackageUpdate>
<Name>A</Name>
<DisplayName>A</DisplayName>
- <Description>Example component A</Description>
+ <Description>Test component A</Description>
<Version>2.0.0</Version>
<ReleaseDate>2020-01-01</ReleaseDate>
<Default>true</Default>
@@ -17,7 +17,7 @@
<PackageUpdate>
<Name>B</Name>
<DisplayName>B</DisplayName>
- <Description>Example component B</Description>
+ <Description>Test component B</Description>
<Version>1.0.0</Version>
<ReleaseDate>2020-01-01</ReleaseDate>
<Default>true</Default>
diff --git a/tests/auto/tools/repotest/tst_repotest.cpp b/tests/auto/tools/repotest/tst_repotest.cpp
index af48ba3fc..f1c0ae9dd 100644
--- a/tests/auto/tools/repotest/tst_repotest.cpp
+++ b/tests/auto/tools/repotest/tst_repotest.cpp
@@ -574,7 +574,7 @@ private slots:
private:
QInstallerTools::RepositoryInfo m_repoInfo;
QInstallerTools::PackageInfoVector m_packages;
- TempDirDeleter m_tempDirDeleter;
+ TempPathDeleter m_tempDirDeleter;
};
QTEST_MAIN(tst_repotest)
diff --git a/tools/binarycreator/binarycreator.pro b/tools/binarycreator/binarycreator.pro
index 1266b91c6..b2e8c68b8 100644
--- a/tools/binarycreator/binarycreator.pro
+++ b/tools/binarycreator/binarycreator.pro
@@ -7,8 +7,6 @@ include(../../installerfw.pri)
QT -= gui
QT += qml xml concurrent
-CONFIG -= import_plugins
-
CONFIG += console
DESTDIR = $$IFW_APP_PATH
SOURCES = main.cpp
diff --git a/tools/binarycreator/main.cpp b/tools/binarycreator/main.cpp
index d74644670..1840d4c2a 100644
--- a/tools/binarycreator/main.cpp
+++ b/tools/binarycreator/main.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -72,6 +72,9 @@ static void printUsage()
<< std::endl;
std::cout << " 'update.rcc' in the current path." << std::endl;
#ifdef Q_OS_MACOS
+ std::cout << " --mt|--create-maintenancetool " << std::endl;
+ std::cout << " Creates maintenance tool app bundle. Target option is omitted, bundle name "<<std::endl;
+ std::cout << " can be configured in the config.xml using element <MaintenanceToolName>." << std::endl;
std::cout << " -s|--sign identity Sign generated app bundle using the given code " << std::endl;
std::cout << " signing identity" << std::endl;
#endif
@@ -99,6 +102,11 @@ static void printUsage()
std::cout << "Example update.rcc:" << std::endl;
std::cout << " " << appName << " -c installer-config" << sep << "config.xml -p packages-directory "
"-rcc" << std::endl;
+#ifdef Q_OS_MACOS
+ std::cout << "Example (maintenance tool bundle):" << std::endl;
+ std::cout << " " << appName << " -c installer-config" << sep << "config.xml" << " --mt" << std::endl;
+ std::cout << std::endl;
+#endif
}
static int printErrorAndUsageAndExit(const QString &err)
@@ -206,6 +214,9 @@ int main(int argc, char **argv)
}
parsedArgs.compression = static_cast<AbstractArchive::CompressionLevel>(value);
#ifdef Q_OS_MACOS
+ } else if (*it == QLatin1String("--mt") || *it == QLatin1String("--create-maintenancetool")) {
+ parsedArgs.createMaintenanceTool = true;
+
} else if (*it == QLatin1String("-s") || *it == QLatin1String("--sign")) {
++it;
if (it == args.end() || it->startsWith(QLatin1String("-")))
diff --git a/tools/devtool/devtool.pro b/tools/devtool/devtool.pro
index def7066af..9a13ae0b8 100644
--- a/tools/devtool/devtool.pro
+++ b/tools/devtool/devtool.pro
@@ -10,6 +10,9 @@ CONFIG -= import_plugins
CONFIG += console
DESTDIR = $$IFW_APP_PATH
+!macos:QTPLUGIN += qopensslbackend
+macos:QTPLUGIN += qsecuretransportbackend
+
HEADERS += operationrunner.h \
binaryreplace.h \
binarydump.h
diff --git a/tools/repogen/repogen.cpp b/tools/repogen/repogen.cpp
index a8720a0de..2abe67e78 100644
--- a/tools/repogen/repogen.cpp
+++ b/tools/repogen/repogen.cpp
@@ -185,7 +185,7 @@ int main(int argc, char** argv)
"Error: Repository parameter missing argument"));
}
- if (!QFileInfo(args.first()).exists()) {
+ if (!QFileInfo::exists(args.first())) {
return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller",
"Error: Only local filesystem repositories now supported"));
}
diff --git a/tools/repogen/repogen.pro b/tools/repogen/repogen.pro
index 73f1eafe5..2740167fc 100644
--- a/tools/repogen/repogen.pro
+++ b/tools/repogen/repogen.pro
@@ -12,6 +12,9 @@ CONFIG -= import_plugins
CONFIG += console
DESTDIR = $$IFW_APP_PATH
+!macos:QTPLUGIN += qopensslbackend
+macos:QTPLUGIN += qsecuretransportbackend
+
SOURCES += repogen.cpp
macx:include(../../no_app_bundle.pri)