ECMAddAppIcon.cmake 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. #.rst:
  2. # ECMAddAppIcon
  3. # -------------
  4. #
  5. # Add icons to executable files and packages.
  6. #
  7. # ::
  8. #
  9. # ecm_add_app_icon(<sources_var>
  10. # ICONS <icon> [<icon> [...]]
  11. # [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49
  12. # [OUTFILE_BASENAME <name>]) # Since 5.49
  13. # )
  14. #
  15. # The given icons, whose names must match the pattern::
  16. #
  17. # <size>-<other_text>.png
  18. #
  19. # will be added to the executable target whose sources are specified by
  20. # ``<sources_var>`` on platforms that support it (Windows and Mac OS X).
  21. # Other icon files are ignored but on Mac SVG files can be supported and
  22. # it is thus possible to mix those with png files in a single macro call.
  23. #
  24. # ``<size>`` is a numeric pixel size (typically 16, 32, 48, 64, 128 or 256).
  25. # ``<other_text>`` can be any other text. See the platform notes below for any
  26. # recommendations about icon sizes.
  27. #
  28. # ``SIDEBAR_ICONS`` can be used to add Mac OS X sidebar
  29. # icons to the generated iconset. They are used when a folder monitored by the
  30. # application is dragged into Finder's sidebar. Since 5.49.
  31. #
  32. # ``OUTFILE_BASENAME`` will be used as the basename for the icon file. If
  33. # you specify it, the icon file will be called ``<OUTFILE_BASENAME>.icns`` on Mac OS X
  34. # and ``<OUTFILE_BASENAME>.ico`` on Windows. If you don't specify it, it defaults
  35. # to ``<sources_var>.<ext>``. Since 5.49.
  36. #
  37. #
  38. # Windows notes
  39. # * Icons are compiled into the executable using a resource file.
  40. # * Icons may not show up in Windows Explorer if the executable
  41. # target does not have the ``WIN32_EXECUTABLE`` property set.
  42. # * One of the tools png2ico (See :find-module:`FindPng2Ico`) or
  43. # icotool (see :find-module:`FindIcoTool`) is required.
  44. # * Supported sizes: 16, 24, 32, 48, 64, 128, 256, 512 and 1024.
  45. #
  46. # Mac OS X notes
  47. # * The executable target must have the ``MACOSX_BUNDLE`` property set.
  48. # * Icons are added to the bundle.
  49. # * If the ksvg2icns tool from KIconThemes is available, .svg and .svgz
  50. # files are accepted; the first that is converted successfully to .icns
  51. # will provide the application icon. SVG files are ignored otherwise.
  52. # * The tool iconutil (provided by Apple) is required for bitmap icons.
  53. # * Supported sizes: 16, 32, 64, 128, 256 (and 512, 1024 after OS X 10.9).
  54. # * At least a 128x128px (or an SVG) icon is required.
  55. # * Larger sizes are automatically used to substitute for smaller sizes on
  56. # "Retina" (high-resolution) displays. For example, a 32px icon, if
  57. # provided, will be used as a 32px icon on standard-resolution displays,
  58. # and as a 16px-equivalent icon (with an "@2x" tag) on high-resolution
  59. # displays. That is why you should provide 64px and 1024px icons although
  60. # they are not supported anymore directly. Instead they will be used as
  61. # 32px@2x and 512px@2x. ksvg2icns handles this internally.
  62. # * This function sets the ``MACOSX_BUNDLE_ICON_FILE`` variable to the name
  63. # of the generated icns file, so that it will be used as the
  64. # ``MACOSX_BUNDLE_ICON_FILE`` target property when you call
  65. # ``add_executable``.
  66. # * Sidebar icons should typically provided in 16, 32, 64, 128 and 256px.
  67. #
  68. # Since 1.7.0.
  69. #=============================================================================
  70. # Copyright 2014 Alex Merry <alex.merry@kde.org>
  71. # Copyright 2014 Ralf Habacker <ralf.habacker@freenet.de>
  72. # Copyright 2006-2009 Alexander Neundorf, <neundorf@kde.org>
  73. # Copyright 2006, 2007, Laurent Montel, <montel@kde.org>
  74. # Copyright 2007 Matthias Kretz <kretz@kde.org>
  75. #
  76. # Redistribution and use in source and binary forms, with or without
  77. # modification, are permitted provided that the following conditions
  78. # are met:
  79. #
  80. # 1. Redistributions of source code must retain the copyright
  81. # notice, this list of conditions and the following disclaimer.
  82. # 2. Redistributions in binary form must reproduce the copyright
  83. # notice, this list of conditions and the following disclaimer in the
  84. # documentation and/or other materials provided with the distribution.
  85. # 3. The name of the author may not be used to endorse or promote products
  86. # derived from this software without specific prior written permission.
  87. #
  88. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  89. # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  90. # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  91. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  92. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  93. # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  94. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  95. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  96. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  97. # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  98. include(CMakeParseArguments)
  99. function(ecm_add_app_icon appsources)
  100. set(options)
  101. set(oneValueArgs OUTFILE_BASENAME)
  102. set(multiValueArgs ICONS SIDEBAR_ICONS)
  103. cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  104. if(NOT ARG_ICONS)
  105. message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
  106. endif()
  107. if(ARG_UNPARSED_ARGUMENTS)
  108. message(FATAL_ERROR "Unexpected arguments to ecm_add_app_icon: ${ARG_UNPARSED_ARGUMENTS}")
  109. endif()
  110. if(APPLE)
  111. find_program(KSVG2ICNS NAMES ksvg2icns)
  112. foreach(icon ${ARG_ICONS})
  113. get_filename_component(icon_full ${icon} ABSOLUTE)
  114. get_filename_component(icon_type ${icon_full} EXT)
  115. # do we have ksvg2icns in the path and did we receive an svg (or compressed svg) icon?
  116. if(KSVG2ICNS AND (${icon_type} STREQUAL ".svg" OR ${icon_type} STREQUAL ".svgz"))
  117. # convert the svg icon to an icon resource
  118. execute_process(COMMAND ${KSVG2ICNS} "${icon_full}"
  119. WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE KSVG2ICNS_ERROR)
  120. if(${KSVG2ICNS_ERROR})
  121. message(AUTHOR_WARNING "ksvg2icns could not generate an OS X application icon from ${icon}")
  122. else()
  123. # install the icns file we just created
  124. get_filename_component(icon_name ${icon_full} NAME_WE)
  125. set(MACOSX_BUNDLE_ICON_FILE ${icon_name}.icns PARENT_SCOPE)
  126. set(${appsources} "${${appsources}};${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns" PARENT_SCOPE)
  127. set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
  128. # we're done now
  129. return()
  130. endif()
  131. endif()
  132. endforeach()
  133. endif()
  134. _ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024")
  135. if(ARG_SIDEBAR_ICONS)
  136. _ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;32;64;128;256")
  137. endif()
  138. set(mac_icons
  139. # Icons: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
  140. ${icons_at_16px}
  141. ${icons_at_32px}
  142. ${icons_at_64px}
  143. ${icons_at_128px}
  144. ${icons_at_256px}
  145. ${icons_at_512px}
  146. ${icons_at_1024px})
  147. set(mac_sidebar_icons
  148. # Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
  149. ${sidebar_icons_at_16px}
  150. ${sidebar_icons_at_32px}
  151. ${sidebar_icons_at_64px}
  152. ${sidebar_icons_at_128px}
  153. ${sidebar_icons_at_256px})
  154. if (NOT (mac_icons OR mac_sidebar_icons))
  155. message(AUTHOR_WARNING "No icons suitable for use on macOS provided")
  156. endif()
  157. set(windows_icons ${icons_at_16px}
  158. ${icons_at_24px}
  159. ${icons_at_32px}
  160. ${icons_at_48px}
  161. ${icons_at_64px}
  162. ${icons_at_128px}
  163. ${icons_at_256px}
  164. ${icons_at_512px}
  165. ${icons_at_1024px})
  166. if (NOT (windows_icons))
  167. message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
  168. endif()
  169. if (ARG_OUTFILE_BASENAME)
  170. set (_outfilebasename "${ARG_OUTFILE_BASENAME}")
  171. else()
  172. set (_outfilebasename "${appsources}")
  173. endif()
  174. set (_outfilename "${CMAKE_CURRENT_BINARY_DIR}/${_outfilebasename}")
  175. if (WIN32 AND windows_icons)
  176. set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
  177. set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
  178. find_package(Png2Ico)
  179. find_package(IcoTool)
  180. set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
  181. function(create_windows_icon_and_rc command args deps)
  182. add_custom_command(
  183. OUTPUT "${_outfilename}.ico"
  184. COMMAND ${command}
  185. ARGS ${args}
  186. DEPENDS ${deps}
  187. WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
  188. )
  189. # this bit's a little hacky to make the dependency stuff work
  190. file(WRITE "${_outfilename}.rc.in" "IDI_ICON1 ICON DISCARDABLE \"${_outfilename}.ico\"\n")
  191. add_custom_command(
  192. OUTPUT "${_outfilename}.rc"
  193. COMMAND ${CMAKE_COMMAND}
  194. ARGS -E copy "${_outfilename}.rc.in" "${_outfilename}.rc"
  195. DEPENDS "${_outfilename}.ico"
  196. WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
  197. )
  198. endfunction()
  199. if (IcoTool_FOUND)
  200. list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
  201. # According to https://stackoverflow.com/a/40851713/2886832
  202. # Windows always chooses the first icon above 255px, all other ones will be ignored
  203. set(maxSize 0)
  204. foreach(size 256 512 1024)
  205. if(icons_at_${size}px)
  206. set(maxSize "${size}")
  207. endif()
  208. endforeach()
  209. foreach(size 16 24 32 48 64 128 ${maxSize})
  210. if(NOT icons_at_${size}px)
  211. continue()
  212. endif()
  213. set(icotool_icon_arg "")
  214. if(size STREQUAL "${maxSize}")
  215. # maxSize icon needs to be included as raw png
  216. list(APPEND icotool_args "-r")
  217. endif()
  218. foreach(icon ${icons_at_${size}px})
  219. list(APPEND icotool_args "${icons_at_${size}px}")
  220. endforeach()
  221. endforeach()
  222. create_windows_icon_and_rc(IcoTool::IcoTool "${icotool_args}" "${windows_icons_modern}")
  223. set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
  224. # standard png2ico has no rcfile argument
  225. # NOTE: We generally use https://github.com/hiiamok/png2ImageMagickICO
  226. # or similar on windows, which is why we provide resolutions >= 256px here.
  227. # Standard png2ico will fail with this.
  228. elseif(Png2Ico_FOUND AND NOT Png2Ico_HAS_RCFILE_ARGUMENT AND windows_icons)
  229. set(png2ico_args)
  230. list(APPEND png2ico_args "${_outfilename}.ico")
  231. list(APPEND png2ico_args "${windows_icons}")
  232. create_windows_icon_and_rc(Png2Ico::Png2Ico "${png2ico_args}" "${windows_icons}")
  233. set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
  234. # png2ico from kdewin provides rcfile argument
  235. elseif(Png2Ico_FOUND AND windows_icons)
  236. add_custom_command(
  237. OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
  238. COMMAND Png2Ico::Png2Ico
  239. ARGS
  240. --rcfile "${_outfilename}.rc"
  241. "${_outfilename}.ico"
  242. ${windows_icons}
  243. DEPENDS ${windows_icons}
  244. WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
  245. )
  246. set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
  247. # else none of the supported tools was found
  248. else()
  249. message(WARNING "Unable to find the png2ico or icotool utilities or icons in matching sizes - application will not have an application icon!")
  250. endif()
  251. elseif (APPLE AND (mac_icons OR mac_sidebar_icons))
  252. # first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility,
  253. # to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
  254. # 256x256, 512x512, 1024x1024
  255. find_program(ICONUTIL_EXECUTABLE NAMES iconutil)
  256. if (ICONUTIL_EXECUTABLE)
  257. add_custom_command(
  258. OUTPUT "${_outfilename}.iconset"
  259. COMMAND ${CMAKE_COMMAND}
  260. ARGS -E make_directory "${_outfilename}.iconset"
  261. )
  262. set(iconset_icons)
  263. macro(copy_icon filename sizename type)
  264. add_custom_command(
  265. OUTPUT "${_outfilename}.iconset/${type}_${sizename}.png"
  266. COMMAND ${CMAKE_COMMAND}
  267. ARGS -E copy
  268. "${filename}"
  269. "${_outfilename}.iconset/${type}_${sizename}.png"
  270. DEPENDS
  271. "${_outfilename}.iconset"
  272. "${filename}"
  273. WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
  274. )
  275. list(APPEND iconset_icons
  276. "${_outfilename}.iconset/${type}_${sizename}.png")
  277. endmacro()
  278. # List of supported sizes and filenames taken from:
  279. # https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
  280. foreach(size 16 32 128 256 512)
  281. math(EXPR double_size "2 * ${size}")
  282. foreach(file ${icons_at_${size}px})
  283. copy_icon("${file}" "${size}x${size}" "icon")
  284. endforeach()
  285. foreach(file ${icons_at_${double_size}px})
  286. copy_icon("${file}" "${size}x${size}@2x" "icon")
  287. endforeach()
  288. endforeach()
  289. # List of supported sizes and filenames taken from:
  290. # https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
  291. foreach(file ${sidebar_icons_at_16px})
  292. copy_icon("${file}" "16x16" "sidebar")
  293. endforeach()
  294. foreach(file ${sidebar_icons_at_32px})
  295. copy_icon("${file}" "16x16@2x" "sidebar")
  296. endforeach()
  297. foreach(file ${sidebar_icons_at_32px})
  298. copy_icon("${file}" "18x18" "sidebar")
  299. endforeach()
  300. foreach(file ${sidebar_icons_at_64px})
  301. copy_icon("${file}" "18x18@2x" "sidebar")
  302. endforeach()
  303. foreach(file ${sidebar_icons_at_128px})
  304. copy_icon("${file}" "32x32" "sidebar")
  305. endforeach()
  306. foreach(file ${sidebar_icons_at_256px})
  307. copy_icon("${file}" "32x32@2x" "sidebar")
  308. endforeach()
  309. # generate .icns icon file
  310. add_custom_command(
  311. OUTPUT "${_outfilename}.icns"
  312. COMMAND ${ICONUTIL_EXECUTABLE}
  313. ARGS
  314. --convert icns
  315. --output "${_outfilename}.icns"
  316. "${_outfilename}.iconset"
  317. DEPENDS "${iconset_icons}"
  318. WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
  319. )
  320. # This will register the icon into the bundle
  321. set(MACOSX_BUNDLE_ICON_FILE "${_outfilebasename}.icns" PARENT_SCOPE)
  322. # Append the icns file to the sources list so it will be a dependency to the
  323. # main target
  324. set(${appsources} "${${appsources}};${_outfilename}.icns" PARENT_SCOPE)
  325. # Install the icon into the Resources dir in the bundle
  326. set_source_files_properties("${_outfilename}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
  327. else()
  328. message(STATUS "Unable to find the iconutil utility - application will not have an application icon!")
  329. endif()
  330. endif()
  331. endfunction()
  332. macro(_ecm_add_app_icon_categorize_icons icons type known_sizes)
  333. set(_${type}_known_sizes)
  334. foreach(size ${known_sizes})
  335. set(${type}_at_${size}px)
  336. list(APPEND _${type}_known_sizes ${size})
  337. endforeach()
  338. foreach(icon ${icons})
  339. get_filename_component(icon_full ${icon} ABSOLUTE)
  340. if (NOT EXISTS "${icon_full}")
  341. message(AUTHOR_WARNING "${icon_full} does not exist, ignoring")
  342. else()
  343. get_filename_component(icon_name ${icon} NAME)
  344. string(REGEX MATCH "([0-9]+)\\-[^/]+\\.([a-z]+)$"
  345. _dummy "${icon_name}")
  346. set(size "${CMAKE_MATCH_1}")
  347. set(ext "${CMAKE_MATCH_2}")
  348. if (NOT (ext STREQUAL "svg" OR ext STREQUAL "svgz"))
  349. if (NOT size)
  350. message(AUTHOR_WARNING "${icon_full} is not named correctly for ecm_add_app_icon - ignoring")
  351. elseif (NOT ext STREQUAL "png")
  352. message(AUTHOR_WARNING "${icon_full} is not a png file - ignoring")
  353. else()
  354. list(FIND _${type}_known_sizes ${size} offset)
  355. if (offset GREATER -1)
  356. list(APPEND ${type}_at_${size}px "${icon_full}")
  357. elseif()
  358. message(STATUS "not found ${type}_at_${size}px ${icon_full}")
  359. endif()
  360. endif()
  361. endif()
  362. endif()
  363. endforeach()
  364. endmacro()