From 7b3ffb408ffde8da0ebd4c7756a2ee9c55f86ef0 Mon Sep 17 00:00:00 2001 From: SineStriker Date: Sat, 24 Oct 2026 17:37:57 +0800 Subject: [PATCH 1/2] Write the icons with qiconmaker at build time --- cmake/AddIcon.cmake | 50 ++++++++++++ scripts/vcpkg-manifest/vcpkg.json | 6 +- src/app/CMakeLists.txt | 63 ++++++++------- src/app/icons/CMakeLists.txt | 129 ------------------------------ 4 files changed, 89 insertions(+), 159 deletions(-) create mode 100644 cmake/AddIcon.cmake delete mode 100644 src/app/icons/CMakeLists.txt diff --git a/cmake/AddIcon.cmake b/cmake/AddIcon.cmake new file mode 100644 index 00000000..cef723ba --- /dev/null +++ b/cmake/AddIcon.cmake @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: Team OpenVPI +# SPDX-License-Identifier: Apache-2.0 + +# Icons written at build time by qiconmaker (https://github.com/stdware/qiconmaker) from images, +# so that the repository keeps the images rather than the icon files. The package qiconmaker is +# found before this file is included. + +# Qt Core for the directory of its DLLs, which qiconmaker loads on Windows +qm_find_qt(Core) + +# add_icon( ) +# +# Writes the icon , an .ico of Windows or an .icns of macOS by its suffix, from the images +# /x.png in a custom command. The image of 1024 px is the master image. A +# size of the format that has an image of its own takes that image, and qiconmaker reduces the +# master image to every other size. A target of the same directory that lists as a source +# builds the icon first, and qm_add_win_rc_enhanced() and qm_add_mac_bundle() of qmsetup accept it. +function(add_icon _output _directory) + set(_master "${_directory}/1024x1024.png") + set(_arguments "${_output}" "${_master}") + set(_depends "${_master}") + + # The sizes that qiconmaker writes for each format by default + if(_output MATCHES "\\.icns$") + set(_sizes 16 32 64 128 256 512) + else() + set(_sizes 16 20 24 30 32 36 40 48 60 64 72 80 96 256) + endif() + + foreach(_size IN LISTS _sizes) + set(_file "${_directory}/${_size}x${_size}.png") + if(EXISTS "${_file}") + list(APPEND _arguments "${_size}=${_file}") + list(APPEND _depends "${_file}") + endif() + endforeach() + + # The program loads the DLLs of Qt, which Windows finds through PATH. On the other systems the + # program holds the run path to the libraries of Qt. + set(_command $) + if(WIN32) + set(_command ${CMAKE_COMMAND} -E env "PATH=$" ${_command}) + endif() + add_custom_command(OUTPUT "${_output}" + COMMAND ${_command} ${_arguments} + DEPENDS ${_depends} + COMMENT "Writing ${_output}" + VERBATIM + ) +endfunction() diff --git a/scripts/vcpkg-manifest/vcpkg.json b/scripts/vcpkg-manifest/vcpkg.json index c2e4d151..cb8abe05 100644 --- a/scripts/vcpkg-manifest/vcpkg.json +++ b/scripts/vcpkg-manifest/vcpkg.json @@ -43,7 +43,11 @@ }, "xxhash", "signalsmith-dsp", - "rubberband" + "rubberband", + { + "name": "qiconmaker", + "platform": "windows | osx" + } ], "vcpkg-configuration": { "overlay-ports": [ diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 0d51d33c..6becc0e8 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -1,33 +1,35 @@ # SPDX-FileCopyrightText: Team OpenVPI # SPDX-License-Identifier: Apache-2.0 -add_subdirectory(icons) - +# The icons of the application and of the project files, written at build time from the images in +# icons/. The installer of Windows takes the icon of the project files from the resources of +# the executable, and the bundle of macOS from its resources. Linux uses the images directly. set(_ico_args) set(_icns_args) +set(_icons) -if(WIN32) - list(APPEND _ico_path - ${CMAKE_CURRENT_BINARY_DIR}/icons/app.ico - ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.ico - ) - set(_filtered_ico_path "") - foreach(_file IN LISTS _ico_path) - if(EXISTS ${_file}) - list(APPEND _filtered_ico_path ${_file}) - endif() - endforeach() - set(_ico_args ICO ${_filtered_ico_path}) -endif() +if(WIN32 OR APPLE) + find_package(qiconmaker CONFIG REQUIRED) + include(AddIcon) -if(APPLE) - set(_icns_path ${CMAKE_CURRENT_BINARY_DIR}/icons/app.icns) - if(EXISTS ${_icns_path}) - set(_icns_args ICNS ${_icns_path}) + if(WIN32) + set(_suffix .ico) + else() + set(_suffix .icns) endif() -endif() + foreach(_name IN ITEMS app dspx) + set(_icon ${CMAKE_CURRENT_BINARY_DIR}/icons/${_name}${_suffix}) + add_icon(${_icon} ${CMAKE_CURRENT_SOURCE_DIR}/icons/${_name}) + list(APPEND _icons ${_icon}) + endforeach() + if(WIN32) + set(_ico_args ICO ${_icons}) + else() + set(_icns_args ICNS ${CMAKE_CURRENT_BINARY_DIR}/icons/app.icns) + endif() +endif() ck_configure_application( ${_ico_args} @@ -35,6 +37,16 @@ ck_configure_application( INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/info.plist.in ) +if(_icons) + target_sources(${PROJECT_NAME} PRIVATE ${_icons}) +endif() + +if(APPLE) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns PROPERTIES + MACOSX_PACKAGE_LOCATION Resources + ) +endif() + qm_configure_target(${PROJECT_NAME} SOURCES main.cpp QT_LINKS_PRIVATE @@ -75,19 +87,12 @@ ck_add_shared_files( # SRC conf/${CK_PLATFORM_LOWER}/qtmediate.json DEST ${CK_BUILD_QT_CONF_DIR} # qtmediate.json ) -if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns) - ck_add_shared_files( - SRC ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns DEST ${CK_BUILD_DATA_DIR} - ) -endif() - -file(GLOB _icons icons/*) -list(FILTER _icons EXCLUDE REGEX CMakeLists.txt) +file(GLOB _icon_images icons/*) qt_add_resources(${PROJECT_NAME} icons PREFIX /diffscope/icons BASE icons - FILES ${_icons} + FILES ${_icon_images} ) if(LINUX) diff --git a/src/app/icons/CMakeLists.txt b/src/app/icons/CMakeLists.txt deleted file mode 100644 index 7c18e871..00000000 --- a/src/app/icons/CMakeLists.txt +++ /dev/null @@ -1,129 +0,0 @@ -# SPDX-FileCopyrightText: Team OpenVPI -# SPDX-License-Identifier: Apache-2.0 - -# Icon generation for DiffScope -# Automatically generates platform-specific icon files from PNG sources at configure time - -# Get all subdirectories in the current source directory -file(GLOB ICON_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) -list(FILTER ICON_SUBDIRS INCLUDE REGEX "^[^.].*") # Exclude hidden directories - -foreach(SUBDIR ${ICON_SUBDIRS}) - if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}) - set(ICON_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}) - - # Check if required PNG files exist - set(REQUIRED_SIZES 16x16 24x24 32x32 48x48 64x64 96x96 128x128 256x256) - set(ALL_PNGS_EXIST TRUE) - - foreach(SIZE ${REQUIRED_SIZES}) - if(NOT EXISTS ${ICON_SOURCE_DIR}/${SIZE}.png) - set(ALL_PNGS_EXIST FALSE) - break() - endif() - endforeach() - - if(ALL_PNGS_EXIST) - if(WIN32) - # Windows: Generate ICO file using ImageMagick at configure time - find_program(MAGICK_EXECUTABLE NAMES magick) - - if(MAGICK_EXECUTABLE) - set(OUTPUT_ICO ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIR}.ico) - - # Build the list of input PNG files - set(INPUT_PNGS) - foreach(SIZE ${REQUIRED_SIZES}) - list(APPEND INPUT_PNGS ${ICON_SOURCE_DIR}/${SIZE}.png) - endforeach() - - # Generate ICO file immediately during configure - message(STATUS "Generating ${SUBDIR}.ico") - execute_process( - COMMAND ${MAGICK_EXECUTABLE} convert ${INPUT_PNGS} ${OUTPUT_ICO} - RESULT_VARIABLE RESULT - ERROR_VARIABLE ERROR_OUTPUT - ) - - if(RESULT EQUAL 0) - message(STATUS "Successfully generated ${OUTPUT_ICO}") - else() - message(WARNING "Failed to generate ${SUBDIR}.ico: ${ERROR_OUTPUT}") - endif() - else() - message(STATUS "ImageMagick not found, skipping ${SUBDIR}.ico generation") - endif() - - elseif(APPLE) - # macOS: Generate ICNS file using iconutil at configure time - find_program(ICONUTIL_EXECUTABLE iconutil) - - if(ICONUTIL_EXECUTABLE) - set(OUTPUT_ICNS ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIR}.icns) - set(ICONSET_DIR ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIR}.iconset) - - message(STATUS "Generating ${SUBDIR}.icns") - - # Create iconset directory - file(MAKE_DIRECTORY ${ICONSET_DIR}) - - # Copy required files with iconset naming convention - file(COPY ${ICON_SOURCE_DIR}/16x16.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/16x16.png ${ICONSET_DIR}/icon_16x16.png) - - file(COPY ${ICON_SOURCE_DIR}/32x32.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/32x32.png ${ICONSET_DIR}/icon_16x16@2x.png) - - file(COPY ${ICON_SOURCE_DIR}/32x32.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/32x32.png ${ICONSET_DIR}/icon_32x32.png) - - file(COPY ${ICON_SOURCE_DIR}/64x64.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/64x64.png ${ICONSET_DIR}/icon_32x32@2x.png) - - file(COPY ${ICON_SOURCE_DIR}/128x128.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/128x128.png ${ICONSET_DIR}/icon_128x128.png) - - file(COPY ${ICON_SOURCE_DIR}/256x256.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/256x256.png ${ICONSET_DIR}/icon_128x128@2x.png) - - file(COPY ${ICON_SOURCE_DIR}/256x256.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/256x256.png ${ICONSET_DIR}/icon_256x256.png) - - # Copy optional files if they exist - if(EXISTS ${ICON_SOURCE_DIR}/512x512.png) - file(COPY ${ICON_SOURCE_DIR}/512x512.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/512x512.png ${ICONSET_DIR}/icon_256x256@2x.png) - - file(COPY ${ICON_SOURCE_DIR}/512x512.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/512x512.png ${ICONSET_DIR}/icon_512x512.png) - endif() - - if(EXISTS ${ICON_SOURCE_DIR}/1024x1024.png) - file(COPY ${ICON_SOURCE_DIR}/1024x1024.png DESTINATION ${ICONSET_DIR}) - file(RENAME ${ICONSET_DIR}/1024x1024.png ${ICONSET_DIR}/icon_512x512@2x.png) - endif() - - # Generate ICNS file - execute_process( - COMMAND ${ICONUTIL_EXECUTABLE} -c icns -o ${OUTPUT_ICNS} ${ICONSET_DIR} - RESULT_VARIABLE RESULT - ERROR_VARIABLE ERROR_OUTPUT - ) - - # Clean up iconset directory - file(REMOVE_RECURSE ${ICONSET_DIR}) - - if(RESULT EQUAL 0) - message(STATUS "Successfully generated ${OUTPUT_ICNS}") - else() - message(WARNING "Failed to generate ${SUBDIR}.icns: ${ERROR_OUTPUT}") - endif() - else() - message(STATUS "iconutil not found, skipping ${SUBDIR}.icns generation") - endif() - endif() - else() - message(STATUS "Required PNG files not found for ${SUBDIR}, skipping icon generation") - endif() - endif() -endforeach() From 09845e1666acb28ff6185edfbabc2f1fae4af693 Mon Sep 17 00:00:00 2001 From: SineStriker Date: Sun, 25 Oct 2026 20:18:56 +0800 Subject: [PATCH 2/2] Include AddIcon.cmake by its path, which the module path of main does not cover --- src/app/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 6becc0e8..601d47cc 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -10,7 +10,7 @@ set(_icons) if(WIN32 OR APPLE) find_package(qiconmaker CONFIG REQUIRED) - include(AddIcon) + include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/AddIcon.cmake) if(WIN32) set(_suffix .ico)