1#.rst:
2# DeployQt5
3# ---------
4#
5# Functions to help assemble a standalone Qt5 executable.
6#
7# A collection of CMake utility functions useful for deploying Qt5
8# executables.
9#
10# The following functions are provided by this module:
11#
12# ::
13#
14#    write_qt5_conf
15#    resolve_qt5_paths
16#    fixup_qt5_executable
17#    install_qt5_plugin_path
18#    install_qt5_plugin
19#    install_qt5_executable
20#
21# Requires CMake 2.8.9 or greater because Qt 5 does.
22# Also depends on BundleUtilities.cmake.
23#
24# ::
25#
26#   WRITE_QT5_CONF(<qt_conf_dir> <qt_conf_contents>)
27#
28# Writes a qt.conf file with the <qt_conf_contents> into <qt_conf_dir>.
29#
30# ::
31#
32#   RESOLVE_QT5_PATHS(<paths_var> [<executable_path>])
33#
34# Loop through <paths_var> list and if any don't exist resolve them
35# relative to the <executable_path> (if supplied) or the
36# CMAKE_INSTALL_PREFIX.
37#
38# ::
39#
40#   FIXUP_QT5_EXECUTABLE(<executable> [<qtplugins> <libs> <dirs> <plugins_dir> <request_qt_conf>])
41#
42# Copies Qt plugins, writes a Qt configuration file (if needed) and
43# fixes up a Qt5 executable using BundleUtilities so it is standalone
44# and can be drag-and-drop copied to another machine as long as all of
45# the system libraries are compatible.
46#
47# <executable> should point to the executable to be fixed-up.
48#
49# <qtplugins> should contain a list of the names or paths of any Qt
50# plugins to be installed.
51#
52# <libs> will be passed to BundleUtilities and should be a list of any
53# already installed plugins, libraries or executables to also be
54# fixed-up.
55#
56# <dirs> will be passed to BundleUtilities and should contain and
57# directories to be searched to find library dependencies.
58#
59# <plugins_dir> allows an custom plugins directory to be used.
60#
61# <request_qt_conf> will force a qt.conf file to be written even if not
62# needed.
63#
64# ::
65#
66#   INSTALL_QT5_PLUGIN_PATH(plugin executable copy installed_plugin_path_var <plugins_dir> <component> <configurations>)
67#
68# Install (or copy) a resolved <plugin> to the default plugins directory
69# (or <plugins_dir>) relative to <executable> and store the result in
70# <installed_plugin_path_var>.
71#
72# If <copy> is set to TRUE then the plugins will be copied rather than
73# installed.  This is to allow this module to be used at CMake time
74# rather than install time.
75#
76# If <component> is set then anything installed will use this COMPONENT.
77#
78# ::
79#
80#   INSTALL_QT5_PLUGIN(plugin executable copy installed_plugin_path_var <plugins_dir> <component>)
81#
82# Install (or copy) an unresolved <plugin> to the default plugins
83# directory (or <plugins_dir>) relative to <executable> and store the
84# result in <installed_plugin_path_var>.  See documentation of
85# INSTALL_QT5_PLUGIN_PATH.
86#
87# ::
88#
89#   INSTALL_QT5_EXECUTABLE(<executable> [<qtplugins> <libs> <dirs> <plugins_dir> <request_qt_conf> <component>])
90#
91# Installs Qt plugins, writes a Qt configuration file (if needed) and
92# fixes up a Qt5 executable using BundleUtilities so it is standalone
93# and can be drag-and-drop copied to another machine as long as all of
94# the system libraries are compatible.  The executable will be fixed-up
95# at install time.  <component> is the COMPONENT used for bundle fixup
96# and plugin installation.  See documentation of FIXUP_QT5_BUNDLE.
97
98#=============================================================================
99# Copyright 2011 Mike McQuaid <mike@mikemcquaid.com>
100#
101# Distributed under the OSI-approved BSD License (the "License");
102# see accompanying file Copyright.txt for details.
103#
104# This software is distributed WITHOUT ANY WARRANTY; without even the
105# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
106# See the License for more information.
107#=============================================================================
108# (To distribute this file outside of CMake, substitute the full
109#  License text for the above reference.)
110
111# The functions defined in this file depend on the fixup_bundle function
112# (and others) found in BundleUtilities.cmake
113
114include(BundleUtilities)
115set(DeployQt5_cmake_dir "${CMAKE_CURRENT_LIST_DIR}")
116set(DeployQt5_apple_plugins_dir "PlugIns")
117
118function(write_qt5_conf qt_conf_dir qt_conf_contents)
119  set(qt_conf_path "${qt_conf_dir}/qt.conf")
120  message(STATUS "Writing ${qt_conf_path}")
121  file(WRITE "${qt_conf_path}" "${qt_conf_contents}")
122endfunction()
123
124function(resolve_qt5_paths paths_var)
125  set(executable_path ${ARGV1})
126
127  set(paths_resolved)
128  foreach(path ${${paths_var}})
129    if(EXISTS "${path}")
130      list(APPEND paths_resolved "${path}")
131    else()
132      if(${executable_path})
133        list(APPEND paths_resolved "${executable_path}/${path}")
134      else()
135        list(APPEND paths_resolved "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${path}")
136      endif()
137    endif()
138  endforeach()
139  set(${paths_var} ${paths_resolved} PARENT_SCOPE)
140endfunction()
141
142function(fixup_qt5_executable executable)
143  set(qtplugins ${ARGV1})
144  set(libs ${ARGV2})
145  set(dirs ${ARGV3})
146  set(plugins_dir ${ARGV4})
147  set(request_qt_conf ${ARGV5})
148
149  message(STATUS "fixup_qt5_executable")
150  message(STATUS "  executable='${executable}'")
151  message(STATUS "  qtplugins='${qtplugins}'")
152  message(STATUS "  libs='${libs}'")
153  message(STATUS "  dirs='${dirs}'")
154  message(STATUS "  plugins_dir='${plugins_dir}'")
155  message(STATUS "  request_qt_conf='${request_qt_conf}'")
156
157  if(QT_LIBRARY_DIR)
158    list(APPEND dirs "${QT_LIBRARY_DIR}")
159  endif()
160  if(QT_BINARY_DIR)
161    list(APPEND dirs "${QT_BINARY_DIR}")
162  endif()
163
164  if(APPLE)
165    set(qt_conf_dir "${executable}/Contents/Resources")
166    set(executable_path "${executable}")
167    set(write_qt_conf TRUE)
168    if(NOT plugins_dir)
169      set(plugins_dir "${DeployQt5_apple_plugins_dir}")
170    endif()
171  else()
172    get_filename_component(executable_path "${executable}" PATH)
173    if(NOT executable_path)
174      set(executable_path ".")
175    endif()
176    set(qt_conf_dir "${executable_path}")
177    set(write_qt_conf ${request_qt_conf})
178  endif()
179
180  foreach(plugin ${qtplugins})
181    set(installed_plugin_path "")
182    install_qt5_plugin("${plugin}" "${executable}" 1 installed_plugin_path)
183    list(APPEND libs ${installed_plugin_path})
184  endforeach()
185
186  foreach(lib ${libs})
187    if(NOT EXISTS "${lib}")
188      message(FATAL_ERROR "Library does not exist: ${lib}")
189    endif()
190  endforeach()
191
192  resolve_qt5_paths(libs "${executable_path}")
193
194  if(write_qt_conf)
195    set(qt_conf_contents "[Paths]\nPlugins = ${plugins_dir}")
196    write_qt5_conf("${qt_conf_dir}" "${qt_conf_contents}")
197  endif()
198
199  fixup_bundle("${executable}" "${libs}" "${dirs}")
200endfunction()
201
202function(install_qt5_plugin_path plugin executable copy installed_plugin_path_var)
203  set(plugins_dir ${ARGV4})
204  set(component ${ARGV5})
205  set(configurations ${ARGV6})
206  if(EXISTS "${plugin}")
207    if(APPLE)
208      if(NOT plugins_dir)
209        set(plugins_dir "${DeployQt5_apple_plugins_dir}")
210      endif()
211      set(plugins_path "${executable}/Contents/${plugins_dir}")
212    else()
213      get_filename_component(plugins_path "${executable}" PATH)
214      if(NOT plugins_path)
215        set(plugins_path ".")
216      endif()
217      if(plugins_dir)
218        set(plugins_path "${plugins_path}/${plugins_dir}")
219      endif()
220    endif()
221
222    set(plugin_group "")
223
224    get_filename_component(plugin_path "${plugin}" PATH)
225    get_filename_component(plugin_parent_path "${plugin_path}" PATH)
226    get_filename_component(plugin_parent_dir_name "${plugin_parent_path}" NAME)
227    get_filename_component(plugin_name "${plugin}" NAME)
228    string(TOLOWER "${plugin_parent_dir_name}" plugin_parent_dir_name)
229
230    if("${plugin_parent_dir_name}" STREQUAL "plugins")
231      get_filename_component(plugin_group "${plugin_path}" NAME)
232      set(${plugin_group_var} "${plugin_group}")
233    endif()
234    set(plugins_path "${plugins_path}/${plugin_group}")
235
236    if(${copy})
237      file(MAKE_DIRECTORY "${plugins_path}")
238      file(COPY "${plugin}" DESTINATION "${plugins_path}")
239    else()
240      if(configurations AND (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE))
241        set(configurations CONFIGURATIONS ${configurations})
242      else()
243        unset(configurations)
244      endif()
245      install(FILES "${plugin}" DESTINATION "${plugins_path}" ${configurations} ${component})
246    endif()
247    set(${installed_plugin_path_var} "${plugins_path}/${plugin_name}" PARENT_SCOPE)
248  endif()
249endfunction()
250
251function(install_qt5_plugin plugin executable copy installed_plugin_path_var)
252  set(plugins_dir ${ARGV4})
253  set(component ${ARGV5})
254  if(EXISTS "${plugin}")
255    install_qt5_plugin_path("${plugin}" "${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}" "${component}")
256  else()
257    string(TOUPPER "QT_${plugin}_PLUGIN" plugin_var)
258    set(plugin_release_var "${plugin_var}_RELEASE")
259    set(plugin_debug_var "${plugin_var}_DEBUG")
260    set(plugin_release "${${plugin_release_var}}")
261    set(plugin_debug "${${plugin_debug_var}}")
262    if(DEFINED "${plugin_release_var}" AND DEFINED "${plugin_debug_var}" AND NOT EXISTS "${plugin_release}" AND NOT EXISTS "${plugin_debug}")
263      message(WARNING "Qt plugin \"${plugin}\" not recognized or found.")
264    endif()
265    if(NOT EXISTS "${${plugin_debug_var}}")
266      set(plugin_debug "${plugin_release}")
267    endif()
268
269    if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE)
270      install_qt5_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}_release" "${plugins_dir}" "${component}" "Release|RelWithDebInfo|MinSizeRel")
271      install_qt5_plugin_path("${plugin_debug}" "${executable}" "${copy}" "${installed_plugin_path_var}_debug" "${plugins_dir}" "${component}" "Debug")
272
273      if(CMAKE_BUILD_TYPE MATCHES "^Debug$")
274        set(${installed_plugin_path_var} ${${installed_plugin_path_var}_debug})
275      else()
276        set(${installed_plugin_path_var} ${${installed_plugin_path_var}_release})
277      endif()
278    else()
279      install_qt5_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}" "${component}")
280    endif()
281  endif()
282  set(${installed_plugin_path_var} ${${installed_plugin_path_var}} PARENT_SCOPE)
283endfunction()
284
285function(install_qt5_executable executable)
286  set(qtplugins ${ARGV1})
287  set(libs ${ARGV2})
288  set(dirs ${ARGV3})
289  set(plugins_dir ${ARGV4})
290  set(request_qt_conf ${ARGV5})
291  set(component ${ARGV6})
292  if(QT_LIBRARY_DIR)
293    list(APPEND dirs "${QT_LIBRARY_DIR}")
294  endif()
295  if(QT_BINARY_DIR)
296    list(APPEND dirs "${QT_BINARY_DIR}")
297  endif()
298  if(TARGET Qt5::Core)
299	get_property(_locCore TARGET Qt5::Core PROPERTY LOCATION_RELEASE)
300	get_filename_component(_loc ${_locCore} DIRECTORY)
301	message(STATUS "Adding Qt 5 directory: ${_loc}")
302	list(APPEND dirs "${_loc}")
303  else()
304    message(FATAL_ERROR "No Qt5::Core target found, ensure it is available")
305  endif()
306  if(component)
307    set(component COMPONENT ${component})
308  else()
309    unset(component)
310  endif()
311
312  get_filename_component(executable_absolute "${executable}" ABSOLUTE)
313  if(EXISTS "${QT_QTCORE_LIBRARY_RELEASE}")
314    gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_RELEASE}" qtcore_type)
315  elseif(EXISTS "${QT_QTCORE_LIBRARY_DEBUG}")
316    gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_DEBUG}" qtcore_type)
317  endif()
318  if(qtcore_type STREQUAL "system")
319    set(qt_plugins_dir "")
320  endif()
321
322  if(QT_IS_STATIC)
323    message(WARNING "Qt built statically: not installing plugins.")
324  else()
325    if(APPLE)
326      get_property(loc TARGET Qt5::QCocoaIntegrationPlugin
327        PROPERTY LOCATION_RELEASE)
328      install_qt5_plugin("${loc}" "${executable}" 0 installed_plugin_paths
329        "PlugIns" "${component}")
330      list(APPEND libs ${installed_plugin_paths})
331    elseif(WIN32)
332      get_property(loc TARGET Qt5::QWindowsIntegrationPlugin
333        PROPERTY LOCATION_RELEASE)
334      install_qt5_plugin("${loc}" "${executable}" 0 installed_plugin_paths
335        "" "${component}")
336      list(APPEND libs ${installed_plugin_paths})
337    endif()
338    foreach(plugin ${qtplugins})
339      set(installed_plugin_paths "")
340      install_qt5_plugin("${plugin}" "${executable}" 0 installed_plugin_paths "${plugins_dir}" "${component}")
341      list(APPEND libs ${installed_plugin_paths})
342    endforeach()
343  endif()
344
345  resolve_qt5_paths(libs "")
346
347  install(CODE
348  "include(\"${DeployQt5_cmake_dir}/DeployQt5.cmake\")
349  set(BU_CHMOD_BUNDLE_ITEMS TRUE)
350  fixup_qt5_executable(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${executable}\" \"\" \"${libs}\" \"${dirs}\" \"${plugins_dir}\" \"${request_qt_conf}\")"
351    ${component}
352    )
353endfunction()
354