1# - Functions to help assemble a standalone Qt5 executable.
2# A collection of CMake utility functions useful for deploying
3# Qt5 executables.
4#
5# The following functions are provided by this module:
6#   write_qt5_conf
7#   resolve_qt5_paths
8#   fixup_qt5_executable
9#   install_qt5_plugin_path
10#   install_qt5_plugin
11#   install_qt5_executable
12# Requires CMake 2.6 or greater because it uses function and
13# PARENT_SCOPE. Also depends on BundleUtilities.cmake.
14#
15#  WRITE_QT5_CONF(<qt_conf_dir> <qt_conf_contents>)
16# Writes a qt.conf file with the <qt_conf_contents> into <qt_conf_dir>.
17#
18#  RESOLVE_QT5_PATHS(<paths_var> [<executable_path>])
19# Loop through <paths_var> list and if any don't exist resolve them
20# relative to the <executable_path> (if supplied) or the CMAKE_INSTALL_PREFIX.
21#
22#  FIXUP_QT5_EXECUTABLE(<executable> [<qtplugins> <libs> <dirs> <plugins_dir> <request_qt_conf>])
23# Copies Qt plugins, writes a Qt configuration file (if needed) and fixes up a
24# Qt5 executable using BundleUtilities so it is standalone and can be
25# drag-and-drop copied to another machine as long as all of the system
26# libraries are compatible.
27#
28# <executable> should point to the executable to be fixed-up.
29#
30# <qtplugins> should contain a list of the names or paths of any Qt plugins
31# to be installed.
32#
33# <libs> will be passed to BundleUtilities and should be a list of any already
34# installed plugins, libraries or executables to also be fixed-up.
35#
36# <dirs> will be passed to BundleUtilities and should contain and directories
37# to be searched to find library dependencies.
38#
39# <plugins_dir> allows an custom plugins directory to be used.
40#
41# <request_qt_conf> will force a qt.conf file to be written even if not needed.
42#
43#  INSTALL_QT5_PLUGIN_PATH(plugin executable copy installed_plugin_path_var <plugins_dir> <component> <configurations>)
44# Install (or copy) a resolved <plugin> to the default plugins directory
45# (or <plugins_dir>) relative to <executable> and store the result in
46# <installed_plugin_path_var>.
47#
48# If <copy> is set to TRUE then the plugins will be copied rather than
49# installed. This is to allow this module to be used at CMake time rather than
50# install time.
51#
52# If <component> is set then anything installed will use this COMPONENT.
53#
54#  INSTALL_QT5_PLUGIN(plugin executable copy installed_plugin_path_var <plugins_dir> <component>)
55# Install (or copy) an unresolved <plugin> to the default plugins directory
56# (or <plugins_dir>) relative to <executable> and store the result in
57# <installed_plugin_path_var>. See documentation of INSTALL_QT5_PLUGIN_PATH.
58#
59#  INSTALL_QT5_EXECUTABLE(<executable> [<qtplugins> <libs> <dirs> <plugins_dir> <request_qt_conf> <component>])
60# Installs Qt plugins, writes a Qt configuration file (if needed) and fixes up
61# a Qt5 executable using BundleUtilities so it is standalone and can be
62# drag-and-drop copied to another machine as long as all of the system
63# libraries are compatible. The executable will be fixed-up at install time.
64# <component> is the COMPONENT used for bundle fixup and plugin installation.
65# See documentation of FIXUP_QT5_BUNDLE.
66
67#=============================================================================
68# Copyright 2011 Mike McQuaid <m...@mikemcquaid.com>
69# Copyright 2013 Mihai Moldovan <io...@ionic.de>
70# CMake - Cross Platform Makefile Generator
71# Copyright 2000-2011 Kitware, Inc., Insight Software Consortium
72# All rights reserved.
73#
74# Redistribution and use in source and binary forms, with or without
75# modification, are permitted provided that the following conditions
76# are met:
77#
78# * Redistributions of source code must retain the above copyright
79#   notice, this list of conditions and the following disclaimer.
80#
81# * Redistributions in binary form must reproduce the above copyright
82#   notice, this list of conditions and the following disclaimer in the
83#   documentation and/or other materials provided with the distribution.
84#
85# * Neither the names of Kitware, Inc., the Insight Software Consortium,
86#   nor the names of their contributors may be used to endorse or promote
87#   products derived from this software without specific prior written
88#   permission.
89#
90# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
91# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
92# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
93# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
94# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
95# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
96# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
97# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
98# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
99# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
100# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
101
102# The functions defined in this file depend on the fixup_bundle function
103# (and others) found in BundleUtilities.cmake
104
105include(BundleUtilities)
106set(DeployQt5_cmake_dir "${CMAKE_CURRENT_LIST_DIR}")
107set(DeployQt5_apple_plugins_dir "PlugIns")
108
109function(write_qt5_conf qt_conf_dir qt_conf_contents)
110        set(qt_conf_path "${qt_conf_dir}/qt.conf")
111        message(STATUS "Writing ${qt_conf_path}")
112        file(WRITE "${qt_conf_path}" "${qt_conf_contents}")
113endfunction()
114
115function(resolve_qt5_paths paths_var)
116        set(executable_path ${ARGV1})
117
118        set(paths_resolved)
119        foreach(path ${${paths_var}})
120                if(EXISTS "${path}")
121                        list(APPEND paths_resolved "${path}")
122                else()
123                        if(${executable_path})
124                                list(APPEND paths_resolved
125"${executable_path}/${path}")
126                        else()
127                                list(APPEND paths_resolved
128"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${path}")
129                        endif()
130                endif()
131        endforeach()
132        set(${paths_var} ${paths_resolved} PARENT_SCOPE)
133endfunction()
134
135function(fixup_qt5_executable executable)
136        set(qtplugins ${ARGV1})
137        set(libs ${ARGV2})
138        set(dirs ${ARGV3})
139        set(plugins_dir ${ARGV4})
140        set(request_qt_conf ${ARGV5})
141
142        message(STATUS "fixup_qt5_executable")
143        message(STATUS "  executable='${executable}'")
144        message(STATUS "  qtplugins='${qtplugins}'")
145        message(STATUS "  libs='${libs}'")
146        message(STATUS "  dirs='${dirs}'")
147        message(STATUS "  plugins_dir='${plugins_dir}'")
148        message(STATUS "  request_qt_conf='${request_qt_conf}'")
149
150        if(QT_LIBRARY_DIR)
151                list(APPEND dirs "${QT_LIBRARY_DIR}")
152        endif()
153        if(QT_BINARY_DIR)
154                list(APPEND dirs "${QT_BINARY_DIR}")
155        endif()
156
157        if(APPLE)
158                set(qt_conf_dir "${executable}/Contents/Resources")
159                set(executable_path "${executable}")
160                set(write_qt_conf TRUE)
161                if(NOT plugins_dir)
162                        set(plugins_dir "${DeployQt5_apple_plugins_dir}")
163                endif()
164        else()
165                get_filename_component(executable_path "${executable}" PATH)
166                if(NOT executable_path)
167                        set(executable_path ".")
168                endif()
169                set(qt_conf_dir "${executable_path}")
170                set(write_qt_conf ${request_qt_conf})
171        endif()
172
173        foreach(plugin ${qtplugins})
174                set(installed_plugin_path "")
175                install_qt5_plugin("${plugin}" "${executable}" 1
176installed_plugin_path)
177                list(APPEND libs ${installed_plugin_path})
178        endforeach()
179
180        foreach(lib ${libs})
181                if(NOT EXISTS "${lib}")
182                        message(FATAL_ERROR "Library does not exist: ${lib}")
183                endif()
184        endforeach()
185
186        resolve_qt5_paths(libs "${executable_path}")
187
188        if(write_qt_conf)
189                set(qt_conf_contents "[Paths]\nPlugins = ${plugins_dir}")
190                write_qt5_conf("${qt_conf_dir}" "${qt_conf_contents}")
191        endif()
192
193        fixup_bundle("${executable}" "${libs}" "${dirs}")
194endfunction()
195
196function(install_qt5_plugin_path plugin executable copy
197installed_plugin_path_var)
198        set(plugins_dir ${ARGV4})
199        set(component ${ARGV5})
200        set(configurations ${ARGV6})
201        if(EXISTS "${plugin}")
202                if(APPLE)
203                        if(NOT plugins_dir)
204                                set(plugins_dir
205"${DeployQt5_apple_plugins_dir}")
206                        endif()
207                        set(plugins_path
208"${executable}/Contents/${plugins_dir}")
209                else()
210                        get_filename_component(plugins_path "${executable}"
211PATH)
212                        if(NOT plugins_path)
213                                set(plugins_path ".")
214                        endif()
215                        if(plugins_dir)
216                                set(plugins_path
217"${plugins_path}/${plugins_dir}")
218                        endif()
219                endif()
220
221                set(plugin_group "")
222
223                get_filename_component(plugin_path "${plugin}" PATH)
224                get_filename_component(plugin_parent_path "${plugin_path}" PATH)
225                get_filename_component(plugin_parent_dir_name
226"${plugin_parent_path}" NAME)
227                get_filename_component(plugin_name "${plugin}" NAME)
228                string(TOLOWER "${plugin_parent_dir_name}"
229plugin_parent_dir_name)
230
231                if("${plugin_parent_dir_name}" STREQUAL "plugins")
232                        get_filename_component(plugin_group "${plugin_path}"
233NAME)
234                        set(${plugin_group_var} "${plugin_group}")
235                endif()
236                set(plugins_path "${plugins_path}/${plugin_group}")
237
238                if(${copy})
239                        file(MAKE_DIRECTORY "${plugins_path}")
240                        file(COPY "${plugin}" DESTINATION "${plugins_path}")
241                else()
242                        if(configurations AND (CMAKE_CONFIGURATION_TYPES OR
243CMAKE_BUILD_TYPE))
244                                set(configurations CONFIGURATIONS
245${configurations})
246                        else()
247                                unset(configurations)
248                        endif()
249                        install(FILES "${plugin}" DESTINATION "${plugins_path}"
250${configurations} ${component})
251                endif()
252                set(${installed_plugin_path_var}
253"${plugins_path}/${plugin_name}" PARENT_SCOPE)
254        endif()
255endfunction()
256
257function(install_qt5_plugin plugin executable copy installed_plugin_path_var)
258        set(plugins_dir ${ARGV4})
259        set(component ${ARGV5})
260        if(EXISTS "${plugin}")
261                install_qt5_plugin_path("${plugin}" "${executable}" "${copy}"
262"${installed_plugin_path_var}" "${plugins_dir}" "${component}")
263        else()
264                #string(TOUPPER "QT_${plugin}_PLUGIN" plugin_var)
265                set(plugin_release)
266                set(plugin_debug)
267                set(plugin_tmp_path)
268                set(plugin_find_path "${Qt5Core_DIR}/../../../plugins/")
269                get_filename_component(plugin_find_path "${plugin_find_path}"
270REALPATH)
271                if(COMMAND cmake_policy)
272                    CMAKE_POLICY(SET CMP0009 NEW)
273                    #CMAKE_POLICY(SET CMP0011 NEW) # disabling a warning about policy changing in this scope
274                endif(COMMAND cmake_policy)
275
276                if (APPLE)
277                    set(plugin_find_release_filename "lib${plugin}.dylib")
278                    set(plugin_find_debug_filename "lib${plugin}_debug.dylib")
279                    file(GLOB_RECURSE pluginlist "${plugin_find_path}" "${plugin_find_path}/*/lib*.dylib")
280                endif()
281                if(WIN32)
282                    set(plugin_find_release_filename "${plugin}.dll")
283                    set(plugin_find_debug_filename "${plugin}d.dll")
284                    file(GLOB_RECURSE pluginlist "${plugin_find_path}" "${plugin_find_path}/*/*.dll")
285                endif(WIN32)
286                foreach(found_plugin ${pluginlist})
287                  get_filename_component(curname "${found_plugin}" NAME)
288                  if("${curname}" STREQUAL "${plugin_find_release_filename}")
289                    set(plugin_tmp_release_path "${found_plugin}")
290                  endif()
291                  if("${curname}" STREQUAL "${plugin_find_debug_filename}")
292                    set(plugin_tmp_debug_path "${found_plugin}")
293                  endif()
294                endforeach()
295
296                if((NOT DEFINED plugin_tmp_release_path OR NOT EXISTS
297"${plugin_tmp_release_path}") AND (NOT DEFINED plugin_tmp_debug_PATH OR NOT
298EXISTS "${plugin_tmp_debug_path}"))
299                        message(WARNING "Qt plugin \"${plugin}\" not recognized
300or found.")
301                endif()
302
303                if(EXISTS "${plugin_tmp_release_path}")
304                  set(plugin_release "${plugin_tmp_release_path}")
305                elseif(EXISTS "${plugin_tmp_debug_path}")
306                  set(plugin_release "${plugin_tmp_debug_path}")
307                endif()
308
309                if(EXISTS "${plugin_tmp_debug_path}")
310                  set(plugin_debug "${plugin_tmp_debug_path}")
311                elseif(EXISTS "${plugin_tmp_release_path}")
312                  set(plugin_debug "${plugin_tmp_release_path}")
313                endif()
314
315                if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE)
316                        install_qt5_plugin_path("${plugin_release}"
317"${executable}" "${copy}" "${installed_plugin_path_var}_release"
318"${plugins_dir}" "${component}" "Release|RelWithDebInfo|MinSizeRel")
319                        install_qt5_plugin_path("${plugin_debug}"
320"${executable}" "${copy}" "${installed_plugin_path_var}_debug" "${plugins_dir}"
321"${component}" "Debug")
322
323                        if(CMAKE_BUILD_TYPE MATCHES "^Debug$")
324                                set(${installed_plugin_path_var}
325${${installed_plugin_path_var}_debug})
326                        else()
327                                set(${installed_plugin_path_var}
328${${installed_plugin_path_var}_release})
329                        endif()
330                else()
331                        install_qt5_plugin_path("${plugin_release}"
332"${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}"
333"${component}")
334                endif()
335        endif()
336        set(${installed_plugin_path_var} ${${installed_plugin_path_var}}
337PARENT_SCOPE)
338endfunction()
339
340function(install_qt5_executable executable)
341        set(qtplugins ${ARGV1})
342        set(libs ${ARGV2})
343        set(dirs ${ARGV3})
344        set(plugins_dir ${ARGV4})
345        set(request_qt_conf ${ARGV5})
346        set(component ${ARGV6})
347        if(QT_LIBRARY_DIR)
348                list(APPEND dirs "${QT_LIBRARY_DIR}")
349        endif()
350        if(QT_BINARY_DIR)
351                list(APPEND dirs "${QT_BINARY_DIR}")
352        endif()
353        if(component)
354                set(component COMPONENT ${component})
355        else()
356                unset(component)
357        endif()
358
359        get_filename_component(executable_absolute "${executable}" ABSOLUTE)
360        if(EXISTS "${QT_QTCORE_LIBRARY_RELEASE}")
361            gp_file_type("${executable_absolute}"
362"${QT_QTCORE_LIBRARY_RELEASE}" qtcore_type)
363        elseif(EXISTS "${QT_QTCORE_LIBRARY_DEBUG}")
364            gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_DEBUG}"
365qtcore_type)
366        endif()
367        if(qtcore_type STREQUAL "system")
368                set(qt_plugins_dir "")
369        endif()
370
371        if(QT_IS_STATIC)
372                message(WARNING "Qt built statically: not installing plugins.")
373        else()
374                foreach(plugin ${qtplugins})
375                        set(installed_plugin_paths "")
376                        install_qt5_plugin("${plugin}" "${executable}" 0
377installed_plugin_paths "${plugins_dir}" "${component}")
378                        list(APPEND libs ${installed_plugin_paths})
379                endforeach()
380        endif()
381
382        resolve_qt5_paths(libs "")
383
384        install(CODE
385  "include(\"${DeployQt5_cmake_dir}/DeployQt5.cmake\")
386  set(BU_CHMOD_BUNDLE_ITEMS TRUE)
387  FIXUP_QT5_EXECUTABLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${executable}\"
388\"\" \"${libs}\" \"${dirs}\" \"${plugins_dir}\" \"${request_qt_conf}\")"
389                ${component}
390        )
391endfunction()
392