Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Installation and Deployment
  4. How to embed custom resources into the installer used for online package updates (QtIFW)
Forum Updated to NodeBB v4.3 + New Features

How to embed custom resources into the installer used for online package updates (QtIFW)

Scheduled Pinned Locked Moved Unsolved Installation and Deployment
1 Posts 1 Posters 95 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • FlincoF Offline
    FlincoF Offline
    Flinco
    wrote on last edited by Flinco
    #1

    Hello everyone,

    I am working with the Qt Installer Framework on Windows and I am facing an issue I cannot fully understand or solve. I hope someone here can clarify whether what I want to achieve is possible, or if I am approaching the problem incorrectly.

    Context / What I am doing

    • I developed a C++ Qt application for Windows.
    • I use QtIFW both for installation and for updating via the Maintenance Tool.
    • During the installation phase only, I need to run a small helper executable (a custom tool).
    • I decided to embed this executable into the installer using the -r <file.qrc> option of binarycreator.
      • When building the offline or hybrid installer, the resource is correctly included.
      • My controller script successfully extracts the embedded file using the undocumented syntax ":/<filename>".
      • The component script can execute the tool, and I can see its effects.
      • When launching the installer from the command line, debug logs confirm that the embedded file is extracted properly.

    Goal / Desired behavior

    I need the same embedded resource to also be available in the installer that gets deployed when the user updates via the online repository, following the procedure described in:
    https://doc.qt.io/qtinstallerframework/ifw-updates.html (see: “Promoting Updates for the Maintenance Tool”).

    More precisely:
    after running repogen, the Maintenance Tool downloads a new installer (the replacement for the existing one).

    I want this new installer to also contain the same embedded resource—just like the offline/hybrid installer built with -r.

    Problem

    I cannot find a way to embed the custom resource into the installer that is produced for online updates.

    When generating update.rcc (the resource file used for online updates), I run:

    binarycreator -c config/config.xml -p packages -rcc
    

    However, if binarycreator is invoked with -rcc, all -r <resource.qrc> arguments are ignored.
    This is an hardcoded behavior (I checked the sources of binarycreator) —binarycreator does not merge extra resources into update.rcc.

    As far as I can see, this makes it impossible to embed arbitrary custom executables into the online-update installer, unless QtIFW itself is modified.

    Questions

    • Is there any supported mechanism to include additional resources inside the installer (the one deployed through online updates)?
    • Is there perhaps a different workflow for embedding custom executables into the update installer?
    • Or is it correct that, at the moment, binarycreator simply ignores -r when -rcc is used, making this impossible?
    • If so, is there a recommended workaround?

    Notes

    I am posting (her below) my CMake setup (large, but provided for completeness). Some sections—such as the custom launcher—are unrelated to the issue.

    Additional thought

    It seems logical that update.rcc should contain my custom resource, but because binarycreator -rcc discards all -r options, this cannot be achieved unless QtIFW changes this behavior.
    If anyone knows whether there is an internal option, undocumented parameter, or alternative approach, I would be grateful.

    Thanks in advance!

    — Lorenzo

    My CMakeLists.txt

    ##############################################################################
     Copyright (C) 2021 by Lorenzo Buzzi (lorenzo@buzzi.pro)                    #
    #                                                                            #
    # This program is free software: you can redistribute it and/or modify       #
    # it under the terms of the GNU General Public License as published by       #
    # the Free Software Foundation, either version 3 of the License, or          #
    # (at your option) any later version.                                        #
    #                                                                            #
    # This program is distributed in the hope that it will be useful,            #
    # but WITHOUT ANY WARRANTY; without even the implied warranty of             #
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the               #
    # GNU General Public License for more details.                               #
    #                                                                            #
    # You should have received a copy of the GNU General Public License          #
    # along with this program. If not, see <https://www.gnu.org/licenses/>.      #
    ##############################################################################
    
    # Windows installer creation (with Qt Installer Framework)
    
    #include(CMakePrintHelpers)
    
    # QtIFW resources directory (build)
    set(QTIFW_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources")
    file(MAKE_DIRECTORY "${QTIFW_RESOURCES_DIR}")
    
    # Extra tools: SetAppUserModelId
    add_executable(setappusermodelid tools/setappusermodelid.cpp)
    set_target_properties(setappusermodelid PROPERTIES
        CXX_STANDARD 20
        CXX_STANDARD_REQUIRED YES
        CXX_EXTENSIONS NO
        RUNTIME_OUTPUT_NAME "SetAppUserModelId"
        RUNTIME_OUTPUT_DIRECTORY "${QTIFW_RESOURCES_DIR}")
    target_link_libraries(setappusermodelid
        PRIVATE
            ole32
            shlwapi
            propsys)
    # Once the target 'setappusermodelid' has been built, copy the .qrc
    set(SETAPPUSERMODELID_QRC "${QTIFW_RESOURCES_DIR}/setappusermodelid.qrc")
    add_custom_command(TARGET setappusermodelid POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
            "${CMAKE_CURRENT_SOURCE_DIR}/tools/setappusermodelid.qrc"
            "${SETAPPUSERMODELID_QRC}"
        COMMENT "Copying setappusermodelid.qrc to ${QTIFW_RESOURCES_DIR}"
        USES_TERMINAL)
    
    # The installer
    add_custom_target(installer
        COMMENT "Setting up the tree for the Qt Installer Framework")
    
    # External tools
    get_target_property(QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION)
    get_filename_component(_QT_BIN_DIR ${QMAKE_EXECUTABLE} DIRECTORY)
    
    # Look for 'windeployqt'
    find_program(WINDEPLOYQT_EXECUTABLE
        NAMES windeployqt
        HINTS "${_QT_BIN_DIR}"
        REQUIRED
        DOC "The Windows deployment tool")
    
    # Look for 'binarycreator'
    find_program(QTIFW_BINARYCREATOR_EXECUTABLE
        NAMES binarycreator
        HINTS "C:/Qt/Tools/QtInstallerFramework/4.10"
        PATH_SUFFIXES "bin"
        REQUIRED
        DOC "Qt Installer Framework binary creator")
    
    # Look for 'repogen'
    find_program(QTIFW_REPOGEN_EXECUTABLE
        NAMES repogen
        HINTS "C:/Qt/Tools/QtInstallerFramework/4.10"
        PATH_SUFFIXES "bin"
        REQUIRED
        DOC "Qt Installer Framework repository generator")
    
    # Look for 'installerbase'
    find_program(QTIFW_INSTALLERBASE_EXECUTABLE
        NAMES installerbase
        HINTS "C:/Qt/Tools/QtInstallerFramework/4.10"
        PATH_SUFFIXES "bin"
        REQUIRED
        DOC "Qt Installer Framework base binary")
    
    mark_as_advanced(WINDEPLOYQT_EXECUTABLE QTIFW_BINARYCREATOR_EXECUTABLE QTIFW_REPOGEN_EXECUTABLE QTIFW_INSTALLERBASE_EXECUTABLE)
    
    # Convenience path variables
    set(WIN_DEPLOY_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/packages/pro.buzzi.lbchronorace/data")
    set(WIN_DEPLOY_SAMPLES_DIR "${CMAKE_CURRENT_BINARY_DIR}/packages/pro.buzzi.lbchronorace.samples/data")
    set(WIN_DEPLOY_MAINTENANCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/packages/org.qtproject.ifw.maintenancetool/data")
    set(WIN_DEPLOY_DIRS "config" "packages")
    set(WIN_REPOSITORY_DIR "${CMAKE_BINARY_DIR}/repository")
    
    # Cleanup
    foreach(WIN_DEPLOY_DIR ${WIN_DEPLOY_DIRS})
        file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/${WIN_DEPLOY_DIR}")
    endforeach()
    file(REMOVE_RECURSE "${WIN_REPOSITORY_DIR}")
    
    # Create the basic tree
    file(COPY ${WIN_DEPLOY_DIRS} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
        PATTERN "*.in" EXCLUDE
        PATTERN "*.ts" EXCLUDE)
    
    # Copy the XMLs: replacing the version and date placeholders but preserve other ones
    set(at "@" CACHE STRING "QTIFW @ placeholder")
    file(GLOB_RECURSE WIN_DEPLOY_CONFIG_XML RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.xml.in")
    foreach(IN_FILE ${WIN_DEPLOY_CONFIG_XML})
        cmake_path(REMOVE_EXTENSION IN_FILE LAST_ONLY OUTPUT_VARIABLE OUT_FILE)
        configure_file(${IN_FILE} ${OUT_FILE} NEWLINE_STYLE WIN32)
    endforeach()
    unset(at CACHE)
    
    # Installer images and icons
    file(COPY
        "${CMAKE_SOURCE_DIR}/icons/installer.ico"
        "${CMAKE_SOURCE_DIR}/icons/LBChronoRace.ico"
        "${CMAKE_SOURCE_DIR}/icons/LBChronoRace-32.png"
        "${CMAKE_SOURCE_DIR}/icons/LBChronoRace-64.png"
        "${CMAKE_SOURCE_DIR}/icons/LBChronoRace-128.png"
        "${CMAKE_SOURCE_DIR}/icons/LBChronoRace-256.png"
        "${CMAKE_SOURCE_DIR}/images/lbchronorace-preview.png"
        "${CMAKE_SOURCE_DIR}/images/lbchronorace-sample.png"
        DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/config")
    
    # Installer translations
    set(WIN_DEPLOY_TS_FILES
        "${CMAKE_CURRENT_SOURCE_DIR}/packages/pro.buzzi.lbchronorace/meta/en.ts"
        "${CMAKE_CURRENT_SOURCE_DIR}/packages/pro.buzzi.lbchronorace/meta/it.ts")
    qt_add_translations(installer
        TS_FILES ${WIN_DEPLOY_TS_FILES}
        QM_FILES_OUTPUT_VARIABLE WIN_DEPLOY_QM_FILES)
    
    foreach(QM_FILE ${WIN_DEPLOY_QM_FILES})
        get_filename_component(QM_NAME "${QM_FILE}" NAME)
        set(QM_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/packages/pro.buzzi.lbchronorace/meta/${QM_NAME}")
    
        add_custom_command(
            OUTPUT ${QM_OUTPUT}
            COMMAND ${CMAKE_COMMAND} -E copy "${QM_FILE}" "${QM_OUTPUT}"
            DEPENDS ${QM_FILE}
            COMMENT "Copying translation file: ${QM_NAME}")
        list(APPEND INSTALLED_QM_FILES ${QM_OUTPUT})
    endforeach()
    
    add_custom_target(install_qms DEPENDS ${INSTALLED_QM_FILES})
    add_dependencies(installer install_qms)
    
    # Installer license files (moving to Windows newline style)
    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/packages/pro.buzzi.lbchronorace/meta/license.txt"
        INPUT "${CMAKE_SOURCE_DIR}/licenses/gpl-3.0.txt"
        NEWLINE_STYLE WIN32)
    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/packages/pro.buzzi.lbchronorace/meta/license_it.txt"
        INPUT "${CMAKE_SOURCE_DIR}/licenses/gpl-3.0_it.txt"
        NEWLINE_STYLE WIN32)
    
    # Application license file (moving to Windows newline style)
    file(GENERATE OUTPUT "${WIN_DEPLOY_TARGET_DIR}/GPLv3.txt"
        INPUT "${CMAKE_SOURCE_DIR}/licenses/gpl-3.0.txt"
        NEWLINE_STYLE WIN32)
    
    # Application icons
    file(GLOB WIN_DEPLOY_ICONS LIST_DIRECTORIES false "${CMAKE_SOURCE_DIR}/icons/*.ico")
    file(COPY ${WIN_DEPLOY_ICONS} DESTINATION ${WIN_DEPLOY_TARGET_DIR})
    
    # Data samples
    file(MAKE_DIRECTORY "${WIN_DEPLOY_SAMPLES_DIR}/samples")
    file(GLOB_RECURSE WIN_DEPLOY_SAMPLES_CONTENTS
        LIST_DIRECTORIES true RELATIVE "${CMAKE_SOURCE_DIR}/samples"
        "${CMAKE_SOURCE_DIR}/samples/*")
    foreach(SAMPLE_CONTENT ${WIN_DEPLOY_SAMPLES_CONTENTS})
        cmake_path(ABSOLUTE_PATH SAMPLE_CONTENT BASE_DIRECTORY "${CMAKE_SOURCE_DIR}/samples"
            OUTPUT_VARIABLE IN_PATH)
        cmake_path(ABSOLUTE_PATH SAMPLE_CONTENT BASE_DIRECTORY "${WIN_DEPLOY_SAMPLES_DIR}/samples"
            OUTPUT_VARIABLE OUT_PATH)
    
        if(IS_DIRECTORY ${IN_PATH})
            file(MAKE_DIRECTORY ${OUT_PATH})
        else()
            cmake_path(GET SAMPLE_CONTENT EXTENSION LAST_ONLY EXT)
            if("${EXT}" STREQUAL ".crd")
                file(COPY_FILE ${IN_PATH} ${OUT_PATH})
            else()
                file(GENERATE OUTPUT ${OUT_PATH} INPUT ${IN_PATH} NEWLINE_STYLE WIN32)
            endif()
        endif()
    endforeach()
    
    # Application executable (copy)
    add_custom_command(TARGET installer PRE_BUILD
        COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:${LBCHRONORACE_BIN}>" ${WIN_DEPLOY_TARGET_DIR}
        DEPENDS "$<TARGET_FILE:${LBCHRONORACE_BIN}>"
        COMMENT "Copy executable into the installer directory"
        USES_TERMINAL)
    
    # Populate the installer tree with the runtime dependencies
    string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
    if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG")
        set(WINDEPLOYQT_ARGS "--debug")
    else()
        set(WINDEPLOYQT_ARGS "--release")
    endif()
    file(MAKE_DIRECTORY "${WIN_DEPLOY_MAINTENANCE_DIR}")
    
    cmake_path(GET CMAKE_CXX_COMPILER PARENT_PATH _GXX_BIN_DIR)
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E env PATH="${_GXX_BIN_DIR}" ${WINDEPLOYQT_EXECUTABLE}
            --qmldir "${CMAKE_SOURCE_DIR}"
            ${WINDEPLOYQT_ARGS}
            --dir "${WIN_DEPLOY_TARGET_DIR}"
            --no-compiler-runtime
            "${WIN_DEPLOY_TARGET_DIR}/$<TARGET_FILE_NAME:${LBCHRONORACE_BIN}>"
        COMMENT "Deploying ${CMAKE_PROJECT_NAME}..."
        USES_TERMINAL)
    
    # Build the hybrid installer binary
    set(INSTALLER_BINARY "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Installer${CMAKE_EXECUTABLE_SUFFIX}")
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E env ${QTIFW_BINARYCREATOR_EXECUTABLE}
            "--hybrid"
            -c "config/config.xml"
            -p "packages"
            -r "${SETAPPUSERMODELID_QRC}"
            -v
            "${INSTALLER_BINARY}"
        COMMENT "Creating ${CMAKE_PROJECT_NAME} hybrid installer"
        USES_TERMINAL)
    
    # Build the on-line installer binary
    set(INSTALLER_ONLINE_BINARY "${CMAKE_BINARY_DIR}/installerbase${CMAKE_EXECUTABLE_SUFFIX}")
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E env ${QTIFW_BINARYCREATOR_EXECUTABLE}
            "--online-only"
            -t "${QTIFW_INSTALLERBASE_EXECUTABLE}"
            -c "config/config.xml"
            -p "packages"
            -r "${SETAPPUSERMODELID_QRC}"
            -v
            "${INSTALLER_ONLINE_BINARY}"
        COMMENT "Creating ${CMAKE_PROJECT_NAME} on-line installer"
        USES_TERMINAL)
    
    # Copy the on-line installer binary (for the on-line updater)
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E copy "${INSTALLER_ONLINE_BINARY}" "${WIN_DEPLOY_MAINTENANCE_DIR}"
        DEPENDS "${INSTALLER_ONLINE_BINARY}"
        COMMENT "Copy ${INSTALLER_ONLINE_BINARY} into the maintenance tool directory"
        USES_TERMINAL)
    
    # Build the installer's resource file (for on-line update)
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E env ${QTIFW_BINARYCREATOR_EXECUTABLE}
            -c "config/config.xml"
            -p "packages"
            -rcc
        COMMENT "Creating ${CMAKE_PROJECT_NAME} installer's resource file"
        USES_TERMINAL)
    
    # Copy the installer's resource file
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E copy "update.rcc" "${WIN_DEPLOY_MAINTENANCE_DIR}"
        DEPENDS "update.rcc"
        COMMENT "Copy update.rcc into the maintenance tool directory"
        USES_TERMINAL)
    
    # Build the repository
    add_custom_command(TARGET installer POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E env ${QTIFW_REPOGEN_EXECUTABLE}
            -p "packages"
            "${WIN_REPOSITORY_DIR}"
        COMMENT "Generating QtIFW repository into ${WIN_REPOSITORY_DIR}"
        USES_TERMINAL)
    
    # Build the launcher that set AllUsers
    configure_file(
        ${CMAKE_CURRENT_SOURCE_DIR}/launcher.rc.in
        ${CMAKE_CURRENT_BINARY_DIR}/launcher.rc
        @ONLY)
    
    # Embed the installer in the launcher executable
    set(INSTALLER_RC "${CMAKE_CURRENT_BINARY_DIR}/installer.rc")
    file(WRITE ${INSTALLER_RC} "#include \"launcher.hpp\"\n\nIDR_INSTALLER_BIN RCDATA \"${INSTALLER_BINARY}\"\n")
    
    # Create the launcher executable
    set(APP_ICON_RESOURCE_LAUNCHER "${CMAKE_SOURCE_DIR}/icons/LBChronoRace.rc")
    set(APP_VERSION_RESOURCE_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launcher.rc")
    if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG")
    set_source_files_properties(${APP_VERSION_RESOURCE_LAUNCHER} PROPERTIES COMPILE_DEFINITIONS QT_QML_DEBUG)
    endif()
    add_executable(launcher WIN32 EXCLUDE_FROM_ALL
        "${CMAKE_SOURCE_DIR}/installer/launcher.cpp"
        ${APP_ICON_RESOURCE_LAUNCHER}
        ${APP_VERSION_RESOURCE_LAUNCHER}
        ${INSTALLER_RC})
    set_target_properties(launcher PROPERTIES
        WIN32_EXECUTABLE TRUE
        RUNTIME_OUTPUT_NAME "${CMAKE_PROJECT_NAME}Installer-${PROJECT_VERSION}u${PROJECT_UPDATE}"
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
    target_include_directories(launcher PRIVATE ${CMAKE_BINARY_DIR})
    add_dependencies(launcher installer)
    
    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved