ECMAddAppIcon.cmake 18 KB

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