From 2a1611bf89a5a793a97cd06bbef58b3e2123bda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:48:21 +0200 Subject: [PATCH 01/57] feat: use conan and ownCloud artifactory --- .conan/config/global.conf | 5 ++ .../profiles/macos-latest-armv8.conanprofile | 9 +++ .../ubuntu-latest-x86_64.conanprofile | 8 +++ .../windows-latest-x86_64.conanprofile | 9 +++ .github/workflows/conan.yml | 60 +++++++++++++++++++ CMakeLists.txt | 4 +- conanfile.py | 34 +++++++++++ 7 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 .conan/config/global.conf create mode 100644 .conan/profiles/macos-latest-armv8.conanprofile create mode 100644 .conan/profiles/ubuntu-latest-x86_64.conanprofile create mode 100644 .conan/profiles/windows-latest-x86_64.conanprofile create mode 100644 .github/workflows/conan.yml create mode 100644 conanfile.py diff --git a/.conan/config/global.conf b/.conan/config/global.conf new file mode 100644 index 00000000000..ab423990b31 --- /dev/null +++ b/.conan/config/global.conf @@ -0,0 +1,5 @@ +core.net.http:timeout = 300 + +tools.system.package_manager:mode = install +tools.system.package_manager:sudo = True + diff --git a/.conan/profiles/macos-latest-armv8.conanprofile b/.conan/profiles/macos-latest-armv8.conanprofile new file mode 100644 index 00000000000..1d4e1262941 --- /dev/null +++ b/.conan/profiles/macos-latest-armv8.conanprofile @@ -0,0 +1,9 @@ +[settings] +arch=armv8 +build_type=Release +compiler=apple-clang +compiler.cppstd=17 +compiler.libcxx=libc++ +compiler.version=15 +os=Macos + diff --git a/.conan/profiles/ubuntu-latest-x86_64.conanprofile b/.conan/profiles/ubuntu-latest-x86_64.conanprofile new file mode 100644 index 00000000000..8976420b032 --- /dev/null +++ b/.conan/profiles/ubuntu-latest-x86_64.conanprofile @@ -0,0 +1,8 @@ +[settings] +arch=x86_64 +build_type=Release +compiler=gcc +compiler.cppstd=gnu17 +compiler.libcxx=libstdc++11 +compiler.version=13 +os=Linux diff --git a/.conan/profiles/windows-latest-x86_64.conanprofile b/.conan/profiles/windows-latest-x86_64.conanprofile new file mode 100644 index 00000000000..1d62d9240cf --- /dev/null +++ b/.conan/profiles/windows-latest-x86_64.conanprofile @@ -0,0 +1,9 @@ +[settings] +arch=x86_64 +build_type=Release +compiler=msvc +compiler.cppstd=17 +compiler.runtime=dynamic +compiler.runtime_type=Debug +compiler.version=194 +os=Windows diff --git a/.github/workflows/conan.yml b/.github/workflows/conan.yml new file mode 100644 index 00000000000..bff9fdf492c --- /dev/null +++ b/.github/workflows/conan.yml @@ -0,0 +1,60 @@ +name: Build with conan + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + name: Building ${{ matrix.build_type }} ${{ matrix.profile }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + # Release build type + - os: ubuntu-latest + profile: ubuntu-latest-x86_64 + build_type: Release + - os: macos-latest + profile: macos-latest-armv8 + build_type: Release + #- os: windows-latest + # profile: windows-latest-x86_64 + # build_type: Release + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Conan + uses: conan-io/setup-conan@v1 + + - name: Add artifactory & set conan config + run: | + conan config install ./.conan/config/global.conf + conan remote remove conancenter + conan remote add oc https://artifactory.owncloud-demo.com/artifactory/api/conan/conan-local + conan remote login oc ${{ secrets.CONAN_USER }} -p ${{ secrets.CONAN_PASSWORD }} + conan remote list + conan profile show + + - name: Create build directory + run: mkdir build + + - name: Install dependencies with conan + run: | + conan install . --profile=.conan/profiles/${{ matrix.profile }}.conanprofile -s build_type=${{ matrix.build_type }} + + - name: Build the client + working-directory: ./build + run: | + cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + cmake --build . + + - name: Run tests + working-directory: ./build + run: ctest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index c7f937934e5..0a1c29f8127 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,8 +26,8 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) find_package(QT 6.8 NAMES Qt6 COMPONENTS Core REQUIRED) find_package(Qt6 COMPONENTS Core Concurrent Network Widgets Xml Quick QuickWidgets QuickControls2 REQUIRED) -find_package(Qt6LinguistTools REQUIRED) -get_target_property (QT_QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION) +find_package(Qt6 CONFIG REQUIRED COMPONENTS LinguistTools) +get_target_property (QT_QMAKE_EXECUTABLE Qt6::qmake IMPORTED_LOCATION) message(STATUS "Using Qt ${QT_VERSION} (${QT_QMAKE_EXECUTABLE})") if (UNIX AND NOT APPLE) diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 00000000000..8dccffd2594 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,34 @@ +from conan import ConanFile + +class ClientRecipe(ConanFile): + settings = "os", "compiler", "arch", "build_type" + generators = "CMakeToolchain", "CMakeDeps" + options = {"shared": [True, False]} + default_options = { + "shared": 'True', + "*:fPIC": True, + 'qt/*:shared': 'True', + 'qt/*:qtdeclarative': 'True', + 'qt/*:qtquickcontrols2': 'True', + 'qt/*:qtshadertools': 'True', + 'qt/*:qttools': 'True', + 'qt/*:gui': 'True', + 'qt/*:widgets': 'True', + 'qt/*:with_dbus': 'True', + } + + def requirements(self): + self.requires("extra-cmake-modules/6.2.0") + self.requires("zlib/1.3.1") + self.requires("sqlite3/3.49.1") + self.requires("openssl/3.4.1") + self.requires("nlohmann_json/3.11.3") + self.requires("qt/6.8.3") + self.requires("kdsingleapplication/1.2.0") + self.requires("qtkeychain/0.15.0") + self.requires("libregraphapi/1.0.4") + if self.settings.os == "Macos": + self.requires("sparkle/2.7.0") + + def build_requirements(self): + self.tool_requires("cmake/3.30.0") From 7ae8926b1a09e5cb77fd7dd7e74afe0df46458ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:34:07 +0200 Subject: [PATCH 02/57] fix: cmake Qt6:: + includes --- .gitignore | 9 ++++++++ CMakeLists.txt | 10 ++++++++- conanfile.py | 33 +++++++++++++++++++--------- src/csync/CMakeLists.txt | 4 ++-- src/gui/CMakeLists.txt | 2 +- src/gui/accountstate.h | 2 +- src/gui/folderwizard/CMakeLists.txt | 2 +- src/gui/main.cpp | 2 +- src/libsync/CMakeLists.txt | 17 +++++++++----- src/libsync/graphapi/jobs/drives.cpp | 4 ++-- src/libsync/graphapi/jobs/drives.h | 2 +- src/libsync/graphapi/space.h | 6 +++-- src/libsync/graphapi/spacesmanager.h | 2 +- src/resources/CMakeLists.txt | 8 ++++++- test/owncloud_add_test.cmake | 2 +- test/testutils/CMakeLists.txt | 4 ++-- 16 files changed, 76 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 3b8359b14af..3c8f116185e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +CMakeUserPresets.json /branding/ .gitmodules *build*/ @@ -180,3 +181,11 @@ test/gui/config.ini test/gui/reports */**/__pycache__/ test/gui/users.json + +# conan generated files +#*-config.cmake +#*-data.cmake +#*-version.cmake +#*-release.cmake +#*Targets.cmake +#*conan*.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a1c29f8127..7400a6da381 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,11 +25,19 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) find_package(QT 6.8 NAMES Qt6 COMPONENTS Core REQUIRED) -find_package(Qt6 COMPONENTS Core Concurrent Network Widgets Xml Quick QuickWidgets QuickControls2 REQUIRED) +find_package(Qt6 COMPONENTS Core Concurrent Network Widgets Xml Quick Qml QuickWidgets QuickControls2 REQUIRED) find_package(Qt6 CONFIG REQUIRED COMPONENTS LinguistTools) + get_target_property (QT_QMAKE_EXECUTABLE Qt6::qmake IMPORTED_LOCATION) message(STATUS "Using Qt ${QT_VERSION} (${QT_QMAKE_EXECUTABLE})") +qt6_standard_project_setup() + +set(QT_KNOWN_POLICY_QTP0004 TRUE) +set(QT_KNOWN_POLICY_QTP0005 TRUE) + +qt6_policy(GET QTP0005 OLD) + if (UNIX AND NOT APPLE) find_package(Qt6 REQUIRED COMPONENTS DBus) endif() diff --git a/conanfile.py b/conanfile.py index 8dccffd2594..93092327086 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,24 +1,27 @@ from conan import ConanFile +from conan.tools.cmake import cmake_layout, CMake class ClientRecipe(ConanFile): settings = "os", "compiler", "arch", "build_type" generators = "CMakeToolchain", "CMakeDeps" options = {"shared": [True, False]} default_options = { - "shared": 'True', + "shared": True, "*:fPIC": True, - 'qt/*:shared': 'True', - 'qt/*:qtdeclarative': 'True', - 'qt/*:qtquickcontrols2': 'True', - 'qt/*:qtshadertools': 'True', - 'qt/*:qttools': 'True', - 'qt/*:gui': 'True', - 'qt/*:widgets': 'True', - 'qt/*:with_dbus': 'True', + 'qt/*:shared': True, + 'qt/*:qtdeclarative': True, + 'qt/*:qtquickcontrols2': True, + 'qt/*:qtshadertools': True, + 'qt/*:qtsvg': True, + 'qt/*:qtimageformats': True, + 'qt/*:qttools': True, + 'qt/*:gui': True, + 'qt/*:widgets': True, + 'qt/*:with_dbus': True, } def requirements(self): - self.requires("extra-cmake-modules/6.2.0") + self.requires("extra-cmake-modules/6.16.0") self.requires("zlib/1.3.1") self.requires("sqlite3/3.49.1") self.requires("openssl/3.4.1") @@ -32,3 +35,13 @@ def requirements(self): def build_requirements(self): self.tool_requires("cmake/3.30.0") + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + #cmake.configure(None, None, ["--trace"]) + cmake.configure() + #cmake.build(None, None, ["--verbose"]) + cmake.build() \ No newline at end of file diff --git a/src/csync/CMakeLists.txt b/src/csync/CMakeLists.txt index aea5015f35e..0439df40d84 100644 --- a/src/csync/CMakeLists.txt +++ b/src/csync/CMakeLists.txt @@ -52,9 +52,9 @@ generate_export_header(csync target_link_libraries(csync PUBLIC - Qt::Core + Qt6::Core PRIVATE - Qt::Concurrent + Qt6::Concurrent SQLite::SQLite3 ZLIB::ZLIB ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 9833a7f5d61..3bf805f3825 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -75,7 +75,7 @@ set_target_properties(owncloudGui PROPERTIES AUTOUIC ON AUTORCC ON) target_include_directories(owncloudGui PRIVATE models spaces creds) target_link_libraries(owncloudGui PUBLIC - Qt::Widgets Qt::Network Qt::Xml Qt::Quick Qt::QuickWidgets Qt::QuickControls2 + Qt6::Widgets Qt6::Network Qt6::Xml Qt6::Quick Qt6::QuickWidgets Qt6::QuickControls2 folderwizard libsync Qt6Keychain::Qt6Keychain diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index dc055deea8c..ecee51ab5ec 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include class QDialog; diff --git a/src/gui/folderwizard/CMakeLists.txt b/src/gui/folderwizard/CMakeLists.txt index 7f5be4d0e1a..fb7b1f72528 100644 --- a/src/gui/folderwizard/CMakeLists.txt +++ b/src/gui/folderwizard/CMakeLists.txt @@ -11,6 +11,6 @@ add_library(folderwizard STATIC spacespage.ui ) -target_link_libraries(folderwizard PUBLIC Qt::Widgets libsync owncloudResources) +target_link_libraries(folderwizard PUBLIC Qt6::Widgets libsync owncloudResources) set_target_properties(folderwizard PROPERTIES AUTOUIC ON AUTORCC ON) apply_common_target_settings(folderwizard) diff --git a/src/gui/main.cpp b/src/gui/main.cpp index b79948c6085..bc97e360d09 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -30,7 +30,7 @@ #include "libsync/logger.h" #include "socketapi/socketapi.h" -#include +#include #ifdef WITH_AUTO_UPDATER #include "updater/updater.h" diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index 5c5278c306f..50cedb970d2 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -86,13 +86,13 @@ target_link_libraries(libsync PUBLIC csync owncloudResources - Qt::Core - Qt::Network - Qt::Widgets - Qt::QuickControls2 + Qt6::Core + Qt6::Network + Qt6::Widgets + Qt6::QuickControls2 PRIVATE - Qt::Concurrent + Qt6::Concurrent ZLIB::ZLIB Qt6Keychain::Qt6Keychain ) @@ -107,7 +107,12 @@ ecm_add_qml_module(libsync ecm_finalize_qml_module(libsync DESTINATION ${KDE_INSTALL_QMLDIR}) -target_link_libraries(libsync PUBLIC $) +set_source_files_properties(configfile.cpp + PROPERTIES + COMPILE_DEFINITIONS EXCLUDE_FILE_NAME="${EXCLUDE_FILE_NAME}" +) + +target_link_libraries(libsync PUBLIC OpenAPI::LibreGraphAPI) add_subdirectory(graphapi) if ( APPLE ) diff --git a/src/libsync/graphapi/jobs/drives.cpp b/src/libsync/graphapi/jobs/drives.cpp index 416861d5a5e..b2a54367cac 100644 --- a/src/libsync/graphapi/jobs/drives.cpp +++ b/src/libsync/graphapi/jobs/drives.cpp @@ -16,8 +16,8 @@ #include "account.h" -#include -#include +#include "OpenAPI/LibreGraphAPI/OAICollection_of_drives.h" +#include "OpenAPI/LibreGraphAPI/OAIDrive.h" using namespace OCC; diff --git a/src/libsync/graphapi/jobs/drives.h b/src/libsync/graphapi/jobs/drives.h index bda75cca589..29b92a26134 100644 --- a/src/libsync/graphapi/jobs/drives.h +++ b/src/libsync/graphapi/jobs/drives.h @@ -16,7 +16,7 @@ #include "networkjobs/jsonjob.h" #include "owncloudlib.h" -#include +#include "OpenAPI/LibreGraphAPI/OAIDrive.h" namespace OCC { namespace GraphApi { diff --git a/src/libsync/graphapi/space.h b/src/libsync/graphapi/space.h index 5db605ca3dd..8e0d598e515 100644 --- a/src/libsync/graphapi/space.h +++ b/src/libsync/graphapi/space.h @@ -16,11 +16,13 @@ #include "owncloudlib.h" -#include +#include "libsync/accountfwd.h" + +#include "OpenAPI/LibreGraphAPI/OAIDrive.h" #include #include -#include +#include namespace OCC { namespace GraphApi { diff --git a/src/libsync/graphapi/spacesmanager.h b/src/libsync/graphapi/spacesmanager.h index 9cf82a5201d..cd30a236318 100644 --- a/src/libsync/graphapi/spacesmanager.h +++ b/src/libsync/graphapi/spacesmanager.h @@ -18,7 +18,7 @@ #include "libsync/graphapi/space.h" -#include +#include "OpenAPI/LibreGraphAPI/OAIDrive.h" class QTimer; diff --git a/src/resources/CMakeLists.txt b/src/resources/CMakeLists.txt index f7d36e3ccd1..f90b47392b8 100644 --- a/src/resources/CMakeLists.txt +++ b/src/resources/CMakeLists.txt @@ -1,3 +1,7 @@ +#include(ECMQmlModule) +include(Qt6QmlPublicCMakeHelpers) + + add_library(owncloudResources SHARED client.qrc core_theme.qrc @@ -14,7 +18,7 @@ generate_theme(owncloudResources OWNCLOUD_SIDEBAR_ICONS) # make them available to the whole project set(OWNCLOUD_SIDEBAR_ICONS ${OWNCLOUD_SIDEBAR_ICONS} CACHE INTERNAL "Sidebar icons" FORCE) -target_link_libraries(owncloudResources PUBLIC Qt::Core Qt::Gui Qt::Quick) +target_link_libraries(owncloudResources PUBLIC Qt6::Core Qt6::Gui Qt6::Quick) apply_common_target_settings(owncloudResources) target_include_directories(owncloudResources PUBLIC $ $) target_compile_definitions(owncloudResources PRIVATE APPLICATION_SHORTNAME="${APPLICATION_SHORTNAME}") @@ -49,6 +53,8 @@ ecm_add_qml_module(owncloudResources GENERATE_PLUGIN_SOURCE ) +ecm_target_qml_sources(owncloudResources SOURCES qmlresources.cpp) + ecm_finalize_qml_module(owncloudResources DESTINATION ${KDE_INSTALL_QMLDIR}) install(TARGETS owncloudResources EXPORT ${APPLICATION_SHORTNAME}Config ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/test/owncloud_add_test.cmake b/test/owncloud_add_test.cmake index db4be0d9052..883ecef574c 100644 --- a/test/owncloud_add_test.cmake +++ b/test/owncloud_add_test.cmake @@ -15,7 +15,7 @@ function(owncloud_add_test test_class) ${ARGN} TEST_NAME "${OWNCLOUD_TEST_CLASS}Test" LINK_LIBRARIES - owncloudGui syncenginetestutils testutilsloader Qt::Test + owncloudGui syncenginetestutils testutilsloader Qt6::Test ) apply_common_target_settings(${OWNCLOUD_TEST_CLASS}Test) target_compile_definitions(${OWNCLOUD_TEST_CLASS}Test PRIVATE SOURCEDIR="${PROJECT_SOURCE_DIR}" QT_FORCE_ASSERTS) diff --git a/test/testutils/CMakeLists.txt b/test/testutils/CMakeLists.txt index efa20a1fa21..dc567ec9286 100644 --- a/test/testutils/CMakeLists.txt +++ b/test/testutils/CMakeLists.txt @@ -1,8 +1,8 @@ add_executable(test_helper test_helper.cpp) -target_link_libraries(test_helper PUBLIC Qt::Core libsync) +target_link_libraries(test_helper PUBLIC Qt6::Core libsync) add_library(syncenginetestutils STATIC syncenginetestutils.cpp testutils.cpp) -target_link_libraries(syncenginetestutils PUBLIC owncloudGui Qt::Test) +target_link_libraries(syncenginetestutils PUBLIC owncloudGui Qt6::Test) target_compile_definitions(syncenginetestutils PRIVATE TEST_HELPER_EXE="$") # testutilsloader.cpp uses Q_COREAPP_STARTUP_FUNCTION which can't used reliably in a static lib From 416724680308b23d16960166064f398f6b7145ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:05:22 +0200 Subject: [PATCH 03/57] fix: use extra-cmake-modules/6.8.0 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 93092327086..4bab2a7d3d7 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,7 +21,7 @@ class ClientRecipe(ConanFile): } def requirements(self): - self.requires("extra-cmake-modules/6.16.0") + self.requires("extra-cmake-modules/6.8.0") self.requires("zlib/1.3.1") self.requires("sqlite3/3.49.1") self.requires("openssl/3.4.1") From 68f8f85a1ebffa368d6a1eca0376e48973e5b307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:33:29 +0200 Subject: [PATCH 04/57] fix: remove include(Qt6QmlPublicCMakeHelpers) - not found on MacOS --- src/resources/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/CMakeLists.txt b/src/resources/CMakeLists.txt index f90b47392b8..f53f1b3d55d 100644 --- a/src/resources/CMakeLists.txt +++ b/src/resources/CMakeLists.txt @@ -1,5 +1,5 @@ #include(ECMQmlModule) -include(Qt6QmlPublicCMakeHelpers) +#include(Qt6QmlPublicCMakeHelpers) add_library(owncloudResources SHARED From 6fee724a67deb2266e01105249b583d094213191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:35:31 +0200 Subject: [PATCH 05/57] feat: openssl/3.4.2 --- conanfile.py | 2 +- src/resources/CMakeLists.txt | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/conanfile.py b/conanfile.py index 4bab2a7d3d7..75a8efc8c7e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,7 +24,7 @@ def requirements(self): self.requires("extra-cmake-modules/6.8.0") self.requires("zlib/1.3.1") self.requires("sqlite3/3.49.1") - self.requires("openssl/3.4.1") + self.requires("openssl/3.4.2") self.requires("nlohmann_json/3.11.3") self.requires("qt/6.8.3") self.requires("kdsingleapplication/1.2.0") diff --git a/src/resources/CMakeLists.txt b/src/resources/CMakeLists.txt index f53f1b3d55d..af4448b8343 100644 --- a/src/resources/CMakeLists.txt +++ b/src/resources/CMakeLists.txt @@ -1,7 +1,3 @@ -#include(ECMQmlModule) -#include(Qt6QmlPublicCMakeHelpers) - - add_library(owncloudResources SHARED client.qrc core_theme.qrc From 05721b7b52b221bf2d4da37432772f24468f4958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:01:57 +0200 Subject: [PATCH 06/57] fix: now with qttranslations --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 75a8efc8c7e..fbb5922eb93 100644 --- a/conanfile.py +++ b/conanfile.py @@ -15,6 +15,7 @@ class ClientRecipe(ConanFile): 'qt/*:qtsvg': True, 'qt/*:qtimageformats': True, 'qt/*:qttools': True, + 'qt/*:qttranslations': True, 'qt/*:gui': True, 'qt/*:widgets': True, 'qt/*:with_dbus': True, From f6739a2806f53be15a05c4c2a9be6208d5aaacfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:48:54 +0200 Subject: [PATCH 07/57] fix: Qt6::DBus it is .... --- CMakeLists.txt | 5 ----- conanfile.py | 7 ++++++- src/gui/CMakeLists.txt | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7400a6da381..8459739903d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,11 +33,6 @@ message(STATUS "Using Qt ${QT_VERSION} (${QT_QMAKE_EXECUTABLE})") qt6_standard_project_setup() -set(QT_KNOWN_POLICY_QTP0004 TRUE) -set(QT_KNOWN_POLICY_QTP0005 TRUE) - -qt6_policy(GET QTP0005 OLD) - if (UNIX AND NOT APPLE) find_package(Qt6 REQUIRED COMPONENTS DBus) endif() diff --git a/conanfile.py b/conanfile.py index fbb5922eb93..abf29c65416 100644 --- a/conanfile.py +++ b/conanfile.py @@ -18,9 +18,14 @@ class ClientRecipe(ConanFile): 'qt/*:qttranslations': True, 'qt/*:gui': True, 'qt/*:widgets': True, - 'qt/*:with_dbus': True, } + def configure(self): + self.options['qt/*'].with_pq = False + self.options['qt/*'].with_odbc = False + if self.settings.os == "Linux": + self.options['qt/*'].with_dbus = True + def requirements(self): self.requires("extra-cmake-modules/6.8.0") self.requires("zlib/1.3.1") diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3bf805f3825..86dc100c49d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -124,8 +124,9 @@ elseif( WIN32 ) folderwatcher_win.cpp) elseif(UNIX AND NOT APPLE) ## handle DBUS for Fdo notifications - if (TARGET Qt::DBus) - target_link_libraries(owncloudGui PUBLIC Qt::DBus) + if (TARGET Qt6::DBus) + target_link_libraries(owncloudGui PUBLIC Qt6::DBus) + target_compile_definitions(owncloudGui PUBLIC "USE_FDO_NOTIFICATIONS") endif() target_sources(owncloudGui PRIVATE folderwatcher_linux.cpp From 39d0da128a8807b226c53d4737e7cc771066b89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:17:04 +0200 Subject: [PATCH 08/57] ci: disable CredentialManagerTest --- test/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d78765e0b84..0dc8084001a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,9 +15,9 @@ owncloud_add_test(ChecksumValidator) owncloud_add_test(ConnectionValidator) # TODO: we need keychain access for this test -if (NOT APPLE OR NOT DEFINED ENV{GITHUB_ACTION}) - owncloud_add_test(CredentialManager) -endif() +#if (NOT APPLE OR NOT DEFINED ENV{GITHUB_ACTION}) +# owncloud_add_test(CredentialManager) +#endif() owncloud_add_test(ExcludedFiles) From a3e2cf5adf5525f7473d62bae8685221e38bb6f2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 18 Aug 2025 15:30:25 +0200 Subject: [PATCH 09/57] Add Qt libraries directory to LD_LIBRARY_PATH This is to let QtKeychain find the dbus library. --- test/owncloud_add_test.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/test/owncloud_add_test.cmake b/test/owncloud_add_test.cmake index 883ecef574c..b929de344e9 100644 --- a/test/owncloud_add_test.cmake +++ b/test/owncloud_add_test.cmake @@ -23,6 +23,7 @@ function(owncloud_add_test test_class) target_include_directories(${OWNCLOUD_TEST_CLASS}Test PRIVATE "${CMAKE_SOURCE_DIR}/test/") if (UNIX AND NOT APPLE) set_property(TEST ${OWNCLOUD_TEST_CLASS}Test PROPERTY ENVIRONMENT "QT_QPA_PLATFORM=offscreen") + set_property(TEST ${OWNCLOUD_TEST_CLASS}Test PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}:${qt_LIB_DIRS_RELEASE}") endif() foreach(arg IN LISTS ARGN) From e2a52c7c7d1654085d5352c392932514927ba2fc Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 19 Aug 2025 15:48:58 +0200 Subject: [PATCH 10/57] Fix env setting on Linux --- test/owncloud_add_test.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/owncloud_add_test.cmake b/test/owncloud_add_test.cmake index b929de344e9..b61dcf1e982 100644 --- a/test/owncloud_add_test.cmake +++ b/test/owncloud_add_test.cmake @@ -22,8 +22,7 @@ function(owncloud_add_test test_class) target_include_directories(${OWNCLOUD_TEST_CLASS}Test PRIVATE "${CMAKE_SOURCE_DIR}/test/") if (UNIX AND NOT APPLE) - set_property(TEST ${OWNCLOUD_TEST_CLASS}Test PROPERTY ENVIRONMENT "QT_QPA_PLATFORM=offscreen") - set_property(TEST ${OWNCLOUD_TEST_CLASS}Test PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}:${qt_LIB_DIRS_RELEASE}") + set_property(TEST ${OWNCLOUD_TEST_CLASS}Test PROPERTY ENVIRONMENT "QT_QPA_PLATFORM=offscreen;LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}:${qt_LIB_DIRS_RELEASE}") endif() foreach(arg IN LISTS ARGN) From 66ed9e0bd5678f42a409125f637e5b7d81310dc4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 28 Sep 2026 17:56:05 +0200 Subject: [PATCH 11/57] Align Qt options with conan-artifact-builder Add with_egl, with_libjpeg, and qtwayland options to match the artifact builder's conanfile, ensuring the pre-built packages in Artifactory have matching package IDs. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conanfile.py b/conanfile.py index abf29c65416..58e82e1e29e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -18,6 +18,8 @@ class ClientRecipe(ConanFile): 'qt/*:qttranslations': True, 'qt/*:gui': True, 'qt/*:widgets': True, + 'qt/*:with_egl': True, + 'qt/*:with_libjpeg': 'libjpeg', } def configure(self): @@ -25,6 +27,7 @@ def configure(self): self.options['qt/*'].with_odbc = False if self.settings.os == "Linux": self.options['qt/*'].with_dbus = True + self.options['qt/*'].qtwayland = True def requirements(self): self.requires("extra-cmake-modules/6.8.0") From 12d467d8c0c3f6bf6f9d5c46634e04c27c419663 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 28 Sep 2026 18:03:52 +0200 Subject: [PATCH 12/57] Make default_options identical to conan-artifact-builder Ensures package IDs match by using the exact same option declarations in the same format (default_options dict vs configure method). Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/conanfile.py b/conanfile.py index 58e82e1e29e..eb7feef91b2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -6,25 +6,25 @@ class ClientRecipe(ConanFile): generators = "CMakeToolchain", "CMakeDeps" options = {"shared": [True, False]} default_options = { - "shared": True, "*:fPIC": True, - 'qt/*:shared': True, - 'qt/*:qtdeclarative': True, - 'qt/*:qtquickcontrols2': True, - 'qt/*:qtshadertools': True, - 'qt/*:qtsvg': True, - 'qt/*:qtimageformats': True, - 'qt/*:qttools': True, - 'qt/*:qttranslations': True, - 'qt/*:gui': True, - 'qt/*:widgets': True, - 'qt/*:with_egl': True, - 'qt/*:with_libjpeg': 'libjpeg', + "qt/*:gui": True, + "qt/*:qtdeclarative": True, + "qt/*:qtimageformats": True, + "qt/*:qtquickcontrols2": True, + "qt/*:qtshadertools": True, + "qt/*:qtsvg": True, + "qt/*:qttools": True, + "qt/*:qttranslations": True, + "qt/*:shared": True, + "qt/*:widgets": True, + "qt/*:with_egl": True, + "qt/*:with_libjpeg": "libjpeg", + "qt/*:with_odbc": False, + "qt/*:with_pq": False, + "shared": True, } def configure(self): - self.options['qt/*'].with_pq = False - self.options['qt/*'].with_odbc = False if self.settings.os == "Linux": self.options['qt/*'].with_dbus = True self.options['qt/*'].qtwayland = True From 05e29abc6cbddc443ec5adc1c610bb18e03ddd37 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 29 Sep 2026 14:22:46 +0200 Subject: [PATCH 13/57] Switch Linux profile to RelWithDebInfo and fix GCC version Change build_type from Release to RelWithDebInfo for debug symbols. Fix compiler.version from 13 to 11 to match AlmaLinux 9's GCC. Co-Authored-By: Claude Opus 4.6 (1M context) --- .conan/profiles/ubuntu-latest-x86_64.conanprofile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.conan/profiles/ubuntu-latest-x86_64.conanprofile b/.conan/profiles/ubuntu-latest-x86_64.conanprofile index 8976420b032..e516cd1d53c 100644 --- a/.conan/profiles/ubuntu-latest-x86_64.conanprofile +++ b/.conan/profiles/ubuntu-latest-x86_64.conanprofile @@ -1,8 +1,8 @@ [settings] arch=x86_64 -build_type=Release +build_type=RelWithDebInfo compiler=gcc compiler.cppstd=gnu17 compiler.libcxx=libstdc++11 -compiler.version=13 +compiler.version=11 os=Linux From edf79449dbd2d4f2d4a44b1b779b4bb9d5420952 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 29 Sep 2026 14:47:06 +0200 Subject: [PATCH 14/57] Remove stale accountfwd.h include from space.h This header no longer exists on master. It was incorrectly kept during the cherry-pick conflict resolution. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/libsync/graphapi/space.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libsync/graphapi/space.h b/src/libsync/graphapi/space.h index 8e0d598e515..f8108df561c 100644 --- a/src/libsync/graphapi/space.h +++ b/src/libsync/graphapi/space.h @@ -16,8 +16,6 @@ #include "owncloudlib.h" -#include "libsync/accountfwd.h" - #include "OpenAPI/LibreGraphAPI/OAIDrive.h" #include From b8ef4d9f64296b676fc7e5bc0a04bbda38fd85f3 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 30 Sep 2026 16:39:44 +0200 Subject: [PATCH 15/57] Update macOS profile: RelWithDebInfo and Apple clang 17 Co-Authored-By: Claude Opus 4.6 (1M context) --- .conan/profiles/macos-latest-armv8.conanprofile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.conan/profiles/macos-latest-armv8.conanprofile b/.conan/profiles/macos-latest-armv8.conanprofile index 1d4e1262941..9859618cdb9 100644 --- a/.conan/profiles/macos-latest-armv8.conanprofile +++ b/.conan/profiles/macos-latest-armv8.conanprofile @@ -1,9 +1,9 @@ [settings] arch=armv8 -build_type=Release +build_type=RelWithDebInfo compiler=apple-clang compiler.cppstd=17 compiler.libcxx=libc++ -compiler.version=15 +compiler.version=17 os=Macos From 5e4edb618773c0de4912ebec853a6e708e4dec55 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 30 Sep 2026 16:59:10 +0200 Subject: [PATCH 16/57] Disable glib for macOS builds glib pulls in libffi which fails to build with Apple clang 17. glib is only needed on Linux (for D-Bus); on macOS harfbuzz works without it. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conanfile.py b/conanfile.py index eb7feef91b2..ff4b0de7610 100644 --- a/conanfile.py +++ b/conanfile.py @@ -28,6 +28,8 @@ def configure(self): if self.settings.os == "Linux": self.options['qt/*'].with_dbus = True self.options['qt/*'].qtwayland = True + if self.settings.os == "Macos": + self.options['harfbuzz/*'].with_glib = False def requirements(self): self.requires("extra-cmake-modules/6.8.0") From 4553988bd7bea893ca945b10dbd93ca92f786176 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 2 Oct 2026 19:10:26 +0200 Subject: [PATCH 17/57] Add AppImage packaging script for CI Standalone script that runs cmake --install into an AppDir and calls linuxdeploy to produce an AppImage. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100755 admin/linux/create-appimage.sh diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh new file mode 100755 index 00000000000..4be3be3213d --- /dev/null +++ b/admin/linux/create-appimage.sh @@ -0,0 +1,137 @@ +#!/bin/bash +set -euo pipefail + +usage() { + echo "Usage: $0 -b BUILD_DIR [-o OUTPUT_DIR] [-v VERSION]" + echo + echo "Create an AppImage from an already-built ownCloud client." + echo + echo "Options:" + echo " -b BUILD_DIR Path to the CMake build directory (required)" + echo " -o OUTPUT_DIR Where to place the resulting AppImage (default: .)" + echo " -v VERSION Version string for the AppImage filename (auto-detected from VERSION.cmake if omitted)" + exit 1 +} + +BUILD_DIR="" +OUTPUT_DIR="$(pwd)" +VERSION="" + +while getopts "b:o:v:h" opt; do + case "$opt" in + b) BUILD_DIR="$OPTARG" ;; + o) OUTPUT_DIR="$OPTARG" ;; + v) VERSION="$OPTARG" ;; + h) usage ;; + *) usage ;; + esac +done + +if [ -z "$BUILD_DIR" ]; then + echo "Error: -b BUILD_DIR is required" + usage +fi + +BUILD_DIR="$(cd "$BUILD_DIR" && pwd)" +OUTPUT_DIR="$(mkdir -p "$OUTPUT_DIR" && cd "$OUTPUT_DIR" && pwd)" +SOURCE_DIR="$(cd "$(dirname "$0")/../.." && pwd)" + +# Auto-detect version from VERSION.cmake if not provided +if [ -z "$VERSION" ]; then + version_file="$SOURCE_DIR/VERSION.cmake" + if [ ! -f "$version_file" ]; then + echo "Error: VERSION.cmake not found at $version_file and no -v given" + exit 1 + fi + MAJOR=$(grep 'MIRALL_VERSION_MAJOR' "$version_file" | sed 's/.*MIRALL_VERSION_MAJOR[[:space:]]*\([0-9]*\).*/\1/') + MINOR=$(grep 'MIRALL_VERSION_MINOR' "$version_file" | sed 's/.*MIRALL_VERSION_MINOR[[:space:]]*\([0-9]*\).*/\1/') + PATCH=$(grep 'MIRALL_VERSION_PATCH' "$version_file" | sed 's/.*MIRALL_VERSION_PATCH[[:space:]]*\([0-9]*\).*/\1/') + VERSION="${MAJOR}.${MINOR}.${PATCH}" + echo "Auto-detected version: $VERSION" +fi + +# Read APPLICATION_EXECUTABLE from the branding/OEM.cmake or OWNCLOUD.cmake +APP_EXECUTABLE="" +for cmake_file in "$SOURCE_DIR/branding/OEM.cmake" "$SOURCE_DIR/OWNCLOUD.cmake"; do + if [ -f "$cmake_file" ]; then + APP_EXECUTABLE=$(grep 'APPLICATION_EXECUTABLE' "$cmake_file" | head -1 | sed 's/.*APPLICATION_EXECUTABLE[[:space:]]*"\([^"]*\)".*/\1/') + break + fi +done +if [ -z "$APP_EXECUTABLE" ]; then + echo "Error: could not determine APPLICATION_EXECUTABLE" + exit 1 +fi +echo "Application executable: $APP_EXECUTABLE" + +# Check that linuxdeploy is available +LINUXDEPLOY="${LINUXDEPLOY:-linuxdeploy-x86_64.AppImage}" +if ! command -v "$LINUXDEPLOY" &>/dev/null; then + if [ -x "$LINUXDEPLOY" ]; then + : # path is usable as-is + else + echo "Error: linuxdeploy not found. Set LINUXDEPLOY env var or put linuxdeploy-x86_64.AppImage on PATH." + exit 1 + fi +fi + +APPDIR="$(mktemp -d)/AppDir" +trap 'rm -rf "$(dirname "$APPDIR")"' EXIT + +echo "==> Installing into AppDir: $APPDIR" +cmake --install "$BUILD_DIR" --prefix "$APPDIR/usr" + +# Symlink etc into the AppDir root (linuxdeploy expects it there) +if [ -d "$APPDIR/usr/etc" ]; then + ln -sfn usr/etc "$APPDIR/etc" +fi + +# Create the runenv hook (replicates Craft's AppImagePackager runenv) +mkdir -p "$APPDIR/apprun-hooks" +cat > "$APPDIR/apprun-hooks/runenv-hook.sh" << 'HOOK' +XDG_DATA_DIRS="$this_dir/usr/share/:$XDG_DATA_DIRS:/usr/local/share:/usr/share" +export XDG_DATA_DIRS +FONTCONFIG_PATH="$(if [ -d /etc/fonts ]; then echo "/etc/fonts"; else echo "$this_dir/etc/fonts"; fi)" +export FONTCONFIG_PATH +PATH="$this_dir/usr/bin:$this_dir/usr/lib:$PATH" +export PATH +HOOK + +# Find the .desktop file +DESKTOP_FILE=$(find "$APPDIR/usr/share/applications" -name "*.desktop" | head -1) +if [ -z "$DESKTOP_FILE" ]; then + echo "Error: no .desktop file found in $APPDIR/usr/share/applications/" + exit 1 +fi +echo "Using desktop file: $DESKTOP_FILE" + +APPIMAGE_NAME="ownCloud-${VERSION}-x86_64.AppImage" + +# In Docker, FUSE is typically not available +if [ -f /.dockerenv ] || grep -q docker /proc/1/cgroup 2>/dev/null; then + export APPIMAGE_EXTRACT_AND_RUN=1 +fi + +export ARCH="${ARCH:-x86_64}" +export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" +export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" +export NO_STRIP=1 +export QMAKE="${QMAKE:-$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || echo qmake)}" + +echo "==> Running linuxdeploy" +"$LINUXDEPLOY" \ + --appdir "$APPDIR" \ + --desktop-file "$DESKTOP_FILE" \ + --output=appimage \ + --plugin=qt + +# linuxdeploy creates the AppImage in the current directory +# Move it to the output directory with the desired name +GENERATED=$(ls -t *.AppImage 2>/dev/null | head -1) +if [ -n "$GENERATED" ] && [ -f "$GENERATED" ]; then + mv "$GENERATED" "$OUTPUT_DIR/$APPIMAGE_NAME" + echo "==> AppImage created: $OUTPUT_DIR/$APPIMAGE_NAME" +else + echo "Error: linuxdeploy did not produce an AppImage" + exit 1 +fi From ab9e100cb64c2bcb85c7a4e44a6b40ce8271b876 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 2 Oct 2026 19:50:05 +0200 Subject: [PATCH 18/57] Find qmake in Conan cache for AppImage packaging When qmake is not on PATH, search the Conan cache as a fallback. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 4be3be3213d..2bc5f97d924 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -116,7 +116,10 @@ export ARCH="${ARCH:-x86_64}" export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" export NO_STRIP=1 -export QMAKE="${QMAKE:-$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || echo qmake)}" +if [ -z "${QMAKE:-}" ]; then + QMAKE=$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || find ~/.conan2 -name qmake6 -path '*/bin/*' 2>/dev/null | head -1 || echo qmake) +fi +export QMAKE echo "==> Running linuxdeploy" "$LINUXDEPLOY" \ From c8f0b2cb46b3d05177925bfcaddf249d288bcccc Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 2 Oct 2026 20:06:20 +0200 Subject: [PATCH 19/57] Set Qt plugin/QML/lib paths from qmake for linuxdeploy linuxdeploy-plugin-qt needs QT_PLUGIN_PATH and QML2_IMPORT_PATH to find Qt plugins when Qt is installed in a non-standard location (e.g. Conan cache). Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 2bc5f97d924..cc818c90efc 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -121,6 +121,14 @@ if [ -z "${QMAKE:-}" ]; then fi export QMAKE +QT_PREFIX="$("$QMAKE" -query QT_INSTALL_PREFIX 2>/dev/null || true)" +if [ -n "$QT_PREFIX" ]; then + echo "Qt prefix: $QT_PREFIX" + export QT_PLUGIN_PATH="${QT_PREFIX}/plugins:${QT_PLUGIN_PATH:-}" + export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" + export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" +fi + echo "==> Running linuxdeploy" "$LINUXDEPLOY" \ --appdir "$APPDIR" \ From 0da512202a1b3c353c030969ca5be7172cb68d04 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 2 Oct 2026 20:17:14 +0200 Subject: [PATCH 20/57] Create missing Qt plugin dirs to prevent linuxdeploy crash linuxdeploy-plugin-qt crashes with a filesystem_error when a plugin directory (e.g. printsupport) doesn't exist in the Qt prefix. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index cc818c90efc..ab564ff855a 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -127,6 +127,10 @@ if [ -n "$QT_PREFIX" ]; then export QT_PLUGIN_PATH="${QT_PREFIX}/plugins:${QT_PLUGIN_PATH:-}" export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" + # linuxdeploy-plugin-qt crashes if expected plugin dirs don't exist + for plugdir in printsupport; do + mkdir -p "${QT_PREFIX}/plugins/${plugdir}" + done fi echo "==> Running linuxdeploy" From b2eab264dec0b6ad38b3037901ce2a528a10f709 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 5 Oct 2026 18:48:11 +0200 Subject: [PATCH 21/57] Let linuxdeploy strip binaries in AppImage Removes NO_STRIP=1 so debug symbols are stripped, significantly reducing AppImage size. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index ab564ff855a..21fbc325b04 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -115,7 +115,6 @@ fi export ARCH="${ARCH:-x86_64}" export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" -export NO_STRIP=1 if [ -z "${QMAKE:-}" ]; then QMAKE=$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || find ~/.conan2 -name qmake6 -path '*/bin/*' 2>/dev/null | head -1 || echo qmake) fi From bc45e5ac7bf7efc9c4c6a1c8d3e37462595c6184 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 16:24:18 +0200 Subject: [PATCH 22/57] List AppDir contents before creating AppImage Split linuxdeploy into populate + package steps with a full file listing in between, to identify unnecessary files for size reduction. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 21fbc325b04..afe91f7141e 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -136,9 +136,20 @@ echo "==> Running linuxdeploy" "$LINUXDEPLOY" \ --appdir "$APPDIR" \ --desktop-file "$DESKTOP_FILE" \ - --output=appimage \ --plugin=qt +echo "==> AppDir contents:" +find "$APPDIR" -type f -printf '%10s %p\n' | sort -rn +echo "==> AppDir directory sizes:" +du -sh "$APPDIR"/usr/* +echo "==> AppDir total size:" +du -sh "$APPDIR" + +echo "==> Creating AppImage" +"$LINUXDEPLOY" \ + --appdir "$APPDIR" \ + --output=appimage + # linuxdeploy creates the AppImage in the current directory # Move it to the output directory with the desired name GENERATED=$(ls -t *.AppImage 2>/dev/null | head -1) From 4944b8d6d51ca0eabb1a9681b9d864710893363f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 16:40:02 +0200 Subject: [PATCH 23/57] Clean up AppDir before creating AppImage Remove dev files (headers, cmake, static libs), unnecessary Qt modules (Designer, Help, UiTools, Test, WaylandCompositor), tool translations, and other build artifacts to reduce AppImage size. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 50 +++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index afe91f7141e..6e34479716e 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -86,7 +86,7 @@ if [ -d "$APPDIR/usr/etc" ]; then ln -sfn usr/etc "$APPDIR/etc" fi -# Create the runenv hook (replicates Craft's AppImagePackager runenv) +# Create the runenv hook for runtime environment setup mkdir -p "$APPDIR/apprun-hooks" cat > "$APPDIR/apprun-hooks/runenv-hook.sh" << 'HOOK' XDG_DATA_DIRS="$this_dir/usr/share/:$XDG_DATA_DIRS:/usr/local/share:/usr/share" @@ -138,6 +138,54 @@ echo "==> Running linuxdeploy" --desktop-file "$DESKTOP_FILE" \ --plugin=qt +echo "==> Cleaning up AppDir" +cd "$APPDIR" + +# Dev files: headers, static libs, cmake, pkgconfig, build artifacts +find . -name '*.cmake' -delete +find . -name '*.pc' -delete +find . -name '*.la' -delete +find . -name '*.prl' -delete +find . -name '*.a' -delete +find . -name '*.h' -delete +find . -name '*.hpp' -delete +find . -name '*.cpp' -delete +find . -name '*.qmltypes' -delete +find . -name '*-qmlmodule.version' -delete +rm -rf usr/include usr/lib/cmake usr/lib/metatypes usr/lib/objects-* + +# Qt dev tools and unnecessary modules +rm -f usr/lib/libQt6Designer* usr/lib/libQt6Help* usr/lib/libQt6UiTools* +rm -f usr/lib/libQt6Test* usr/lib/libQt6QuickTest* +rm -f usr/lib/libQt6WaylandCompositor* usr/lib/libQt6WaylandEgl* + +# Qt plugins not needed at runtime +rm -rf usr/plugins/designer usr/plugins/qmllint usr/plugins/qmltooling +rm -f usr/plugins/sqldrivers/libqsqlmysql* + +# QML dev files and test modules +rm -rf usr/qml/Qt/test +find . -path '*/objects-RelWithDebInfo/*' -delete + +# Translations for Qt tools we don't ship +rm -f usr/translations/assistant_* usr/translations/designer_* +rm -f usr/translations/linguist_* usr/translations/qt_help_* +rm -f usr/translations/qtconnectivity_* usr/translations/qtlocation_* +rm -f usr/translations/qtmultimedia_* usr/translations/qtscript_* +rm -f usr/translations/qtserialport_* + +# Misc build/doc leftovers +rm -rf usr/share/man usr/share/doc usr/share/gtk-doc usr/share/gdb +rm -rf usr/share/info usr/share/aclocal usr/share/bash-completion +rm -rf usr/lib/gettext usr/share/gettext +rm -rf usr/share/pkgconfig +find . -name 'mkspecs' -type d -exec rm -rf {} + 2>/dev/null || true + +# Remove empty directories +find . -type d -empty -delete 2>/dev/null || true + +cd "$OLDPWD" + echo "==> AppDir contents:" find "$APPDIR" -type f -printf '%10s %p\n' | sort -rn echo "==> AppDir directory sizes:" From c0e1bde0fd46dec7bd05ab7b67e9002c311d3d37 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 17:46:38 +0200 Subject: [PATCH 24/57] Strip debug symbols from AppDir binaries The two-step linuxdeploy flow (populate then package) doesn't strip automatically. Add an explicit strip step in the cleanup phase. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 6e34479716e..52ec03d9004 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -181,6 +181,11 @@ rm -rf usr/lib/gettext usr/share/gettext rm -rf usr/share/pkgconfig find . -name 'mkspecs' -type d -exec rm -rf {} + 2>/dev/null || true +# Strip debug symbols from shared libraries and executables +echo "==> Stripping binaries" +find . -type f \( -name '*.so' -o -name '*.so.*' \) -exec strip --strip-debug {} + 2>/dev/null || true +find . -type f -executable -exec sh -c 'file "$1" | grep -q "ELF" && strip --strip-debug "$1"' _ {} \; 2>/dev/null || true + # Remove empty directories find . -type d -empty -delete 2>/dev/null || true From f5a057ef3cdbebdeb1aa3a66c813f9dd67acee7b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 18:35:46 +0200 Subject: [PATCH 25/57] Remove unnecessary Qt styles, modules, and translations from AppImage Remove FluentWinUI3 (Windows-only), Imagine, NativeStyle, Particles, VectorImage, ShaderTools, and additional tool translations to reduce AppImage size. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 52ec03d9004..ee6bdaf9552 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -158,11 +158,23 @@ rm -rf usr/include usr/lib/cmake usr/lib/metatypes usr/lib/objects-* rm -f usr/lib/libQt6Designer* usr/lib/libQt6Help* usr/lib/libQt6UiTools* rm -f usr/lib/libQt6Test* usr/lib/libQt6QuickTest* rm -f usr/lib/libQt6WaylandCompositor* usr/lib/libQt6WaylandEgl* +rm -f usr/lib/libQt6ShaderTools* +rm -f usr/lib/libQt6QuickControls2FluentWinUI3StyleImpl* +rm -f usr/lib/libQt6QuickControls2ImagineStyleImpl* +rm -f usr/lib/libQt6QuickParticles* +rm -f usr/lib/libQt6QuickVectorImage* # Qt plugins not needed at runtime rm -rf usr/plugins/designer usr/plugins/qmllint usr/plugins/qmltooling rm -f usr/plugins/sqldrivers/libqsqlmysql* +# QML styles and modules not needed on Linux +rm -rf usr/qml/QtQuick/Controls/FluentWinUI3 +rm -rf usr/qml/QtQuick/Controls/Imagine +rm -rf usr/qml/QtQuick/NativeStyle +rm -rf usr/qml/QtQuick/Particles +rm -rf usr/qml/QtQuick/VectorImage + # QML dev files and test modules rm -rf usr/qml/Qt/test find . -path '*/objects-RelWithDebInfo/*' -delete @@ -173,6 +185,7 @@ rm -f usr/translations/linguist_* usr/translations/qt_help_* rm -f usr/translations/qtconnectivity_* usr/translations/qtlocation_* rm -f usr/translations/qtmultimedia_* usr/translations/qtscript_* rm -f usr/translations/qtserialport_* +rm -f usr/translations/qtdeclarative_* usr/translations/qtquickcontrols_* # Misc build/doc leftovers rm -rf usr/share/man usr/share/doc usr/share/gtk-doc usr/share/gdb From 5843b02b4c3d6736a15d145bfedaa68da1566316 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 18:40:07 +0200 Subject: [PATCH 26/57] Remove PrintSupport from AppImage Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index ee6bdaf9552..65394ba6931 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -163,6 +163,7 @@ rm -f usr/lib/libQt6QuickControls2FluentWinUI3StyleImpl* rm -f usr/lib/libQt6QuickControls2ImagineStyleImpl* rm -f usr/lib/libQt6QuickParticles* rm -f usr/lib/libQt6QuickVectorImage* +rm -f usr/lib/libQt6PrintSupport* # Qt plugins not needed at runtime rm -rf usr/plugins/designer usr/plugins/qmllint usr/plugins/qmltooling From 9ffe0558f3effda3e08e98b4be98ceb674609bd5 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 18:54:01 +0200 Subject: [PATCH 27/57] Remove unnecessary X11 libs from AppImage These are pulled in by -devel packages in the build container but are not needed at runtime (Athena/Xt widgets, unused X extensions). Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 65394ba6931..bb33080795c 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -180,6 +180,16 @@ rm -rf usr/qml/QtQuick/VectorImage rm -rf usr/qml/Qt/test find . -path '*/objects-RelWithDebInfo/*' -delete +# X11 widget/extension libs pulled in from -devel packages. +# Review this list when upgrading Qt — new versions may need additional X11 libs. +rm -f usr/lib/libXt.so* usr/lib/libXaw.so* usr/lib/libXmu.so* usr/lib/libXmuu.so* +rm -f usr/lib/libXpm.so* usr/lib/libfontenc.so* usr/lib/libxkbfile.so* +rm -f usr/lib/libXRes.so* usr/lib/libXss.so* usr/lib/libXv.so* usr/lib/libXxf86vm.so* +rm -f usr/lib/libXinerama.so* usr/lib/libXdamage.so* usr/lib/libXcomposite.so* +rm -f usr/lib/libxcb-composite.so* usr/lib/libxcb-xinerama.so* +rm -f usr/lib/libxcb-res.so* usr/lib/libxcb-present.so* usr/lib/libxcb-ewmh.so* +rm -f usr/lib/libxcb-cursor.so* + # Translations for Qt tools we don't ship rm -f usr/translations/assistant_* usr/translations/designer_* rm -f usr/translations/linguist_* usr/translations/qt_help_* From 535c154c09107fee88d40722d51b03541f89cb11 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 19:00:19 +0200 Subject: [PATCH 28/57] Disable md4c in Qt build Markdown rendering in text widgets is not needed by the client. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index ff4b0de7610..8fa9087abf4 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,6 +21,7 @@ class ClientRecipe(ConanFile): "qt/*:with_libjpeg": "libjpeg", "qt/*:with_odbc": False, "qt/*:with_pq": False, + "qt/*:with_md4c": False, "shared": True, } From ebaeeaf21d2d7dece401a6d7df0b4d6bac08033c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Oct 2026 19:06:59 +0200 Subject: [PATCH 29/57] Revert md4c change, needs Conan package rebuild first Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 8fa9087abf4..ff4b0de7610 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,7 +21,6 @@ class ClientRecipe(ConanFile): "qt/*:with_libjpeg": "libjpeg", "qt/*:with_odbc": False, "qt/*:with_pq": False, - "qt/*:with_md4c": False, "shared": True, } From 7f2ba849538ae33ba8ebaf6c17c65b69373e00c8 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Oct 2026 16:44:40 +0200 Subject: [PATCH 30/57] Set GIO_MODULE_DIR to prevent loading system GIO modules The bundled glib crashes with a type registration assertion when it loads GIO modules compiled against a different system glib version. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index bb33080795c..be573425c81 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -95,6 +95,8 @@ FONTCONFIG_PATH="$(if [ -d /etc/fonts ]; then echo "/etc/fonts"; else echo "$thi export FONTCONFIG_PATH PATH="$this_dir/usr/bin:$this_dir/usr/lib:$PATH" export PATH +GIO_MODULE_DIR="$this_dir/usr/lib/gio/modules" +export GIO_MODULE_DIR HOOK # Find the .desktop file From daaa254fa40100d0f982774260d273e12f0eb630 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Oct 2026 17:03:07 +0200 Subject: [PATCH 31/57] Keep Qt libs that the binary links against transitively The Conan Qt recipe creates transitive link dependencies that pull Designer, Help, UiTools, QuickTest, ShaderTools, PrintSupport, and WaylandCompositor into the binary. Removing them from the AppDir causes crashes due to symbol resolution falling back to system libs. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index be573425c81..75411864a89 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -156,16 +156,16 @@ find . -name '*.qmltypes' -delete find . -name '*-qmlmodule.version' -delete rm -rf usr/include usr/lib/cmake usr/lib/metatypes usr/lib/objects-* -# Qt dev tools and unnecessary modules -rm -f usr/lib/libQt6Designer* usr/lib/libQt6Help* usr/lib/libQt6UiTools* -rm -f usr/lib/libQt6Test* usr/lib/libQt6QuickTest* -rm -f usr/lib/libQt6WaylandCompositor* usr/lib/libQt6WaylandEgl* -rm -f usr/lib/libQt6ShaderTools* +# Qt modules not needed at runtime but kept because the Conan Qt recipe +# creates transitive link dependencies that pull them into the binary: +# Designer, Help, UiTools, QuickTest, ShaderTools, PrintSupport, +# WaylandCompositor +# TODO: fix Conan Qt recipe to not create these transitive deps, then +# remove these libraries here. rm -f usr/lib/libQt6QuickControls2FluentWinUI3StyleImpl* rm -f usr/lib/libQt6QuickControls2ImagineStyleImpl* rm -f usr/lib/libQt6QuickParticles* rm -f usr/lib/libQt6QuickVectorImage* -rm -f usr/lib/libQt6PrintSupport* # Qt plugins not needed at runtime rm -rf usr/plugins/designer usr/plugins/qmllint usr/plugins/qmltooling From b081ab691bfc2f9ad36973f12ec2cee7f4341253 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Oct 2026 17:35:23 +0200 Subject: [PATCH 32/57] Disable unused Qt features: Designer, Assistant, markdown These require a Conan Qt package rebuild before they take effect. - designer/assistant: we only need linguist tools from qttools - md4c: markdown rendering in text widgets is not needed Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conanfile.py b/conanfile.py index ff4b0de7610..4409faf65e6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,6 +21,8 @@ class ClientRecipe(ConanFile): "qt/*:with_libjpeg": "libjpeg", "qt/*:with_odbc": False, "qt/*:with_pq": False, + "qt/*:with_md4c": False, + "qt/*:disabled_features": "designer assistant", "shared": True, } From b414c3565df1ade1079a1f3b2ea31435a7b8378b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Oct 2026 16:14:18 +0200 Subject: [PATCH 33/57] Upgrade to Qt 6.11.1 and bump pruned dependency versions - Qt 6.8.3 -> 6.11.1 - zlib 1.3.1 -> 1.3.2 - sqlite3 3.49.1 -> 3.51.3 - openssl 3.4.2 -> 3.4.8 - Minimum Qt version in CMakeLists bumped to 6.11 Requires CCI sync (owncloud/conan-center-index#65) and a Conan package rebuild before the client can be built. Co-Authored-By: Claude Opus 4.6 (1M context) --- CMakeLists.txt | 2 +- conanfile.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8459739903d..9dcf13d347d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ set(APPLE_SUPPRESS_X11_WARNING ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -find_package(QT 6.8 NAMES Qt6 COMPONENTS Core REQUIRED) +find_package(QT 6.11 NAMES Qt6 COMPONENTS Core REQUIRED) find_package(Qt6 COMPONENTS Core Concurrent Network Widgets Xml Quick Qml QuickWidgets QuickControls2 REQUIRED) find_package(Qt6 CONFIG REQUIRED COMPONENTS LinguistTools) diff --git a/conanfile.py b/conanfile.py index 4409faf65e6..d943eb4b044 100644 --- a/conanfile.py +++ b/conanfile.py @@ -35,11 +35,11 @@ def configure(self): def requirements(self): self.requires("extra-cmake-modules/6.8.0") - self.requires("zlib/1.3.1") - self.requires("sqlite3/3.49.1") - self.requires("openssl/3.4.2") + self.requires("zlib/1.3.2") + self.requires("sqlite3/3.51.3") + self.requires("openssl/3.4.8") self.requires("nlohmann_json/3.11.3") - self.requires("qt/6.8.3") + self.requires("qt/6.11.1") self.requires("kdsingleapplication/1.2.0") self.requires("qtkeychain/0.15.0") self.requires("libregraphapi/1.0.4") From 6509486496de5bc1705ec4a8551201713d33a0be Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Oct 2026 16:27:38 +0200 Subject: [PATCH 34/57] Bump cmake tool_requires to 3.31.12 Upstream pruned cmake/3.30.0. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index d943eb4b044..0b55c6168b8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -47,7 +47,7 @@ def requirements(self): self.requires("sparkle/2.7.0") def build_requirements(self): - self.tool_requires("cmake/3.30.0") + self.tool_requires("cmake/3.31.12") def layout(self): cmake_layout(self) From 6cf01273b4bbf35b03c3c4aa29683734c30acb7e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Oct 2026 17:32:34 +0200 Subject: [PATCH 35/57] Set macOS deployment target to 13.0 in Conan profile Matches CMAKE_OSX_DEPLOYMENT_TARGET in CMakeLists.txt and the minimum macOS version supported by Qt 6.11. Added cross-reference comments to keep both in sync. Co-Authored-By: Claude Opus 4.6 (1M context) --- .conan/profiles/macos-latest-armv8.conanprofile | 2 ++ CMakeLists.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/.conan/profiles/macos-latest-armv8.conanprofile b/.conan/profiles/macos-latest-armv8.conanprofile index 9859618cdb9..67da6519295 100644 --- a/.conan/profiles/macos-latest-armv8.conanprofile +++ b/.conan/profiles/macos-latest-armv8.conanprofile @@ -6,4 +6,6 @@ compiler.cppstd=17 compiler.libcxx=libc++ compiler.version=17 os=Macos +# Keep in sync with CMAKE_OSX_DEPLOYMENT_TARGET in CMakeLists.txt +os.version=13.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dcf13d347d..9bda1b0fc3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Set this before the first project definition +# Keep in sync with os.version in .conan/profiles/macos-*.conanprofile set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "Minimum macOS deployment version" FORCE) include(VERSION.cmake) From 0b8f0c7401d0552e0d640fe64530105a7c45c07c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 13:58:26 +0200 Subject: [PATCH 36/57] Remove QML designer files from AppImage 544 QML designer support files were being bundled by linuxdeploy-plugin-qt. These are only used by Qt Designer, not at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 75411864a89..64922c7ad8b 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -178,8 +178,9 @@ rm -rf usr/qml/QtQuick/NativeStyle rm -rf usr/qml/QtQuick/Particles rm -rf usr/qml/QtQuick/VectorImage -# QML dev files and test modules +# QML dev files, designer support, and test modules rm -rf usr/qml/Qt/test +find . -path '*/designer/*' -delete find . -path '*/objects-RelWithDebInfo/*' -delete # X11 widget/extension libs pulled in from -devel packages. From 6ea1cd75836c0514aef5edff705f2e11014a0b63 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 17:39:42 +0200 Subject: [PATCH 37/57] Use Conan-built libraries for linuxdeploy bundling Extract the Conan package lib paths from the binary's RPATH and prepend them to LD_LIBRARY_PATH before running linuxdeploy. This ensures linuxdeploy bundles the Conan-built glib (2.86) instead of the system glib from the build container, avoiding version conflicts on the target system. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 64922c7ad8b..cd178fb9411 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -115,7 +115,12 @@ if [ -f /.dockerenv ] || grep -q docker /proc/1/cgroup 2>/dev/null; then fi export ARCH="${ARCH:-x86_64}" -export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" +# Extract Conan package lib paths from the binary's RPATH so linuxdeploy +# bundles Conan-built libraries instead of system ones (e.g. glib). +CONAN_LIB_PATHS=$(readelf -d "$APPDIR/usr/bin/$APP_EXECUTABLE" 2>/dev/null \ + | grep -oP 'RPATH.*\[\K[^\]]+' \ + | tr ':' '\n' | grep conan2 | tr '\n' ':') +export LD_LIBRARY_PATH="${CONAN_LIB_PATHS}$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" if [ -z "${QMAKE:-}" ]; then QMAKE=$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || find ~/.conan2 -name qmake6 -path '*/bin/*' 2>/dev/null | head -1 || echo qmake) From d1f319d7716a10f5e5a714b41448cfee7ab9e87e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 17:56:55 +0200 Subject: [PATCH 38/57] Fix RPATH extraction: handle RUNPATH and pipefail readelf may report RUNPATH instead of RPATH. Also add || true to prevent pipefail from aborting on empty grep results. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index cd178fb9411..c14f8fea006 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -118,8 +118,8 @@ export ARCH="${ARCH:-x86_64}" # Extract Conan package lib paths from the binary's RPATH so linuxdeploy # bundles Conan-built libraries instead of system ones (e.g. glib). CONAN_LIB_PATHS=$(readelf -d "$APPDIR/usr/bin/$APP_EXECUTABLE" 2>/dev/null \ - | grep -oP 'RPATH.*\[\K[^\]]+' \ - | tr ':' '\n' | grep conan2 | tr '\n' ':') + | grep -oP '(?:RPATH|RUNPATH).*\[\K[^\]]+' \ + | tr ':' '\n' | grep conan2 | tr '\n' ':' || true) export LD_LIBRARY_PATH="${CONAN_LIB_PATHS}$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" if [ -z "${QMAKE:-}" ]; then From c366d73548384c626d9089a60c9d965641bfd3cb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 18:12:05 +0200 Subject: [PATCH 39/57] Use Python script to extract Conan lib paths from RPATH Replace fragile shell grep/sed pipeline with a Python script that reliably parses readelf output across distributions. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 8 ++++---- admin/linux/extract-conan-lib-paths.py | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100755 admin/linux/extract-conan-lib-paths.py diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index c14f8fea006..bb12c8b739c 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -117,10 +117,10 @@ fi export ARCH="${ARCH:-x86_64}" # Extract Conan package lib paths from the binary's RPATH so linuxdeploy # bundles Conan-built libraries instead of system ones (e.g. glib). -CONAN_LIB_PATHS=$(readelf -d "$APPDIR/usr/bin/$APP_EXECUTABLE" 2>/dev/null \ - | grep -oP '(?:RPATH|RUNPATH).*\[\K[^\]]+' \ - | tr ':' '\n' | grep conan2 | tr '\n' ':' || true) -export LD_LIBRARY_PATH="${CONAN_LIB_PATHS}$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" +CONAN_LIB_PATHS=$(python3 "$SOURCE_DIR/admin/linux/extract-conan-lib-paths.py" \ + "$APPDIR/usr/bin/$APP_EXECUTABLE" || true) +echo "Conan lib paths: $CONAN_LIB_PATHS" +export LD_LIBRARY_PATH="${CONAN_LIB_PATHS:+${CONAN_LIB_PATHS}:}$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" if [ -z "${QMAKE:-}" ]; then QMAKE=$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || find ~/.conan2 -name qmake6 -path '*/bin/*' 2>/dev/null | head -1 || echo qmake) diff --git a/admin/linux/extract-conan-lib-paths.py b/admin/linux/extract-conan-lib-paths.py new file mode 100755 index 00000000000..6d1da39af23 --- /dev/null +++ b/admin/linux/extract-conan-lib-paths.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +import subprocess, sys + +if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + sys.exit(1) + +out = subprocess.check_output(["readelf", "-d", sys.argv[1]], text=True) +for line in out.splitlines(): + if "RPATH" in line or "RUNPATH" in line: + paths = line.split("[")[1].rstrip("]") + print(":".join(p for p in paths.split(":") if "conan2" in p)) + break From 5d88aecc4d076ddfa5855b885fb10e23c1deac8b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 18:26:21 +0200 Subject: [PATCH 40/57] Copy Conan-built libs into AppDir before running linuxdeploy linuxdeploy resolves dependencies from system paths, not LD_LIBRARY_PATH. Pre-populate the AppDir with Conan-built shared libraries (glib, libffi, libsecret, etc.) so linuxdeploy finds them there instead of bundling incompatible system versions from the build container. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index bb12c8b739c..9e1b981ea7c 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -81,6 +81,21 @@ trap 'rm -rf "$(dirname "$APPDIR")"' EXIT echo "==> Installing into AppDir: $APPDIR" cmake --install "$BUILD_DIR" --prefix "$APPDIR/usr" +# Copy Conan-built shared libraries into AppDir so linuxdeploy bundles them +# instead of system libraries from the build container. +CONAN_LIB_PATHS=$(python3 "$SOURCE_DIR/admin/linux/extract-conan-lib-paths.py" \ + "$APPDIR/usr/bin/$APP_EXECUTABLE" || true) +if [ -n "$CONAN_LIB_PATHS" ]; then + echo "==> Copying Conan libraries into AppDir" + echo "$CONAN_LIB_PATHS" | tr ':' '\n' | while read -r libdir; do + [ -d "$libdir" ] || continue + for lib in "$libdir"/lib*.so*; do + [ -f "$lib" ] || continue + cp -n "$lib" "$APPDIR/usr/lib/" + done + done +fi + # Symlink etc into the AppDir root (linuxdeploy expects it there) if [ -d "$APPDIR/usr/etc" ]; then ln -sfn usr/etc "$APPDIR/etc" @@ -115,12 +130,7 @@ if [ -f /.dockerenv ] || grep -q docker /proc/1/cgroup 2>/dev/null; then fi export ARCH="${ARCH:-x86_64}" -# Extract Conan package lib paths from the binary's RPATH so linuxdeploy -# bundles Conan-built libraries instead of system ones (e.g. glib). -CONAN_LIB_PATHS=$(python3 "$SOURCE_DIR/admin/linux/extract-conan-lib-paths.py" \ - "$APPDIR/usr/bin/$APP_EXECUTABLE" || true) -echo "Conan lib paths: $CONAN_LIB_PATHS" -export LD_LIBRARY_PATH="${CONAN_LIB_PATHS:+${CONAN_LIB_PATHS}:}$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" +export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LINUXDEPLOY_OUTPUT_VERSION="$VERSION" if [ -z "${QMAKE:-}" ]; then QMAKE=$(command -v qmake6 2>/dev/null || command -v qmake 2>/dev/null || find ~/.conan2 -name qmake6 -path '*/bin/*' 2>/dev/null | head -1 || echo qmake) From 9084a8d55a298fcea1f57812f972c8880bdcd7fb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 19:06:59 +0200 Subject: [PATCH 41/57] Skip plugin directories when copying Conan libs into AppDir Only copy from lib directories, not plugin directories. Qt plugins are handled separately by linuxdeploy-plugin-qt via QT_PLUGIN_PATH. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 9e1b981ea7c..069db58e8af 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -89,6 +89,7 @@ if [ -n "$CONAN_LIB_PATHS" ]; then echo "==> Copying Conan libraries into AppDir" echo "$CONAN_LIB_PATHS" | tr ':' '\n' | while read -r libdir; do [ -d "$libdir" ] || continue + case "$libdir" in */plugins/*) continue ;; esac for lib in "$libdir"/lib*.so*; do [ -f "$lib" ] || continue cp -n "$lib" "$APPDIR/usr/lib/" From c2c2c16cef8ac64b1ee3113c9eae02f1445d1b7e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Oct 2026 19:19:24 +0200 Subject: [PATCH 42/57] Also copy Conan Qt plugins into AppDir linuxdeploy-plugin-qt expects plugins at usr/plugins/ when it finds the corresponding Qt libs in usr/lib/. Copy Qt plugins from Conan cache preserving the subdirectory structure. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 069db58e8af..2ef5c98b76b 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -89,11 +89,22 @@ if [ -n "$CONAN_LIB_PATHS" ]; then echo "==> Copying Conan libraries into AppDir" echo "$CONAN_LIB_PATHS" | tr ':' '\n' | while read -r libdir; do [ -d "$libdir" ] || continue - case "$libdir" in */plugins/*) continue ;; esac - for lib in "$libdir"/lib*.so*; do - [ -f "$lib" ] || continue - cp -n "$lib" "$APPDIR/usr/lib/" - done + case "$libdir" in + */plugins/*) + pluginsubdir="${libdir##*/plugins/}" + mkdir -p "$APPDIR/usr/plugins/$pluginsubdir" + for lib in "$libdir"/*.so*; do + [ -f "$lib" ] || continue + cp -n "$lib" "$APPDIR/usr/plugins/$pluginsubdir/" + done + ;; + *) + for lib in "$libdir"/lib*.so*; do + [ -f "$lib" ] || continue + cp -n "$lib" "$APPDIR/usr/lib/" + done + ;; + esac done fi From 8731f0f94d516eeea96966042d753dbd023c55b7 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 15:51:06 +0200 Subject: [PATCH 43/57] Add verbose linuxdeploy output and fix missing plugin dirs Enable --verbosity=2 for easier debugging. Create platforminputcontexts dir in both Qt prefix and AppDir to prevent linuxdeploy-plugin-qt crash. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 2ef5c98b76b..9decde3def3 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -156,13 +156,15 @@ if [ -n "$QT_PREFIX" ]; then export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" # linuxdeploy-plugin-qt crashes if expected plugin dirs don't exist - for plugdir in printsupport; do + for plugdir in printsupport platforminputcontexts; do mkdir -p "${QT_PREFIX}/plugins/${plugdir}" + mkdir -p "$APPDIR/usr/plugins/${plugdir}" done fi echo "==> Running linuxdeploy" "$LINUXDEPLOY" \ + --verbosity=2 \ --appdir "$APPDIR" \ --desktop-file "$DESKTOP_FILE" \ --plugin=qt From 95499c23715735b5d3d26566d93837a00d28c95b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 16:03:09 +0200 Subject: [PATCH 44/57] Copy all Qt plugins into AppDir before linuxdeploy linuxdeploy-plugin-qt crashes when it finds a Qt module whose plugin directory doesn't exist in the AppDir. Instead of listing individual dirs, copy the entire Qt plugins tree from the Conan Qt prefix. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 9decde3def3..0d579f3a1ee 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -155,11 +155,12 @@ if [ -n "$QT_PREFIX" ]; then export QT_PLUGIN_PATH="${QT_PREFIX}/plugins:${QT_PLUGIN_PATH:-}" export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" - # linuxdeploy-plugin-qt crashes if expected plugin dirs don't exist - for plugdir in printsupport platforminputcontexts; do - mkdir -p "${QT_PREFIX}/plugins/${plugdir}" - mkdir -p "$APPDIR/usr/plugins/${plugdir}" - done + # Copy all Qt plugin dirs into the AppDir. linuxdeploy-plugin-qt crashes + # with a filesystem_error if it encounters a plugin category that exists + # in the Qt prefix but not in the AppDir. + if [ -d "${QT_PREFIX}/plugins" ]; then + cp -rn "${QT_PREFIX}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true + fi fi echo "==> Running linuxdeploy" From ee0d165ec3120e22111c4eb73593b4437a10df07 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 16:15:27 +0200 Subject: [PATCH 45/57] Fix Qt plugin copy: ensure target dir exists, allow overwrite mkdir -p the plugins dir before copying, and drop -n flag so Conan-built plugins overwrite any system ones. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 0d579f3a1ee..ba1517b9ce6 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -159,7 +159,8 @@ if [ -n "$QT_PREFIX" ]; then # with a filesystem_error if it encounters a plugin category that exists # in the Qt prefix but not in the AppDir. if [ -d "${QT_PREFIX}/plugins" ]; then - cp -rn "${QT_PREFIX}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true + mkdir -p "$APPDIR/usr/plugins" + cp -r "${QT_PREFIX}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true fi fi From 0fdeb364dfeda38d27c1c63afd713c07fb066e8e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 16:29:51 +0200 Subject: [PATCH 46/57] Create all standard Qt plugin dirs in AppDir linuxdeploy-plugin-qt crashes when iterating a plugin category that doesn't exist as a directory, even if no plugins were built for it. Create all standard plugin subdirectories unconditionally. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index ba1517b9ce6..b4eae721cd3 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -155,13 +155,21 @@ if [ -n "$QT_PREFIX" ]; then export QT_PLUGIN_PATH="${QT_PREFIX}/plugins:${QT_PLUGIN_PATH:-}" export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" - # Copy all Qt plugin dirs into the AppDir. linuxdeploy-plugin-qt crashes - # with a filesystem_error if it encounters a plugin category that exists - # in the Qt prefix but not in the AppDir. + # Copy Qt plugins into AppDir and create all standard plugin dirs. + # linuxdeploy-plugin-qt crashes with a filesystem_error if it tries to + # iterate a plugin category directory that doesn't exist in the AppDir. + mkdir -p "$APPDIR/usr/plugins" if [ -d "${QT_PREFIX}/plugins" ]; then - mkdir -p "$APPDIR/usr/plugins" cp -r "${QT_PREFIX}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true fi + for plugdir in bearer egldeviceintegrations generic iconengines \ + imageformats platforminputcontexts platforms \ + platformthemes printsupport sqldrivers \ + tls wayland-decoration-client wayland-graphics-integration-client \ + wayland-graphics-integration-server wayland-shell-integration \ + xcbglintegrations; do + mkdir -p "$APPDIR/usr/plugins/$plugdir" + done fi echo "==> Running linuxdeploy" From 6b7ae7446f63a40a7694201e24e5d7189e75180f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 16:41:46 +0200 Subject: [PATCH 47/57] Add Qt bin/libexec to PATH for qmlimportscanner linuxdeploy-plugin-qt runs qmlimportscanner during QML deployment. It needs to be on PATH. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index b4eae721cd3..13a39f8f316 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -155,6 +155,7 @@ if [ -n "$QT_PREFIX" ]; then export QT_PLUGIN_PATH="${QT_PREFIX}/plugins:${QT_PLUGIN_PATH:-}" export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" + export PATH="${QT_PREFIX}/bin:${QT_PREFIX}/libexec:${PATH}" # Copy Qt plugins into AppDir and create all standard plugin dirs. # linuxdeploy-plugin-qt crashes with a filesystem_error if it tries to # iterate a plugin category directory that doesn't exist in the AppDir. From cc775d67ca59683a26ed322f81a8a19678d119ff Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 16:56:15 +0200 Subject: [PATCH 48/57] Use Conan Qt dir for PATH/plugins instead of qmake -query qmake -query QT_INSTALL_PREFIX returns the AppDir path when a qt.conf is present there. Derive the Qt Conan directory from qmake's actual filesystem location instead, so qmlimportscanner and plugins are found in the Conan cache. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 13a39f8f316..979bd1d9461 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -149,19 +149,24 @@ if [ -z "${QMAKE:-}" ]; then fi export QMAKE -QT_PREFIX="$("$QMAKE" -query QT_INSTALL_PREFIX 2>/dev/null || true)" +# Derive the Qt Conan prefix from qmake's location, not from qmake -query +# (which may report the AppDir path if qt.conf is present there). +QT_CONAN_DIR="$(dirname "$QMAKE")/.." +QT_CONAN_DIR="$(cd "$QT_CONAN_DIR" && pwd)" +QT_PREFIX="$("$QMAKE" -query QT_INSTALL_PREFIX 2>/dev/null || echo "$QT_CONAN_DIR")" if [ -n "$QT_PREFIX" ]; then echo "Qt prefix: $QT_PREFIX" - export QT_PLUGIN_PATH="${QT_PREFIX}/plugins:${QT_PLUGIN_PATH:-}" - export QML2_IMPORT_PATH="${QT_PREFIX}/qml:${QML2_IMPORT_PATH:-}" - export LD_LIBRARY_PATH="${QT_PREFIX}/lib:${LD_LIBRARY_PATH}" - export PATH="${QT_PREFIX}/bin:${QT_PREFIX}/libexec:${PATH}" + echo "Qt Conan dir: $QT_CONAN_DIR" + export QT_PLUGIN_PATH="${QT_CONAN_DIR}/plugins:${QT_PLUGIN_PATH:-}" + export QML2_IMPORT_PATH="${QT_CONAN_DIR}/qml:${QML2_IMPORT_PATH:-}" + export LD_LIBRARY_PATH="${QT_CONAN_DIR}/lib:${LD_LIBRARY_PATH}" + export PATH="${QT_CONAN_DIR}/bin:${QT_CONAN_DIR}/libexec:${PATH}" # Copy Qt plugins into AppDir and create all standard plugin dirs. # linuxdeploy-plugin-qt crashes with a filesystem_error if it tries to # iterate a plugin category directory that doesn't exist in the AppDir. mkdir -p "$APPDIR/usr/plugins" - if [ -d "${QT_PREFIX}/plugins" ]; then - cp -r "${QT_PREFIX}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true + if [ -d "${QT_CONAN_DIR}/plugins" ]; then + cp -r "${QT_CONAN_DIR}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true fi for plugdir in bearer egldeviceintegrations generic iconengines \ imageformats platforminputcontexts platforms \ From 1ca7878138568bfcfb65420752e7a39ad2af80a4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 17:11:17 +0200 Subject: [PATCH 49/57] =?UTF-8?q?Don't=20set=20QT=5FPLUGIN=5FPATH=20?= =?UTF-8?q?=E2=80=94=20let=20linuxdeploy=20use=20qmake=20-query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting QT_PLUGIN_PATH causes linuxdeploy-plugin-qt to iterate all plugin dirs in that path, crashing on missing subdirectories (e.g. printsupport when PrintSupport is disabled). Without QT_PLUGIN_PATH, it uses qmake -query QT_INSTALL_PLUGINS which works correctly. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 39 +++++++++++----------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 979bd1d9461..08a97a7b1b7 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -149,33 +149,18 @@ if [ -z "${QMAKE:-}" ]; then fi export QMAKE -# Derive the Qt Conan prefix from qmake's location, not from qmake -query -# (which may report the AppDir path if qt.conf is present there). -QT_CONAN_DIR="$(dirname "$QMAKE")/.." -QT_CONAN_DIR="$(cd "$QT_CONAN_DIR" && pwd)" -QT_PREFIX="$("$QMAKE" -query QT_INSTALL_PREFIX 2>/dev/null || echo "$QT_CONAN_DIR")" -if [ -n "$QT_PREFIX" ]; then - echo "Qt prefix: $QT_PREFIX" - echo "Qt Conan dir: $QT_CONAN_DIR" - export QT_PLUGIN_PATH="${QT_CONAN_DIR}/plugins:${QT_PLUGIN_PATH:-}" - export QML2_IMPORT_PATH="${QT_CONAN_DIR}/qml:${QML2_IMPORT_PATH:-}" - export LD_LIBRARY_PATH="${QT_CONAN_DIR}/lib:${LD_LIBRARY_PATH}" - export PATH="${QT_CONAN_DIR}/bin:${QT_CONAN_DIR}/libexec:${PATH}" - # Copy Qt plugins into AppDir and create all standard plugin dirs. - # linuxdeploy-plugin-qt crashes with a filesystem_error if it tries to - # iterate a plugin category directory that doesn't exist in the AppDir. - mkdir -p "$APPDIR/usr/plugins" - if [ -d "${QT_CONAN_DIR}/plugins" ]; then - cp -r "${QT_CONAN_DIR}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true - fi - for plugdir in bearer egldeviceintegrations generic iconengines \ - imageformats platforminputcontexts platforms \ - platformthemes printsupport sqldrivers \ - tls wayland-decoration-client wayland-graphics-integration-client \ - wayland-graphics-integration-server wayland-shell-integration \ - xcbglintegrations; do - mkdir -p "$APPDIR/usr/plugins/$plugdir" - done +# Derive the Qt Conan prefix from qmake's location. +QT_CONAN_DIR="$(cd "$(dirname "$QMAKE")/.." && pwd)" +echo "Qt Conan dir: $QT_CONAN_DIR" +export LD_LIBRARY_PATH="${QT_CONAN_DIR}/lib:${LD_LIBRARY_PATH}" +export PATH="${QT_CONAN_DIR}/bin:${QT_CONAN_DIR}/libexec:${PATH}" + +# Copy Qt plugins into AppDir. Don't set QT_PLUGIN_PATH — let +# linuxdeploy-plugin-qt find plugins via qmake -query instead, +# which avoids crashes on missing plugin subdirectories. +mkdir -p "$APPDIR/usr/plugins" +if [ -d "${QT_CONAN_DIR}/plugins" ]; then + cp -r "${QT_CONAN_DIR}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true fi echo "==> Running linuxdeploy" From e4ae166e177b57215c2248a1f171f38e9e314f96 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 17:22:43 +0200 Subject: [PATCH 50/57] Create missing plugin dirs in both AppDir and Conan Qt prefix linuxdeploy-plugin-qt iterates plugin dirs from qmake -query and crashes on any missing subdirectory. Create all standard Qt plugin directories in both locations. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 08a97a7b1b7..68b6cad9c13 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -155,14 +155,25 @@ echo "Qt Conan dir: $QT_CONAN_DIR" export LD_LIBRARY_PATH="${QT_CONAN_DIR}/lib:${LD_LIBRARY_PATH}" export PATH="${QT_CONAN_DIR}/bin:${QT_CONAN_DIR}/libexec:${PATH}" -# Copy Qt plugins into AppDir. Don't set QT_PLUGIN_PATH — let -# linuxdeploy-plugin-qt find plugins via qmake -query instead, -# which avoids crashes on missing plugin subdirectories. +# Copy Qt plugins into AppDir. mkdir -p "$APPDIR/usr/plugins" if [ -d "${QT_CONAN_DIR}/plugins" ]; then cp -r "${QT_CONAN_DIR}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true fi +# linuxdeploy-plugin-qt iterates plugin dirs reported by qmake -query and +# crashes if any expected subdirectory doesn't exist. Create all standard +# plugin dirs in both locations. +for plugdir in bearer egldeviceintegrations generic iconengines \ + imageformats platforminputcontexts platforms \ + platformthemes printsupport sqldrivers \ + tls wayland-decoration-client wayland-graphics-integration-client \ + wayland-graphics-integration-server wayland-shell-integration \ + xcbglintegrations; do + mkdir -p "$APPDIR/usr/plugins/$plugdir" + mkdir -p "${QT_CONAN_DIR}/plugins/$plugdir" +done + echo "==> Running linuxdeploy" "$LINUXDEPLOY" \ --verbosity=2 \ From d500c4bd2dfab50e8866e7e2477fface727a7c00 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 17:37:33 +0200 Subject: [PATCH 51/57] Preserve symlinks when copying Conan libs into AppDir cp without -a dereferences symlinks, tripling the AppDir size (libQt6Gui.so, .so.6, .so.6.11.1 each as full copies instead of the last one being real and the others being symlinks). Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 68b6cad9c13..58a1983dd08 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -93,16 +93,10 @@ if [ -n "$CONAN_LIB_PATHS" ]; then */plugins/*) pluginsubdir="${libdir##*/plugins/}" mkdir -p "$APPDIR/usr/plugins/$pluginsubdir" - for lib in "$libdir"/*.so*; do - [ -f "$lib" ] || continue - cp -n "$lib" "$APPDIR/usr/plugins/$pluginsubdir/" - done + cp -an "$libdir"/*.so* "$APPDIR/usr/plugins/$pluginsubdir/" 2>/dev/null || true ;; *) - for lib in "$libdir"/lib*.so*; do - [ -f "$lib" ] || continue - cp -n "$lib" "$APPDIR/usr/lib/" - done + cp -an "$libdir"/lib*.so* "$APPDIR/usr/lib/" 2>/dev/null || true ;; esac done @@ -158,7 +152,7 @@ export PATH="${QT_CONAN_DIR}/bin:${QT_CONAN_DIR}/libexec:${PATH}" # Copy Qt plugins into AppDir. mkdir -p "$APPDIR/usr/plugins" if [ -d "${QT_CONAN_DIR}/plugins" ]; then - cp -r "${QT_CONAN_DIR}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true + cp -a "${QT_CONAN_DIR}/plugins/"* "$APPDIR/usr/plugins/" 2>/dev/null || true fi # linuxdeploy-plugin-qt iterates plugin dirs reported by qmake -query and From 913a45797570b4c401c4ddf637ca93020b30c8cd Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Oct 2026 18:30:28 +0200 Subject: [PATCH 52/57] Disable Qt glib integration on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Qt's glib event loop integration causes a constructor ordering crash in AppImage: Qt6Core initializes before libgobject and calls gobject type system functions that aren't ready yet. The client doesn't need glib integration — Qt's own event loop works for all our use cases. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 0b55c6168b8..0bd310fdc27 100644 --- a/conanfile.py +++ b/conanfile.py @@ -29,6 +29,7 @@ class ClientRecipe(ConanFile): def configure(self): if self.settings.os == "Linux": self.options['qt/*'].with_dbus = True + self.options['qt/*'].with_glib = False self.options['qt/*'].qtwayland = True if self.settings.os == "Macos": self.options['harfbuzz/*'].with_glib = False From 1398b0af1212ea42a28264d525fd3369c0350a1b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 13 Oct 2026 18:55:53 +0200 Subject: [PATCH 53/57] Force all Conan dependencies to build as shared libraries glib was statically linked into libownCloudLibSync, creating duplicate glib symbols that shadowed the dynamic glib at runtime. This caused gobject's type system initialization to fail because it used the uninitialized static copy instead of the initialized dynamic one. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 0bd310fdc27..e0cc66f5cca 100644 --- a/conanfile.py +++ b/conanfile.py @@ -7,6 +7,7 @@ class ClientRecipe(ConanFile): options = {"shared": [True, False]} default_options = { "*:fPIC": True, + "*:shared": True, "qt/*:gui": True, "qt/*:qtdeclarative": True, "qt/*:qtimageformats": True, From 995f96b61ae2f6fca4c864089b34f439c12e2049 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Oct 2026 15:40:29 +0200 Subject: [PATCH 54/57] Build glib as shared on Linux to prevent static symbol duplication glib was statically linked into client libraries, causing duplicate glib symbols that crashed gobject's type system initialization in AppImage. Only needed on Linux where glib is a transitive dependency via libsecret. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index e0cc66f5cca..ec676d9cd16 100644 --- a/conanfile.py +++ b/conanfile.py @@ -7,7 +7,6 @@ class ClientRecipe(ConanFile): options = {"shared": [True, False]} default_options = { "*:fPIC": True, - "*:shared": True, "qt/*:gui": True, "qt/*:qtdeclarative": True, "qt/*:qtimageformats": True, @@ -32,6 +31,7 @@ def configure(self): self.options['qt/*'].with_dbus = True self.options['qt/*'].with_glib = False self.options['qt/*'].qtwayland = True + self.options['glib/*'].shared = True if self.settings.os == "Macos": self.options['harfbuzz/*'].with_glib = False From 2c82008ed3accb51986cc7b53680b63a7c6bde19 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Oct 2026 16:27:20 +0200 Subject: [PATCH 55/57] Disable SELinux support in glib Avoids building libselinux from source (persistent download failures). The client doesn't need glib's SELinux file label support. Co-Authored-By: Claude Opus 4.6 (1M context) --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index ec676d9cd16..455b8058057 100644 --- a/conanfile.py +++ b/conanfile.py @@ -32,6 +32,7 @@ def configure(self): self.options['qt/*'].with_glib = False self.options['qt/*'].qtwayland = True self.options['glib/*'].shared = True + self.options['glib/*'].with_selinux = False if self.settings.os == "Macos": self.options['harfbuzz/*'].with_glib = False From 61f571dd7ebdf10bf9fe67af6ad7df88fa60967e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 15 Oct 2026 15:07:31 +0200 Subject: [PATCH 56/57] Clean up create-appimage.sh: remove debug verbosity and GIO_MODULE_DIR Remove --verbosity=2 from linuxdeploy (debug noise). Remove GIO_MODULE_DIR from runenv hook (no longer needed with glib built as shared). Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/linux/create-appimage.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/admin/linux/create-appimage.sh b/admin/linux/create-appimage.sh index 58a1983dd08..ce3e7656c6c 100755 --- a/admin/linux/create-appimage.sh +++ b/admin/linux/create-appimage.sh @@ -116,8 +116,6 @@ FONTCONFIG_PATH="$(if [ -d /etc/fonts ]; then echo "/etc/fonts"; else echo "$thi export FONTCONFIG_PATH PATH="$this_dir/usr/bin:$this_dir/usr/lib:$PATH" export PATH -GIO_MODULE_DIR="$this_dir/usr/lib/gio/modules" -export GIO_MODULE_DIR HOOK # Find the .desktop file @@ -170,7 +168,6 @@ done echo "==> Running linuxdeploy" "$LINUXDEPLOY" \ - --verbosity=2 \ --appdir "$APPDIR" \ --desktop-file "$DESKTOP_FILE" \ --plugin=qt From 5ac5246974b25ec098fdba8a0f0738fbd917a388 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 16 Oct 2026 18:47:16 +0200 Subject: [PATCH 57/57] Add macOS packaging script for Conan builds Python script that produces a .pkg installer from a conan build output. Handles cmake install, dylib bundling, install name fixup, macdeployqt, Qt translations, and packagesbuild. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/osx/create_macos_pkg.py | 410 ++++++++++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100755 admin/osx/create_macos_pkg.py diff --git a/admin/osx/create_macos_pkg.py b/admin/osx/create_macos_pkg.py new file mode 100755 index 00000000000..92677a77494 --- /dev/null +++ b/admin/osx/create_macos_pkg.py @@ -0,0 +1,410 @@ +#!/usr/bin/env python3 +"""macOS packaging script for the ownCloud/Kiteworks desktop client. + +Produces a .pkg installer from a completed conan build. + +Prerequisites: + - A completed `conan build .` with the macOS profile + - packagesbuild (from http://s.sudre.free.fr/Software/Packages/about.html) + +Usage: build_macos_pkg.py --build-dir --output-dir +""" + +import argparse +import os +import re +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + + +def run(cmd, **kwargs): + print(f" $ {' '.join(str(c) for c in cmd)}") + return subprocess.run(cmd, check=True, **kwargs) + + +def otool_deps(binary): + """Return list of dylib references from otool -L.""" + result = subprocess.run( + ["otool", "-L", str(binary)], capture_output=True, text=True + ) + deps = [] + for line in result.stdout.splitlines()[1:]: + match = re.match(r"\s+(\S+)", line) + if match: + deps.append(match.group(1)) + return deps + + +def otool_rpaths(binary): + """Return list of LC_RPATH entries from a Mach-O binary.""" + result = subprocess.run( + ["otool", "-l", str(binary)], capture_output=True, text=True + ) + rpaths = [] + lines = result.stdout.splitlines() + for i, line in enumerate(lines): + if "LC_RPATH" in line and i + 2 < len(lines): + match = re.match(r"\s+path\s+(\S+)", lines[i + 2]) + if match: + rpaths.append(match.group(1)) + return rpaths + + +def resolve_rpath_ref(ref, source_binary): + """Resolve an @rpath/ reference to an actual file path.""" + libname = ref.removeprefix("@rpath/") + for rpath in otool_rpaths(source_binary): + resolved = rpath.replace("@loader_path", str(source_binary.parent)) + candidate = Path(resolved) / libname + if candidate.exists(): + return candidate + return None + + +def is_qt_ref(ref): + return "/Qt" in ref or ref.startswith("@rpath/libQt") + + +def is_system_ref(ref): + return ref.startswith("/usr/lib/") or ref.startswith("/System/Library/") + + +def copy_lib(src, dest_dir): + """Copy a dylib, preserving symlinks.""" + dest = dest_dir / src.name + if dest.exists(): + return + if src.is_symlink(): + linkto = os.readlink(src) + os.symlink(linkto, dest) + else: + shutil.copy2(src, dest) + + +def copy_client_libs(install_dir, frameworks_dir): + """Copy client dylibs (real files + symlinks) into Frameworks.""" + print("=== Copying client libraries ===") + lib_dir = install_dir / "lib" + if not lib_dir.exists(): + return + real_files = sorted(p for p in lib_dir.glob("*.dylib") if not p.is_symlink()) + symlinks = sorted(p for p in lib_dir.glob("*.dylib") if p.is_symlink()) + for lib in real_files: + print(f" {lib.name}") + copy_lib(lib, frameworks_dir) + for lib in symlinks: + copy_lib(lib, frameworks_dir) + + +def copy_qml_modules(install_dir, resources_dir): + """Copy client QML modules into Resources/qml.""" + print("=== Copying QML modules ===") + qml_src = install_dir / "lib" / "qml" + if not qml_src.exists(): + return + qml_dest = resources_dir / "qml" + shutil.copytree(qml_src, qml_dest, dirs_exist_ok=True, symlinks=True) + + +def resolve_conan_deps(executable, frameworks_dir): + """Find and copy non-Qt Conan dependencies into Frameworks.""" + print("=== Resolving Conan library dependencies ===") + binaries_to_scan = [executable] + scanned = set() + + while binaries_to_scan: + binary = binaries_to_scan.pop(0) + if binary in scanned: + continue + scanned.add(binary) + + for ref in otool_deps(binary): + if not ref.startswith("@rpath/"): + continue + if is_qt_ref(ref): + continue + + libname = ref.removeprefix("@rpath/") + dest = frameworks_dir / libname + if dest.exists(): + continue + + resolved = resolve_rpath_ref(ref, binary) + if resolved: + print(f" {libname}") + copy_lib(resolved, frameworks_dir) + if not resolved.is_symlink(): + binaries_to_scan.append(dest) + else: + print(f" WARNING: could not resolve {ref}") + + +def fix_install_names(binary, frameworks_dir): + """Rewrite dylib references to use @executable_path/../Frameworks/.""" + changes = [] + for ref in otool_deps(binary): + if is_system_ref(ref): + continue + + base = Path(ref).name + + # Absolute paths to libs we have in Frameworks + if not ref.startswith("@") and (frameworks_dir / base).exists(): + new = f"@executable_path/../Frameworks/{base}" + changes.append(("-change", ref, new)) + + # @rpath references to non-Qt libs we copied + elif ref.startswith("@rpath/") and not is_qt_ref(ref): + libname = ref.removeprefix("@rpath/") + if (frameworks_dir / libname).exists(): + new = f"@executable_path/../Frameworks/{libname}" + changes.append(("-change", ref, new)) + + if changes: + args = [] + for change in changes: + args.extend(change) + print(f" {change[1]} -> {Path(change[2]).name} (in {binary.name})") + subprocess.run( + ["install_name_tool"] + args + [str(binary)], check=True + ) + + # Fix the library's own id + if binary.suffix == ".dylib": + new_id = f"@executable_path/../Frameworks/{binary.name}" + subprocess.run( + ["install_name_tool", "-id", new_id, str(binary)], check=True + ) + + +def fix_all_install_names(executable, frameworks_dir, qml_dir): + """Fix install names for the executable, all Frameworks dylibs, and QML plugins.""" + print("=== Fixing install names ===") + + fix_install_names(executable, frameworks_dir) + + for lib in sorted(frameworks_dir.glob("*.dylib")): + if lib.is_symlink(): + continue + fix_install_names(lib, frameworks_dir) + + if qml_dir.exists(): + for lib in sorted(qml_dir.rglob("*.dylib")): + if lib.is_symlink(): + continue + fix_install_names(lib, frameworks_dir) + + +def find_macdeployqt(build_dir): + """Find macdeployqt by sourcing the Conan build environment.""" + generators = build_dir / "generators" + env_scripts = sorted(generators.glob("conanbuildenv-*.sh")) + run_scripts = sorted(generators.glob("conanrunenv-*.sh")) + + env = os.environ.copy() + for script in env_scripts + run_scripts: + result = subprocess.run( + ["bash", "-c", f"source {script} && env"], + capture_output=True, text=True + ) + for line in result.stdout.splitlines(): + key, _, value = line.partition("=") + if key: + env[key] = value + + for path_dir in env.get("PATH", "").split(":"): + candidate = Path(path_dir) / "macdeployqt" + if candidate.is_file(): + return candidate, env + + return None, env + + +def run_macdeployqt(macdeployqt, app_bundle, frameworks_dir, qml_dir): + """Run macdeployqt with -executable= for each bundled dylib.""" + print("=== Running macdeployqt ===") + + qt_lib_dir = macdeployqt.parent.parent / "lib" + + exec_args = [] + for lib in sorted(frameworks_dir.glob("*.dylib")): + if lib.is_symlink(): + continue + exec_args.append(f"-executable={lib}") + + cmd = [str(macdeployqt), str(app_bundle)] + exec_args + cmd.append(f"-libpath={qt_lib_dir}") + if qml_dir.exists(): + cmd.append(f"-qmldir={qml_dir}") + + result = subprocess.run(cmd, capture_output=True, text=True) + # macdeployqt prints ERRORs for non-Mach-O files (plists, icons) — harmless + for line in result.stderr.splitlines() + result.stdout.splitlines(): + if "ERROR" in line and "is not an object file" not in line: + print(f" macdeployqt: {line.strip()}") + + # Clean up unwanted plugins + unwanted = [ + app_bundle / "Contents/PlugIns/sqldrivers/libqsqlpsql.dylib", + app_bundle / "Contents/PlugIns/sqldrivers/libqsqlodbc.dylib", + ] + for path in unwanted: + if path.exists(): + print(f" Removing {path.name}") + path.unlink() + + +def copy_translations(macdeployqt, resources_dir): + """Copy Qt translations into the bundle.""" + print("=== Copying translations ===") + qt_translations = macdeployqt.parent.parent / "translations" + translations_dir = resources_dir / "Translations" + translations_dir.mkdir(exist_ok=True) + + if qt_translations.is_dir(): + for qm in sorted(qt_translations.glob("*.qm")): + shutil.copy2(qm, translations_dir) + print(f" Qt: {len(list(qt_translations.glob('*.qm')))} translation files") + else: + print(" WARNING: Qt translations not found") + + # TODO: qtkeychain translations are not included in the Conan package. + # Fix the qtkeychain recipe to install them, then copy here. + + +def write_qt_conf(resources_dir): + print("=== Writing qt.conf ===") + qt_conf = resources_dir / "qt.conf" + qt_conf.write_text( + "[Paths]\n" + "Prefix=..\n" + "Binaries=MacOS\n" + "Libraries=Frameworks\n" + "Plugins=PlugIns\n" + "Qml2Imports=Resources/qml\n" + "Translations=Resources/Translations\n" + ) + + +def create_pkg(build_dir, app_bundle, output_dir): + print("=== Creating .pkg installer ===") + create_mac = build_dir / "admin" / "osx" / "create_mac.sh" + if not create_mac.exists(): + print(f"Warning: create_mac.sh not found at {create_mac}") + print("Run create_mac.sh manually after installing packagesbuild.") + return False + + # create_mac.sh expects the .app to be directly inside the install path + app_parent = app_bundle.parent + create_mac.chmod(0o755) + result = subprocess.run( + [str(create_mac), str(app_parent), str(build_dir)], + capture_output=True, text=True + ) + print(result.stdout.strip()) + if result.returncode != 0: + print(result.stderr.strip()) + return False + + # packagesbuild writes the .pkg to BUILD_PATH from the .pkgproj, which is + # CMAKE_INSTALL_PREFIX. Parse it from the CMake cache. + pkg_search_dirs = [app_parent] + cmake_cache = build_dir / "CMakeCache.txt" + if cmake_cache.exists(): + for line in cmake_cache.read_text().splitlines(): + if line.startswith("CMAKE_INSTALL_PREFIX:PATH="): + prefix = Path(line.split("=", 1)[1]) + if prefix.is_dir(): + pkg_search_dirs.append(prefix) + + for search_dir in pkg_search_dirs: + for pkg in search_dir.glob("*.pkg"): + dest = output_dir / pkg.name + shutil.move(pkg, dest) + print(f" Package: {dest}") + return True + + print(" WARNING: .pkg file not found after build") + return False + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--build-dir", required=True, type=Path, + help="Conan build output (e.g. client/build/RelWithDebInfo)", + ) + parser.add_argument( + "--output-dir", required=True, type=Path, + help="Where to write the .pkg file", + ) + args = parser.parse_args() + + build_dir = args.build_dir.resolve() + output_dir = args.output_dir.resolve() + output_dir.mkdir(parents=True, exist_ok=True) + + if not build_dir.is_dir(): + sys.exit(f"Error: build directory not found: {build_dir}") + + macdeployqt, env = find_macdeployqt(build_dir) + if not macdeployqt: + sys.exit("Error: macdeployqt not found via Conan build environment") + print(f"Using macdeployqt: {macdeployqt}") + os.environ.update(env) + + # 1. cmake --install + print("=== Installing to staging directory ===") + install_dir = Path(tempfile.mkdtemp()) / "install" + run(["cmake", "--install", str(build_dir), "--prefix", str(install_dir)]) + + app_bundles = list(install_dir.rglob("*.app")) + if not app_bundles: + sys.exit(f"Error: no .app bundle found in {install_dir}") + app_bundle = app_bundles[0] + print(f"Found: {app_bundle}") + + contents = app_bundle / "Contents" + frameworks = contents / "Frameworks" + resources = contents / "Resources" + macos = contents / "MacOS" + frameworks.mkdir(exist_ok=True) + resources.mkdir(exist_ok=True) + + executables = [p for p in macos.iterdir() if p.is_file() and os.access(p, os.X_OK)] + if not executables: + sys.exit("Error: no executable found in Contents/MacOS") + executable = executables[0] + print(f"Main executable: {executable}") + + # 2-3. Copy libs and QML + copy_client_libs(install_dir, frameworks) + copy_qml_modules(install_dir, resources) + + # 4. Resolve Conan deps + resolve_conan_deps(executable, frameworks) + + # 5. Fix install names + fix_all_install_names(executable, frameworks, resources / "qml") + + # 6. macdeployqt + run_macdeployqt(macdeployqt, app_bundle, frameworks, resources / "qml") + + # 7. Translations + copy_translations(macdeployqt, resources) + + # 8. qt.conf + write_qt_conf(resources) + + # 9. .pkg + if not create_pkg(build_dir, app_bundle, output_dir): + print(f"The .app bundle is ready at: {app_bundle}") + + print("=== Done ===") + + +if __name__ == "__main__": + main()