ECMAddAppIcon.cmake 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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, 20, 24, 32, 40, 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 ICON_INDEX DO_NOT_GENERATE_RC_FILE)
  102. set(multiValueArgs ICONS SIDEBAR_ICONS RC_DEPENDENCIES)
  103. cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  104. if (ARG_DO_NOT_GENERATE_RC_FILE)
  105. set (_do_not_generate_rc_file TRUE)
  106. else()
  107. set (_do_not_generate_rc_file FALSE)
  108. endif()
  109. if(NOT ARG_ICONS)
  110. message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
  111. endif()
  112. if(ARG_UNPARSED_ARGUMENTS)
  113. message(FATAL_ERROR "Unexpected arguments to ecm_add_app_icon: ${ARG_UNPARSED_ARGUMENTS}")
  114. endif()
  115. if(APPLE)
  116. find_program(KSVG2ICNS NAMES ksvg2icns)
  117. foreach(icon ${ARG_ICONS})
  118. get_filename_component(icon_full ${icon} ABSOLUTE)
  119. get_filename_component(icon_type ${icon_full} EXT)
  120. # do we have ksvg2icns in the path and did we receive an svg (or compressed svg) icon?
  121. if(KSVG2ICNS AND (${icon_type} STREQUAL ".svg" OR ${icon_type} STREQUAL ".svgz"))
  122. # convert the svg icon to an icon resource
  123. execute_process(COMMAND ${KSVG2ICNS} "${icon_full}"
  124. WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE KSVG2ICNS_ERROR)
  125. if(${KSVG2ICNS_ERROR})
  126. message(AUTHOR_WARNING "ksvg2icns could not generate an OS X application icon from ${icon}")
  127. else()
  128. # install the icns file we just created
  129. get_filename_component(icon_name ${icon_full} NAME_WE)
  130. set(MACOSX_BUNDLE_ICON_FILE ${icon_name}.icns PARENT_SCOPE)
  131. set(${appsources} "${${appsources}};${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns" PARENT_SCOPE)
  132. set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
  133. # we're done now
  134. return()
  135. endif()
  136. endif()
  137. endforeach()
  138. endif()
  139. if (WIN32)
  140. _ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;20;24;32;40;48;64;128;256;512;1024")
  141. else()
  142. _ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024")
  143. endif()
  144. if(ARG_SIDEBAR_ICONS)
  145. _ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;32;64;128;256")
  146. endif()
  147. set(mac_icons
  148. # Icons: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
  149. ${icons_at_16px}
  150. ${icons_at_32px}
  151. ${icons_at_64px}
  152. ${icons_at_128px}
  153. ${icons_at_256px}
  154. ${icons_at_512px}
  155. ${icons_at_1024px})
  156. set(mac_sidebar_icons
  157. # Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
  158. ${sidebar_icons_at_16px}
  159. ${sidebar_icons_at_32px}
  160. ${sidebar_icons_at_64px}
  161. ${sidebar_icons_at_128px}
  162. ${sidebar_icons_at_256px})
  163. if (NOT (mac_icons OR mac_sidebar_icons))
  164. message(AUTHOR_WARNING "No icons suitable for use on macOS provided")
  165. endif()
  166. set(windows_icons ${icons_at_16px}
  167. ${icons_at_20px}
  168. ${icons_at_24px}
  169. ${icons_at_32px}
  170. ${icons_at_40px}
  171. ${icons_at_48px}
  172. ${icons_at_64px}
  173. ${icons_at_128px}
  174. ${icons_at_256px}
  175. ${icons_at_512px}
  176. ${icons_at_1024px})
  177. if (NOT (windows_icons))
  178. message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
  179. endif()
  180. if (ARG_OUTFILE_BASENAME)
  181. set (_outfilebasename "${ARG_OUTFILE_BASENAME}")
  182. else()
  183. set (_outfilebasename "${appsources}")
  184. endif()
  185. set (_outfilename "${CMAKE_CURRENT_BINARY_DIR}/${_outfilebasename}")
  186. if (WIN32 AND windows_icons)
  187. set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
  188. set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
  189. find_package(Png2Ico)
  190. find_package(IcoTool)
  191. set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
  192. function(create_windows_icon_and_rc command args deps)
  193. add_custom_command(
  194. OUTPUT "${_outfilename}.ico"
  195. COMMAND ${command}
  196. ARGS ${args}
  197. DEPENDS ${deps}
  198. WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
  199. )
  200. if (NOT _do_not_generate_rc_file)
  201. # this bit's a little hacky to make the dependency stuff work
  202. file(WRITE "${_outfilename}.rc.in" "IDI_ICON${ARG_ICON_INDEX} ICON DISCARDABLE \"${_outfilename}.ico\"\n")
  203. add_custom_command(
  204. OUTPUT "${_outfilename}.rc"
  205. COMMAND ${CMAKE_COMMAND}
  206. ARGS -E copy "${_outfilename}.rc.in" "${_outfilename}.rc"
  207. DEPENDS ${ARG_RC_DEPENDENCIES} "${_outfilename}.ico"
  208. WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
  209. )
  210. endif()
  211. endfunction()
  212. if (IcoTool_FOUND)
  213. list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
  214. # According to https://stackoverflow.com/a/40851713/2886832
  215. # Windows always chooses the first icon above 255px, all other ones will be ignored
  216. set(maxSize 0)
  217. foreach(size 256 512 1024)
  218. if(icons_at_${size}px)
  219. set(maxSize "${size}")
  220. endif()
  221. endforeach()
  222. foreach(size 16 20 24 32 40 48 64 128 ${maxSize})
  223. if(NOT icons_at_${size}px)
  224. continue()
  225. endif()
  226. set(icotool_icon_arg "")
  227. if(size STREQUAL "${maxSize}")
  228. # maxSize icon needs to be included as raw png
  229. list(APPEND icotool_args "-r")
  230. endif()
  231. foreach(icon ${icons_at_${size}px})
  232. list(APPEND icotool_args "${icons_at_${size}px}")
  233. endforeach()
  234. endforeach()
  235. create_windows_icon_and_rc(IcoTool::IcoTool "${icotool_args}" "${windows_icons_modern}")
  236. set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
  237. # standard png2ico has no rcfile argument
  238. # NOTE: We generally use https://github.com/hiiamok/png2ImageMagickICO
  239. # or similar on windows, which is why we provide resolutions >= 256px here.
  240. # Standard png2ico will fail with this.
  241. elseif(Png2Ico_FOUND AND NOT Png2Ico_HAS_RCFILE_ARGUMENT AND windows_icons)
  242. set(png2ico_args)
  243. list(APPEND png2ico_args "${_outfilename}.ico")
  244. list(APPEND png2ico_args "${windows_icons}")
  245. create_windows_icon_and_rc(Png2Ico::Png2Ico "${png2ico_args}" "${windows_icons}")
  246. set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
  247. # png2ico from kdewin provides rcfile argument
  248. elseif(Png2Ico_FOUND AND windows_icons)
  249. add_custom_command(
  250. OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
  251. COMMAND Png2Ico::Png2Ico
  252. ARGS
  253. --rcfile "${_outfilename}.rc"
  254. "${_outfilename}.ico"
  255. ${windows_icons}
  256. DEPENDS ${windows_icons}
  257. WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
  258. )
  259. set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
  260. # else none of the supported tools was found
  261. else()
  262. message(WARNING "Unable to find the png2ico or icotool utilities or icons in matching sizes - application will not have an application icon!")
  263. endif()
  264. elseif (APPLE AND (mac_icons OR mac_sidebar_icons))
  265. # first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility,
  266. # to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
  267. # 256x256, 512x512, 1024x1024
  268. find_program(ICONUTIL_EXECUTABLE NAMES iconutil)
  269. if (ICONUTIL_EXECUTABLE)
  270. add_custom_command(
  271. OUTPUT "${_outfilename}.iconset"
  272. COMMAND ${CMAKE_COMMAND}
  273. ARGS -E make_directory "${_outfilename}.iconset"
  274. )
  275. set(iconset_icons)
  276. macro(copy_icon filename sizename type)
  277. add_custom_command(
  278. OUTPUT "${_outfilename}.iconset/${type}_${sizename}.png"
  279. COMMAND ${CMAKE_COMMAND}
  280. ARGS -E copy
  281. "${filename}"
  282. "${_outfilename}.iconset/${type}_${sizename}.png"
  283. DEPENDS
  284. "${_outfilename}.iconset"
  285. "${filename}"
  286. WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
  287. )
  288. list(APPEND iconset_icons
  289. "${_outfilename}.iconset/${type}_${sizename}.png")
  290. endmacro()
  291. # List of supported sizes and filenames taken from:
  292. # https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
  293. foreach(size 16 32 128 256 512)
  294. math(EXPR double_size "2 * ${size}")
  295. foreach(file ${icons_at_${size}px})
  296. copy_icon("${file}" "${size}x${size}" "icon")
  297. endforeach()
  298. foreach(file ${icons_at_${double_size}px})
  299. copy_icon("${file}" "${size}x${size}@2x" "icon")
  300. endforeach()
  301. endforeach()
  302. # List of supported sizes and filenames taken from:
  303. # https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
  304. foreach(file ${sidebar_icons_at_16px})
  305. copy_icon("${file}" "16x16" "sidebar")
  306. endforeach()
  307. foreach(file ${sidebar_icons_at_32px})
  308. copy_icon("${file}" "16x16@2x" "sidebar")
  309. endforeach()
  310. foreach(file ${sidebar_icons_at_32px})
  311. copy_icon("${file}" "18x18" "sidebar")
  312. endforeach()
  313. foreach(file ${sidebar_icons_at_64px})
  314. copy_icon("${file}" "18x18@2x" "sidebar")
  315. endforeach()
  316. foreach(file ${sidebar_icons_at_128px})
  317. copy_icon("${file}" "32x32" "sidebar")
  318. endforeach()
  319. foreach(file ${sidebar_icons_at_256px})
  320. copy_icon("${file}" "32x32@2x" "sidebar")
  321. endforeach()
  322. # generate .icns icon file
  323. add_custom_command(
  324. OUTPUT "${_outfilename}.icns"
  325. COMMAND ${ICONUTIL_EXECUTABLE}
  326. ARGS
  327. --convert icns
  328. --output "${_outfilename}.icns"
  329. "${_outfilename}.iconset"
  330. DEPENDS "${iconset_icons}"
  331. WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
  332. )
  333. # This will register the icon into the bundle
  334. set(MACOSX_BUNDLE_ICON_FILE "${_outfilebasename}.icns" PARENT_SCOPE)
  335. # Append the icns file to the sources list so it will be a dependency to the
  336. # main target
  337. set(${appsources} "${${appsources}};${_outfilename}.icns" PARENT_SCOPE)
  338. # Install the icon into the Resources dir in the bundle
  339. set_source_files_properties("${_outfilename}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
  340. else()
  341. message(STATUS "Unable to find the iconutil utility - application will not have an application icon!")
  342. endif()
  343. endif()
  344. endfunction()
  345. macro(_ecm_add_app_icon_categorize_icons icons type known_sizes)
  346. set(_${type}_known_sizes)
  347. foreach(size ${known_sizes})
  348. set(${type}_at_${size}px)
  349. list(APPEND _${type}_known_sizes ${size})
  350. endforeach()
  351. foreach(icon ${icons})
  352. get_filename_component(icon_full ${icon} ABSOLUTE)
  353. if (NOT EXISTS "${icon_full}")
  354. message(AUTHOR_WARNING "${icon_full} does not exist, ignoring")
  355. else()
  356. get_filename_component(icon_name ${icon} NAME)
  357. string(REGEX MATCH "([0-9]+)\\-[^/]+\\.([a-z]+)$"
  358. _dummy "${icon_name}")
  359. set(size "${CMAKE_MATCH_1}")
  360. set(ext "${CMAKE_MATCH_2}")
  361. if (NOT (ext STREQUAL "svg" OR ext STREQUAL "svgz"))
  362. if (NOT size)
  363. message(AUTHOR_WARNING "${icon_full} is not named correctly for ecm_add_app_icon - ignoring")
  364. elseif (NOT ext STREQUAL "png")
  365. message(AUTHOR_WARNING "${icon_full} is not a png file - ignoring")
  366. else()
  367. list(FIND _${type}_known_sizes ${size} offset)
  368. if (offset GREATER -1)
  369. list(APPEND ${type}_at_${size}px "${icon_full}")
  370. elseif()
  371. message(STATUS "not found ${type}_at_${size}px ${icon_full}")
  372. endif()
  373. endif()
  374. endif()
  375. endif()
  376. endforeach()
  377. endmacro()