Resync (forgot to add new files?)
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / QtDialog / CMakeIngestOSXBundleLibraries.cmake
blob37e470142e5368ed4b10e31cb8a4ee786abaa51b
2 # CMakeIngestOSXBundleLibraries.cmake
4 # Only for the Mac build.
6 # Depends on OS tools:
7 #   otool
8 #   install_name_tool
10 # This script ingests libraries and frameworks into an existing .app bundle and
11 # then uses install_name_tool to fixup the references to the newly embedded
12 # libraries so that they all refer to each other via "@executable_path."
14 # The main intent (and simplifying assumption used for developing the script)
15 # is to have a single executable .app bundle that becomes "self-contained" by
16 # copying all non-system libs that it depends on into itself. The further
17 # assumption is that all such dependencies are simple .dylib shared library
18 # files or Mac Framework libraries.
20 # This script can be used as part of the build via ADD_CUSTOM_COMMAND, or used
21 # only during make install via INSTALL SCRIPT.
23 if(NOT DEFINED input_file)
24   message(FATAL_ERROR "
25 ${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): error: Variable input_file is not defined.
27 Use a command line like this to use this script:
28   cmake \"-Dinput_file=filename\" \"-Dextra_libs=/path/to/lib1;/path/to/lib2\" \"-Dlib_path=/path/to/unqualified/libs\" -P \"${CMAKE_CURRENT_LIST_FILE}\"
30 'input_file' should be the main executable inside a Mac bundle directory structure.
31 For example, use 'bin/paraview.app/Contents/MacOS/paraview' from a ParaView binary dir.
33 'extra_libs' should be a semi-colon separated list of full path names to extra libraries
34 to copy into the bundle that cannot be derived from otool -L output. For example, you may
35 also want to fixup dynamically loaded plugins from your build tree and copy them into the
36 bundle.
38 'lib_path' should be the path where to find libraries referenced without a path name in
39 otool -L output.
42 endif(NOT DEFINED input_file)
43 message("ingest ${input_file}")
44 set(eol_char "E")
46 if(APPLE)
47   set(dep_tool "otool")
48   set(dep_cmd_args "-L")
49   set(dep_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
50 endif(APPLE)
52 message("")
53 message("# Script \"${CMAKE_CURRENT_LIST_FILE}\" running...")
54 message("")
55 message("input_file: '${input_file}'")
56 message("extra_libs: '${extra_libs}'")
57 message("lib_path: '${lib_path}'")
58 message("")
60 get_filename_component(input_file_full "${input_file}" ABSOLUTE)
61 message("input_file_full: '${input_file_full}'")
63 get_filename_component(bundle "${input_file_full}/../../.." ABSOLUTE)
64 message("bundle: '${bundle}'")
67 find_program(dep_cmd ${dep_tool})
69 # find the full path to the framework in path set the result
70 # in pathout
71 macro(find_framework_full_path path pathout)
72   set(${pathout} "${path}")
73   if(NOT EXISTS "${path}")
74     set(FRAMEWORK_SEARCH "/Library/Frameworks"
75       "/System/Library/Frameworks" )
76     set(__FOUND FALSE)
77     foreach(f ${FRAMEWORK_SEARCH})
78       set(newd "${f}/${path}")
79       if(EXISTS "${newd}" AND NOT __FOUND)
80         set(${pathout} "${newd}")
81         set(__FOUND TRUE)
82       endif(EXISTS "${newd}" AND NOT __FOUND)
83     endforeach(f)
84   endif(NOT EXISTS "${path}")
85 endmacro(find_framework_full_path)
88 macro(append_unique au_list_var au_value)
89   set(${au_list_var} ${${au_list_var}} "${au_value}")
90 endmacro(append_unique)
93 macro(gather_dependents gd_target gd_dependents_var)
94   execute_process(
95     COMMAND ${dep_cmd} ${dep_cmd_args} ${gd_target}
96     OUTPUT_VARIABLE dep_tool_ov
97     )
99   string(REGEX REPLACE ";" "\\\\;" dep_candidates "${dep_tool_ov}")
100   string(REGEX REPLACE "\n" "${eol_char};" dep_candidates "${dep_candidates}")
102   set(${gd_dependents_var} "")
104   foreach(candidate ${dep_candidates})
105   if("${candidate}" MATCHES "${dep_regex}")
106     string(REGEX REPLACE "${dep_regex}" "\\1" raw_item "${candidate}")
107     string(REGEX REPLACE "${dep_regex}" "\\2" raw_compat_version "${candidate}")
108     string(REGEX REPLACE "${dep_regex}" "\\3" raw_current_version "${candidate}")
110     set(item "${raw_item}")
112     string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
113     string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
114     string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
116     string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
117     string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
118     string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
120     #message("${raw_item} - compat ${raw_compat_version} - current ${raw_current_version}")
121     append_unique("${gd_dependents_var}" "${item}")
122   else("${candidate}" MATCHES "${dep_regex}")
123     if("${candidate}" STREQUAL "${gd_target}:${eol_char}")
124       #message("info: ignoring target name...")
125     else("${candidate}" STREQUAL "${gd_target}:${eol_char}")
126       message("error: candidate='${candidate}'")
127     endif("${candidate}" STREQUAL "${gd_target}:${eol_char}")
128   endif("${candidate}" MATCHES "${dep_regex}")
129   endforeach(candidate)
130 endmacro(gather_dependents)
133 message("Gathering dependent libraries for '${input_file_full}'...")
134 gather_dependents("${input_file_full}" deps)
135 message("")
138 # Order lexicographically:
140 list(SORT deps)
143 # Split into separate lists, "system" "embedded" and "nonsystem" libraries.
144 # System libs are assumed to be available on all target runtime Macs and do not
145 # need to be copied/fixed-up by this script. Embedded libraries are assumed to
146 # be in the bundle and fixed-up already. Only non-system, non-embedded libs
147 # need copying and fixing up...
149 set(system_deps "")
150 set(embedded_deps "")
151 set(nonsystem_deps "")
153 foreach(d ${deps})
154   set(d_is_embedded_lib 0)
155   set(d_is_system_lib 0)
157   if("${d}" MATCHES "^(/System/Library|/usr/lib)")
158     set(d_is_system_lib 1)
159   else("${d}" MATCHES "^(/System/Library|/usr/lib)")
160     if("${d}" MATCHES "^@executable_path")
161       set(d_is_embedded_lib 1)
162     endif("${d}" MATCHES "^@executable_path")
163   endif("${d}" MATCHES "^(/System/Library|/usr/lib)")
165   if(d_is_system_lib)
166     set(system_deps ${system_deps} "${d}")
167   else(d_is_system_lib)
168     if(d_is_embedded_lib)
169       set(embedded_deps ${embedded_deps} "${d}")
170     else(d_is_embedded_lib)
171       set(nonsystem_deps ${nonsystem_deps} "${d}")
172     endif(d_is_embedded_lib)
173   endif(d_is_system_lib)
174 endforeach(d)
176 message("")
177 message("system_deps:")
178 foreach(d ${system_deps})
179   message("${d}")
180 endforeach(d ${system_deps})
182 message("")
183 message("embedded_deps:")
184 foreach(d ${embedded_deps})
185   message("${d}")
186 endforeach(d ${embedded_deps})
188 message("")
189 message("nonsystem_deps:")
190 foreach(d ${nonsystem_deps})
191   message("${d}")
192 endforeach(d ${nonsystem_deps})
194 message("")
197 macro(copy_library_into_bundle clib_bundle clib_libsrc clib_dstlibs clib_fixups)
198   #
199   # If the source library is a framework, copy just the shared lib bit of the framework
200   # into the bundle under "${clib_bundle}/Contents/Frameworks" - if it is just a dylib
201   # copy it into the same directory with the main bundle executable under
202   # "${clib_bundle}/Contents/MacOS"
203   #
204   if("${clib_libsrc}" MATCHES ".framework/.*/.*/.*")
205     # make sure clib_libsrc is a full path to the framework as a framework
206     # maybe linked in with relative paths in some cases
207     find_framework_full_path("${clib_libsrc}" fw_full_src)
208     get_filename_component(fw_src "${fw_full_src}" ABSOLUTE)
209     get_filename_component(fw_srcdir "${clib_libsrc}/../../.." ABSOLUTE)
210     get_filename_component(fwdirname "${fw_srcdir}" NAME)
211     string(REGEX REPLACE "^(.*)\\.framework$" "\\1" fwname "${fwdirname}")
212     string(REGEX REPLACE "^.*/${fwname}\\.framework/(.*)$" "\\1" fwlibname "${clib_libsrc}")
213     set(fw_dstdir "${clib_bundle}/Contents/Frameworks/${fwdirname}")
215 #     message("")
216 #     message("fwdirname: '${fwdirname}'")
217 #     message("fwname: '${fwname}'")
218 #     message("fwlibname: '${fwlibname}'")
219 #     message("fw_src: '${fw_src}'")
220 #     message("fw_srcdir: '${fw_srcdir}'")
221 #     message("fw_dstdir: '${fw_dstdir}'")
222 #     message("new_name: '@executable_path/../Frameworks/${fwdirname}/${fwlibname}'")
223 #     message("")
225     message("Copying ${fw_srcdir} into bundle...")
227     # This command copies the *entire* framework recursively:
228     #
229 #    execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory
230 #      "${fw_srcdir}" "${fw_dstdir}"
231 #    )
233     # This command copies just the main shared lib of the framework:
234     # (This technique will not work for frameworks that have necessary
235     # resource or auxiliary files...)
236     #
237     message("fw_src = [${fw_src}]   fw_full_src = [${fw_full_src}]")
238     message("Copy: ${CMAKE_COMMAND} -E copy \"${fw_src}\"  \"${fw_dstdir}/${fwlibname}\"")
239     execute_process(COMMAND ${CMAKE_COMMAND} -E copy
240       "${fw_src}" "${fw_dstdir}/${fwlibname}"
241     )
243     execute_process(COMMAND install_name_tool
244       -id "@executable_path/../Frameworks/${fwdirname}/${fwlibname}"
245       "${clib_bundle}/Contents/Frameworks/${fwdirname}/${fwlibname}"
246     )
247     set(${clib_dstlibs} ${${clib_dstlibs}}
248       "${clib_bundle}/Contents/Frameworks/${fwdirname}/${fwlibname}"
249     )
250     set(${clib_fixups} ${${clib_fixups}}
251       "-change"
252       "${clib_libsrc}"
253       "@executable_path/../Frameworks/${fwdirname}/${fwlibname}"
254     )
255   else("${clib_libsrc}" MATCHES ".framework/.*/.*/.*")
256     if("${clib_libsrc}" MATCHES "/")
257       set(clib_libsrcfull "${clib_libsrc}")
258     else("${clib_libsrc}" MATCHES "/")
259       set(clib_libsrcfull "${lib_path}/${clib_libsrc}")
260       if(NOT EXISTS "${clib_libsrcfull}")
261         message(FATAL_ERROR "error: '${clib_libsrcfull}' does not exist...")
262       endif(NOT EXISTS "${clib_libsrcfull}")
263     endif("${clib_libsrc}" MATCHES "/")
265     get_filename_component(dylib_src "${clib_libsrcfull}" ABSOLUTE)
266     get_filename_component(dylib_name "${dylib_src}" NAME)
267     set(dylib_dst "${clib_bundle}/Contents/MacOS/${dylib_name}")
269 #    message("dylib_src: ${dylib_src}")
270 #    message("dylib_dst: ${dylib_dst}")
271 #    message("new_name: '@executable_path/${dylib_name}'")
273     message("Copying ${dylib_src} into bundle...")
274     execute_process(COMMAND ${CMAKE_COMMAND} -E copy
275       "${dylib_src}" "${dylib_dst}")
276     execute_process(COMMAND install_name_tool
277       -id "@executable_path/${dylib_name}"
278       "${dylib_dst}"
279     )
280     set(${clib_dstlibs} ${${clib_dstlibs}}
281       "${dylib_dst}"
282     )
283     set(${clib_fixups} ${${clib_fixups}}
284       "-change"
285       "${clib_libsrc}"
286       "@executable_path/${dylib_name}"
287     )
288   endif("${clib_libsrc}" MATCHES ".framework/.*/.*/.*")
289 endmacro(copy_library_into_bundle)
292 # Copy dependent "nonsystem" libraries into the bundle:
294 message("Copying dependent libraries into bundle...")
295 set(srclibs ${nonsystem_deps} ${extra_libs})
296 set(dstlibs "")
297 set(fixups "")
298 foreach(d ${srclibs})
299   message("copy it --- ${d}")
300   copy_library_into_bundle("${bundle}" "${d}" dstlibs fixups)
301 endforeach(d)
303 message("")
304 message("dstlibs='${dstlibs}'")
305 message("")
306 message("fixups='${fixups}'")
307 message("")
310 # Fixup references to copied libraries in the main bundle executable and in the
311 # copied libraries themselves:
313 if(NOT "${fixups}" STREQUAL "")
314   message("Fixing up references...")
315   foreach(d ${dstlibs} "${input_file_full}")
316     message("fixing up references in: '${d}'")
317     execute_process(COMMAND install_name_tool ${fixups} "${d}")
318   endforeach(d)
319   message("")
320 endif(NOT "${fixups}" STREQUAL "")
323 # List all references to eyeball them and make sure they look right:
325 message("Listing references...")
326 foreach(d ${dstlibs} "${input_file_full}")
327   execute_process(COMMAND otool -L "${d}")
328   message("")
329 endforeach(d)
330 message("")
333 # Output file:
335 #get_filename_component(script_name "${CMAKE_CURRENT_LIST_FILE}" NAME)
336 #file(WRITE "${input_file_full}_${script_name}" "# Script \"${CMAKE_CURRENT_LIST_FILE}\" completed.\n")
337 message("")
338 message("# Script \"${CMAKE_CURRENT_LIST_FILE}\" completed.")
339 message("")