1# Taken from https://github.com/Kitware/CMake/blob/45ed314bfff5b9b59dcba8139ab1c695a81a05f3/Modules/BundleUtilities.cmake,
2# because macOS packaging needs 45ed314 in order to preserve executable scripts in the bundle.
3# This change has been included in CMake 3.20.1.
4
5# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
6# file Copyright.txt or https://cmake.org/licensing for details.
7
8#[=======================================================================[.rst:
9BundleUtilities
10---------------
11
12Functions to help assemble a standalone bundle application.
13
14A collection of CMake utility functions useful for dealing with ``.app``
15bundles on the Mac and bundle-like directories on any OS.
16
17The following functions are provided by this module:
18
19.. code-block:: cmake
20
21   fixup_bundle
22   copy_and_fixup_bundle
23   verify_app
24   get_bundle_main_executable
25   get_dotapp_dir
26   get_bundle_and_executable
27   get_bundle_all_executables
28   get_item_key
29   get_item_rpaths
30   clear_bundle_keys
31   set_bundle_key_values
32   get_bundle_keys
33   copy_resolved_item_into_bundle
34   copy_resolved_framework_into_bundle
35   fixup_bundle_item
36   verify_bundle_prerequisites
37   verify_bundle_symlinks
38
39Requires CMake 2.6 or greater because it uses function, break and
40``PARENT_SCOPE``.  Also depends on ``GetPrerequisites.cmake``.
41
42DO NOT USE THESE FUNCTIONS AT CONFIGURE TIME (from ``CMakeLists.txt``)!
43Instead, invoke them from an :command:`install(CODE)` or
44:command:`install(SCRIPT)` rule.
45
46.. code-block:: cmake
47
48  fixup_bundle(<app> <libs> <dirs>)
49
50Fix up ``<app>`` bundle in-place and make it standalone, such that it can be
51drag-n-drop copied to another machine and run on that machine as long
52as all of the system libraries are compatible.
53
54If you pass plugins to ``fixup_bundle`` as the libs parameter, you should
55install them or copy them into the bundle before calling ``fixup_bundle``.
56The ``<libs>`` parameter is a list of libraries that must be fixed up, but
57that cannot be determined by ``otool`` output analysis  (i.e. ``plugins``).
58
59Gather all the keys for all the executables and libraries in a bundle,
60and then, for each key, copy each prerequisite into the bundle.  Then
61fix each one up according to its own list of prerequisites.
62
63Then clear all the keys and call ``verify_app`` on the final bundle to
64ensure that it is truly standalone.
65
66As an optional parameter (``IGNORE_ITEM``) a list of file names can be passed,
67which are then ignored
68(e.g. ``IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"``).
69
70.. code-block:: cmake
71
72  copy_and_fixup_bundle(<src> <dst> <libs> <dirs>)
73
74Makes a copy of the bundle ``<src>`` at location ``<dst>`` and then fixes up
75the new copied bundle in-place at ``<dst>``.
76
77.. code-block:: cmake
78
79  verify_app(<app>)
80
81Verifies that an application ``<app>`` appears valid based on running
82analysis tools on it.  Calls :command:`message(FATAL_ERROR)` if the application
83is not verified.
84
85As an optional parameter (``IGNORE_ITEM``) a list of file names can be passed,
86which are then ignored
87(e.g. ``IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"``)
88
89.. code-block:: cmake
90
91  get_bundle_main_executable(<bundle> <result_var>)
92
93The result will be the full path name of the bundle's main executable
94file or an ``error:`` prefixed string if it could not be determined.
95
96.. code-block:: cmake
97
98  get_dotapp_dir(<exe> <dotapp_dir_var>)
99
100Returns the nearest parent dir whose name ends with ``.app`` given the
101full path to an executable.  If there is no such parent dir, then
102simply return the dir containing the executable.
103
104The returned directory may or may not exist.
105
106.. code-block:: cmake
107
108  get_bundle_and_executable(<app> <bundle_var> <executable_var> <valid_var>)
109
110Takes either a ``.app`` directory name or the name of an executable
111nested inside a ``.app`` directory and returns the path to the ``.app``
112directory in ``<bundle_var>`` and the path to its main executable in
113``<executable_var>``.
114
115.. code-block:: cmake
116
117  get_bundle_all_executables(<bundle> <exes_var>)
118
119Scans ``<bundle>`` bundle recursively for all ``<exes_var>`` executable
120files and accumulates them into a variable.
121
122.. code-block:: cmake
123
124  get_item_key(<item> <key_var>)
125
126Given ``<item>`` file name, generate ``<key_var>`` key that should be unique
127considering the set of libraries that need copying or fixing up to
128make a bundle standalone.  This is essentially the file name including
129extension with ``.`` replaced by ``_``
130
131This key is used as a prefix for CMake variables so that we can
132associate a set of variables with a given item based on its key.
133
134.. code-block:: cmake
135
136  clear_bundle_keys(<keys_var>)
137
138Loop over the ``<keys_var>`` list of keys, clearing all the variables
139associated with each key.  After the loop, clear the list of keys itself.
140
141Caller of ``get_bundle_keys`` should call ``clear_bundle_keys`` when done with
142list of keys.
143
144.. code-block:: cmake
145
146  set_bundle_key_values(<keys_var> <context> <item> <exepath> <dirs>
147                        <copyflag> [<rpaths>])
148
149Add ``<keys_var>`` key to the list (if necessary) for the given item.
150If added, also set all the variables associated with that key.
151
152.. code-block:: cmake
153
154  get_bundle_keys(<app> <libs> <dirs> <keys_var>)
155
156Loop over all the executable and library files within ``<app>`` bundle (and
157given as extra ``<libs>``) and accumulate a list of keys representing
158them.  Set values associated with each key such that we can loop over
159all of them and copy prerequisite libs into the bundle and then do
160appropriate ``install_name_tool`` fixups.
161
162As an optional parameter (``IGNORE_ITEM``) a list of file names can be passed,
163which are then ignored
164(e.g. ``IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"``)
165
166.. code-block:: cmake
167
168  copy_resolved_item_into_bundle(<resolved_item> <resolved_embedded_item>)
169
170Copy a resolved item into the bundle if necessary.
171Copy is not necessary, if the ``<resolved_item>`` is "the same as" the
172``<resolved_embedded_item>``.
173
174.. code-block:: cmake
175
176  copy_resolved_framework_into_bundle(<resolved_item> <resolved_embedded_item>)
177
178Copy a resolved framework into the bundle if necessary.
179Copy is not necessary, if the ``<resolved_item>`` is "the same as" the
180``<resolved_embedded_item>``.
181
182By default, ``BU_COPY_FULL_FRAMEWORK_CONTENTS`` is not set.  If you want
183full frameworks embedded in your bundles, set
184``BU_COPY_FULL_FRAMEWORK_CONTENTS`` to ``ON`` before calling fixup_bundle.  By
185default, ``COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE`` copies the framework
186dylib itself plus the framework ``Resources`` directory.
187
188.. code-block:: cmake
189
190  fixup_bundle_item(<resolved_embedded_item> <exepath> <dirs>)
191
192Get the direct/non-system prerequisites of the ``<resolved_embedded_item>``.
193For each prerequisite, change the way it is referenced to the value of
194the ``_EMBEDDED_ITEM`` keyed variable for that prerequisite.  (Most likely
195changing to an ``@executable_path`` style reference.)
196
197This function requires that the ``<resolved_embedded_item>`` be ``inside``
198the bundle already.  In other words, if you pass plugins to ``fixup_bundle``
199as the libs parameter, you should install them or copy them into the
200bundle before calling ``fixup_bundle``.  The ``libs`` parameter is a list of
201libraries that must be fixed up, but that cannot be determined by
202otool output analysis.  (i.e., ``plugins``)
203
204Also, change the id of the item being fixed up to its own
205``_EMBEDDED_ITEM`` value.
206
207Accumulate changes in a local variable and make *one* call to
208``install_name_tool`` at the end of the function with all the changes at
209once.
210
211If the ``BU_CHMOD_BUNDLE_ITEMS`` variable is set then bundle items will be
212marked writable before ``install_name_tool`` tries to change them.
213
214.. code-block:: cmake
215
216  verify_bundle_prerequisites(<bundle> <result_var> <info_var>)
217
218Verifies that the sum of all prerequisites of all files inside the
219bundle are contained within the bundle or are ``system`` libraries,
220presumed to exist everywhere.
221
222As an optional parameter (``IGNORE_ITEM``) a list of file names can be passed,
223which are then ignored
224(e.g. ``IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"``)
225
226.. code-block:: cmake
227
228  verify_bundle_symlinks(<bundle> <result_var> <info_var>)
229
230Verifies that any symlinks found in the ``<bundle>`` bundle point to other files
231that are already also in the bundle...  Anything that points to an
232external file causes this function to fail the verification.
233#]=======================================================================]
234
235function(_warn_cmp0080)
236  cmake_policy(GET_WARNING CMP0080 _cmp0080_warning)
237  message(AUTHOR_WARNING "${_cmp0080_warning}\n")
238endfunction()
239
240# Do not include this module at configure time!
241if(DEFINED CMAKE_GENERATOR)
242  cmake_policy(GET CMP0080 _BundleUtilities_CMP0080)
243  if(_BundleUtilities_CMP0080 STREQUAL "NEW")
244    message(FATAL_ERROR "BundleUtilities cannot be included at configure time!")
245  elseif(NOT _BundleUtilities_CMP0080 STREQUAL "OLD" AND NOT _CMP0080_SUPPRESS_WARNING)
246    _warn_cmp0080()
247  endif()
248endif()
249
250cmake_policy(PUSH)
251cmake_policy(SET CMP0057 NEW) # if IN_LIST
252
253# The functions defined in this file depend on the get_prerequisites function
254# (and possibly others) found in:
255#
256include("${CMAKE_CURRENT_LIST_DIR}/GetPrerequisites.cmake")
257
258
259function(get_bundle_main_executable bundle result_var)
260  set(result "error: '${bundle}/Contents/Info.plist' file does not exist")
261
262  if(EXISTS "${bundle}/Contents/Info.plist")
263    set(result "error: no CFBundleExecutable in '${bundle}/Contents/Info.plist' file")
264    set(line_is_main_executable 0)
265    set(bundle_executable "")
266
267    # Read Info.plist as a list of lines:
268    #
269    set(eol_char "E")
270    file(READ "${bundle}/Contents/Info.plist" info_plist)
271    string(REPLACE ";" "\\;" info_plist "${info_plist}")
272    string(REPLACE "\n" "${eol_char};" info_plist "${info_plist}")
273    string(REPLACE "\r" "${eol_char};" info_plist "${info_plist}")
274
275    # Scan the lines for "<key>CFBundleExecutable</key>" - the line after that
276    # is the name of the main executable.
277    #
278    foreach(line ${info_plist})
279      if(line_is_main_executable)
280        string(REGEX REPLACE "^.*<string>(.*)</string>.*$" "\\1" bundle_executable "${line}")
281        break()
282      endif()
283
284      if(line MATCHES "<key>CFBundleExecutable</key>")
285        set(line_is_main_executable 1)
286      endif()
287    endforeach()
288
289    if(NOT bundle_executable STREQUAL "")
290      if(EXISTS "${bundle}/Contents/MacOS/${bundle_executable}")
291        set(result "${bundle}/Contents/MacOS/${bundle_executable}")
292      else()
293
294        # Ultimate goal:
295        # If not in "Contents/MacOS" then scan the bundle for matching files. If
296        # there is only one executable file that matches, then use it, otherwise
297        # it's an error...
298        #
299        #file(GLOB_RECURSE file_list "${bundle}/${bundle_executable}")
300
301        # But for now, pragmatically, it's an error. Expect the main executable
302        # for the bundle to be in Contents/MacOS, it's an error if it's not:
303        #
304        set(result "error: '${bundle}/Contents/MacOS/${bundle_executable}' does not exist")
305      endif()
306    endif()
307  else()
308    #
309    # More inclusive technique... (This one would work on Windows and Linux
310    # too, if a developer followed the typical Mac bundle naming convention...)
311    #
312    # If there is no Info.plist file, try to find an executable with the same
313    # base name as the .app directory:
314    #
315  endif()
316
317  set(${result_var} "${result}" PARENT_SCOPE)
318endfunction()
319
320
321function(get_dotapp_dir exe dotapp_dir_var)
322  set(s "${exe}")
323
324  if(s MATCHES "/.*\\.app/")
325    # If there is a ".app" parent directory,
326    # ascend until we hit it:
327    #   (typical of a Mac bundle executable)
328    #
329    set(done 0)
330    while(NOT ${done})
331      get_filename_component(snamewe "${s}" NAME_WE)
332      get_filename_component(sname "${s}" NAME)
333      get_filename_component(sdir "${s}" PATH)
334      set(s "${sdir}")
335      if(sname MATCHES "\\.app$")
336        set(done 1)
337        set(dotapp_dir "${sdir}/${sname}")
338      endif()
339    endwhile()
340  else()
341    # Otherwise use a directory containing the exe
342    #   (typical of a non-bundle executable on Mac, Windows or Linux)
343    #
344    is_file_executable("${s}" is_executable)
345    if(is_executable)
346      get_filename_component(sdir "${s}" PATH)
347      set(dotapp_dir "${sdir}")
348    else()
349      set(dotapp_dir "${s}")
350    endif()
351  endif()
352
353
354  set(${dotapp_dir_var} "${dotapp_dir}" PARENT_SCOPE)
355endfunction()
356
357
358function(get_bundle_and_executable app bundle_var executable_var valid_var)
359  set(valid 0)
360
361  if(EXISTS "${app}")
362    # Is it a directory ending in .app?
363    if(IS_DIRECTORY "${app}")
364      if(app MATCHES "\\.app$")
365        get_bundle_main_executable("${app}" executable)
366        if(EXISTS "${app}" AND EXISTS "${executable}")
367          set(${bundle_var} "${app}" PARENT_SCOPE)
368          set(${executable_var} "${executable}" PARENT_SCOPE)
369          set(valid 1)
370          #message(STATUS "info: handled .app directory case...")
371        else()
372          message(STATUS "warning: *NOT* handled - .app directory case...")
373        endif()
374      else()
375        message(STATUS "warning: *NOT* handled - directory but not .app case...")
376      endif()
377    else()
378      # Is it an executable file?
379      is_file_executable("${app}" is_executable)
380      if(is_executable)
381        get_dotapp_dir("${app}" dotapp_dir)
382        if(EXISTS "${dotapp_dir}")
383          set(${bundle_var} "${dotapp_dir}" PARENT_SCOPE)
384          set(${executable_var} "${app}" PARENT_SCOPE)
385          set(valid 1)
386          #message(STATUS "info: handled executable file in .app dir case...")
387        else()
388          get_filename_component(app_dir "${app}" PATH)
389          set(${bundle_var} "${app_dir}" PARENT_SCOPE)
390          set(${executable_var} "${app}" PARENT_SCOPE)
391          set(valid 1)
392          #message(STATUS "info: handled executable file in any dir case...")
393        endif()
394      else()
395        message(STATUS "warning: *NOT* handled - not .app dir, not executable file...")
396      endif()
397    endif()
398  else()
399    message(STATUS "warning: *NOT* handled - directory/file does not exist...")
400  endif()
401
402  if(NOT valid)
403    set(${bundle_var} "error: not a bundle" PARENT_SCOPE)
404    set(${executable_var} "error: not a bundle" PARENT_SCOPE)
405  endif()
406
407  set(${valid_var} ${valid} PARENT_SCOPE)
408endfunction()
409
410
411function(get_bundle_all_executables bundle exes_var)
412  set(exes "")
413
414  if(UNIX)
415    find_program(find_cmd "find")
416    mark_as_advanced(find_cmd)
417  endif()
418
419  # find command is much quicker than checking every file one by one on Unix
420  # which can take long time for large bundles, and since anyway we expect
421  # executable to have execute flag set we can narrow the list much quicker.
422  if(find_cmd)
423    execute_process(COMMAND "${find_cmd}" "${bundle}"
424      -type f \( -perm -0100 -o -perm -0010 -o -perm -0001 \)
425      OUTPUT_VARIABLE file_list
426      OUTPUT_STRIP_TRAILING_WHITESPACE
427      )
428    string(REPLACE "\n" ";" file_list "${file_list}")
429  else()
430    file(GLOB_RECURSE file_list "${bundle}/*")
431  endif()
432
433  foreach(f ${file_list})
434    is_file_executable("${f}" is_executable)
435    if(is_executable)
436      set(exes ${exes} "${f}")
437    endif()
438  endforeach()
439
440  set(${exes_var} "${exes}" PARENT_SCOPE)
441endfunction()
442
443
444function(get_item_rpaths item rpaths_var)
445  if(APPLE)
446    find_program(otool_cmd "otool")
447    mark_as_advanced(otool_cmd)
448  endif()
449
450  if(otool_cmd)
451    execute_process(
452      COMMAND "${otool_cmd}" -l "${item}"
453      OUTPUT_VARIABLE load_cmds_ov
454      RESULT_VARIABLE otool_rv
455      ERROR_VARIABLE otool_ev
456      )
457    if(NOT otool_rv STREQUAL "0")
458      message(FATAL_ERROR "otool -l failed: ${otool_rv}\n${otool_ev}")
459    endif()
460    string(REGEX REPLACE "[^\n]+cmd LC_RPATH\n[^\n]+\n[^\n]+path ([^\n]+) \\(offset[^\n]+\n" "rpath \\1\n" load_cmds_ov "${load_cmds_ov}")
461    string(REGEX MATCHALL "rpath [^\n]+" load_cmds_ov "${load_cmds_ov}")
462    string(REGEX REPLACE "rpath " "" load_cmds_ov "${load_cmds_ov}")
463    if(load_cmds_ov)
464      foreach(rpath ${load_cmds_ov})
465        gp_append_unique(${rpaths_var} "${rpath}")
466      endforeach()
467    endif()
468  endif()
469
470  if(UNIX AND NOT APPLE)
471    file(READ_ELF ${item} RPATH rpath_var RUNPATH runpath_var CAPTURE_ERROR error_var)
472    get_filename_component(item_dir ${item} DIRECTORY)
473    foreach(rpath ${rpath_var} ${runpath_var})
474      # Substitute $ORIGIN with the exepath and add to the found rpaths
475      string(REPLACE "$ORIGIN" "${item_dir}" rpath "${rpath}")
476      gp_append_unique(${rpaths_var} "${rpath}")
477    endforeach()
478  endif()
479
480  set(${rpaths_var} ${${rpaths_var}} PARENT_SCOPE)
481endfunction()
482
483
484function(get_item_key item key_var)
485  get_filename_component(item_name "${item}" NAME)
486  if(WIN32)
487    string(TOLOWER "${item_name}" item_name)
488  endif()
489  string(REPLACE "." "_" ${key_var} "${item_name}")
490  set(${key_var} ${${key_var}} PARENT_SCOPE)
491endfunction()
492
493
494function(clear_bundle_keys keys_var)
495  foreach(key ${${keys_var}})
496    set(${key}_ITEM PARENT_SCOPE)
497    set(${key}_RESOLVED_ITEM PARENT_SCOPE)
498    set(${key}_DEFAULT_EMBEDDED_PATH PARENT_SCOPE)
499    set(${key}_EMBEDDED_ITEM PARENT_SCOPE)
500    set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE)
501    set(${key}_COPYFLAG PARENT_SCOPE)
502    set(${key}_RPATHS PARENT_SCOPE)
503  endforeach()
504  set(${keys_var} PARENT_SCOPE)
505endfunction()
506
507
508function(set_bundle_key_values keys_var context item exepath dirs copyflag)
509  if(ARGC GREATER 6)
510    set(rpaths "${ARGV6}")
511  else()
512    set(rpaths "")
513  endif()
514  get_filename_component(item_name "${item}" NAME)
515
516  get_item_key("${item}" key)
517
518  list(LENGTH ${keys_var} length_before)
519  gp_append_unique(${keys_var} "${key}")
520  list(LENGTH ${keys_var} length_after)
521
522  if(NOT length_before EQUAL length_after)
523    gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}")
524
525    gp_item_default_embedded_path("${item}" default_embedded_path)
526
527    get_item_rpaths("${resolved_item}" item_rpaths)
528
529    if((NOT item MATCHES "\\.dylib$") AND (item MATCHES "[^/]+\\.framework/"))
530      # For frameworks, construct the name under the embedded path from the
531      # opening "${item_name}.framework/" to the closing "/${item_name}":
532      #
533      string(REGEX REPLACE "^.*(${item_name}.framework/.*/?${item_name}).*$" "${default_embedded_path}/\\1" embedded_item "${item}")
534    else()
535      # For other items, just use the same name as the original, but in the
536      # embedded path:
537      #
538      set(embedded_item "${default_embedded_path}/${item_name}")
539    endif()
540
541    # Replace @executable_path and resolve ".." references:
542    #
543    string(REPLACE "@executable_path" "${exepath}" resolved_embedded_item "${embedded_item}")
544    get_filename_component(resolved_embedded_item "${resolved_embedded_item}" ABSOLUTE)
545
546    # *But* -- if we are not copying, then force resolved_embedded_item to be
547    # the same as resolved_item. In the case of multiple executables in the
548    # original bundle, using the default_embedded_path results in looking for
549    # the resolved executable next to the main bundle executable. This is here
550    # so that exes in the other sibling directories (like "bin") get fixed up
551    # properly...
552    #
553    if(NOT copyflag)
554      set(resolved_embedded_item "${resolved_item}")
555    endif()
556
557    set(${keys_var} ${${keys_var}} PARENT_SCOPE)
558    set(${key}_ITEM "${item}" PARENT_SCOPE)
559    set(${key}_RESOLVED_ITEM "${resolved_item}" PARENT_SCOPE)
560    set(${key}_DEFAULT_EMBEDDED_PATH "${default_embedded_path}" PARENT_SCOPE)
561    set(${key}_EMBEDDED_ITEM "${embedded_item}" PARENT_SCOPE)
562    set(${key}_RESOLVED_EMBEDDED_ITEM "${resolved_embedded_item}" PARENT_SCOPE)
563    set(${key}_COPYFLAG "${copyflag}" PARENT_SCOPE)
564    set(${key}_RPATHS "${item_rpaths}" PARENT_SCOPE)
565    set(${key}_RDEP_RPATHS "${rpaths}" PARENT_SCOPE)
566  else()
567    #message("warning: item key '${key}' already in the list, subsequent references assumed identical to first")
568  endif()
569endfunction()
570
571
572function(get_bundle_keys app libs dirs keys_var)
573  set(${keys_var} PARENT_SCOPE)
574
575  set(options)
576  set(oneValueArgs)
577  set(multiValueArgs IGNORE_ITEM)
578  cmake_parse_arguments(CFG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
579
580  get_bundle_and_executable("${app}" bundle executable valid)
581  if(valid)
582    # Always use the exepath of the main bundle executable for @executable_path
583    # replacements:
584    #
585    get_filename_component(exepath "${executable}" PATH)
586
587    # But do fixups on all executables in the bundle:
588    #
589    get_bundle_all_executables("${bundle}" exes)
590
591    # Set keys for main executable first:
592    #
593    set_bundle_key_values(${keys_var} "${executable}" "${executable}" "${exepath}" "${dirs}" 0)
594
595    # Get rpaths specified by main executable:
596    #
597    get_item_key("${executable}" executable_key)
598    set(main_rpaths "${${executable_key}_RPATHS}")
599
600    # For each extra lib, accumulate a key as well and then also accumulate
601    # any of its prerequisites. (Extra libs are typically dynamically loaded
602    # plugins: libraries that are prerequisites for full runtime functionality
603    # but that do not show up in otool -L output...)
604    #
605    foreach(lib ${libs})
606      set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0 "${main_rpaths}")
607
608      set(prereqs "")
609      get_filename_component(prereq_filename ${lib} NAME)
610
611      if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM)
612        get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}" "${main_rpaths}")
613        foreach(pr ${prereqs})
614          set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1 "${main_rpaths}")
615        endforeach()
616      else()
617        message(STATUS "Ignoring file: ${prereq_filename}")
618      endif()
619    endforeach()
620
621    # For each executable found in the bundle, accumulate keys as we go.
622    # The list of keys should be complete when all prerequisites of all
623    # binaries in the bundle have been analyzed.
624    #
625    foreach(exe ${exes})
626      # Main executable is scanned first above:
627      #
628      if(NOT exe STREQUAL executable)
629        # Add the exe itself to the keys:
630        #
631        set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0 "${main_rpaths}")
632
633        # Get rpaths specified by executable:
634        #
635        get_item_key("${exe}" exe_key)
636        set(exe_rpaths "${main_rpaths}" "${${exe_key}_RPATHS}")
637      else()
638        set(exe_rpaths "${main_rpaths}")
639      endif()
640
641      # Add each prerequisite to the keys:
642      #
643      set(prereqs "")
644      get_filename_component(prereq_filename ${exe} NAME)
645
646      if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM)
647        get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}" "${exe_rpaths}")
648        foreach(pr ${prereqs})
649          set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1 "${exe_rpaths}")
650        endforeach()
651      else()
652        message(STATUS "Ignoring file: ${prereq_filename}")
653      endif()
654    endforeach()
655
656    # preserve library symlink structure
657    foreach(key ${${keys_var}})
658      if("${${key}_COPYFLAG}" STREQUAL "1")
659        if(IS_SYMLINK "${${key}_RESOLVED_ITEM}")
660          get_filename_component(target "${${key}_RESOLVED_ITEM}" REALPATH)
661          set_bundle_key_values(${keys_var} "${exe}" "${target}" "${exepath}" "${dirs}" 1 "${exe_rpaths}")
662          get_item_key("${target}" targetkey)
663
664          if(WIN32)
665            # ignore case on Windows
666            string(TOLOWER "${${key}_RESOLVED_ITEM}" resolved_item_compare)
667            string(TOLOWER "${${targetkey}_RESOLVED_EMBEDDED_ITEM}" resolved_embedded_item_compare)
668          else()
669            set(resolved_item_compare "${${key}_RESOLVED_ITEM}")
670            set(resolved_embedded_item_compare "${${targetkey}_RESOLVED_EMBEDDED_ITEM}")
671          endif()
672          get_filename_component(resolved_item_compare "${resolved_item_compare}" NAME)
673          get_filename_component(resolved_embedded_item_compare "${resolved_embedded_item_compare}" NAME)
674
675          if(NOT resolved_item_compare STREQUAL resolved_embedded_item_compare)
676            set(${key}_COPYFLAG "2")
677            set(${key}_RESOLVED_ITEM "${${targetkey}_RESOLVED_EMBEDDED_ITEM}")
678          endif()
679
680        endif()
681      endif()
682    endforeach()
683    # Propagate values to caller's scope:
684    #
685    set(${keys_var} ${${keys_var}} PARENT_SCOPE)
686    foreach(key ${${keys_var}})
687      set(${key}_ITEM "${${key}_ITEM}" PARENT_SCOPE)
688      set(${key}_RESOLVED_ITEM "${${key}_RESOLVED_ITEM}" PARENT_SCOPE)
689      set(${key}_DEFAULT_EMBEDDED_PATH "${${key}_DEFAULT_EMBEDDED_PATH}" PARENT_SCOPE)
690      set(${key}_EMBEDDED_ITEM "${${key}_EMBEDDED_ITEM}" PARENT_SCOPE)
691      set(${key}_RESOLVED_EMBEDDED_ITEM "${${key}_RESOLVED_EMBEDDED_ITEM}" PARENT_SCOPE)
692      set(${key}_COPYFLAG "${${key}_COPYFLAG}" PARENT_SCOPE)
693      set(${key}_RPATHS "${${key}_RPATHS}" PARENT_SCOPE)
694      set(${key}_RDEP_RPATHS "${${key}_RDEP_RPATHS}" PARENT_SCOPE)
695    endforeach()
696  endif()
697endfunction()
698
699function(link_resolved_item_into_bundle resolved_item resolved_embedded_item)
700  if(WIN32)
701    # ignore case on Windows
702    string(TOLOWER "${resolved_item}" resolved_item_compare)
703    string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
704  else()
705    set(resolved_item_compare "${resolved_item}")
706    set(resolved_embedded_item_compare "${resolved_embedded_item}")
707  endif()
708
709  if(resolved_item_compare STREQUAL resolved_embedded_item_compare)
710    message(STATUS "warning: resolved_item == resolved_embedded_item - not linking...")
711  else()
712    get_filename_component(target_dir "${resolved_embedded_item}" DIRECTORY)
713    file(RELATIVE_PATH symlink_target "${target_dir}" "${resolved_item}")
714    if (NOT EXISTS "${target_dir}")
715      file(MAKE_DIRECTORY "${target_dir}")
716    endif()
717    execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${symlink_target}" "${resolved_embedded_item}")
718  endif()
719endfunction()
720
721function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
722  if(WIN32)
723    # ignore case on Windows
724    string(TOLOWER "${resolved_item}" resolved_item_compare)
725    string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
726  else()
727    set(resolved_item_compare "${resolved_item}")
728    set(resolved_embedded_item_compare "${resolved_embedded_item}")
729  endif()
730
731  if(resolved_item_compare STREQUAL resolved_embedded_item_compare)
732    message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
733  else()
734    #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
735    execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
736    if(UNIX AND NOT APPLE)
737      file(RPATH_REMOVE FILE "${resolved_embedded_item}")
738    endif()
739  endif()
740
741endfunction()
742
743
744function(copy_resolved_framework_into_bundle resolved_item resolved_embedded_item)
745  if(WIN32)
746    # ignore case on Windows
747    string(TOLOWER "${resolved_item}" resolved_item_compare)
748    string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
749  else()
750    set(resolved_item_compare "${resolved_item}")
751    set(resolved_embedded_item_compare "${resolved_embedded_item}")
752  endif()
753
754  if(resolved_item_compare STREQUAL resolved_embedded_item_compare)
755    message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
756  else()
757    if(BU_COPY_FULL_FRAMEWORK_CONTENTS)
758      # Full Framework (everything):
759      get_filename_component(resolved_dir "${resolved_item}" PATH)
760      get_filename_component(resolved_dir "${resolved_dir}/../.." ABSOLUTE)
761      get_filename_component(resolved_embedded_dir "${resolved_embedded_item}" PATH)
762      get_filename_component(resolved_embedded_dir "${resolved_embedded_dir}/../.." ABSOLUTE)
763      #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_dir}' '${resolved_embedded_dir}'")
764      execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_dir}" "${resolved_embedded_dir}")
765    else()
766      # Framework lib itself:
767      #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
768      execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
769
770      # Plus Resources, if they exist:
771      string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}")
772      string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}")
773      if(EXISTS "${resolved_resources}")
774        #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_resources}' '${resolved_embedded_resources}'")
775        execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_resources}" "${resolved_embedded_resources}")
776      endif()
777
778      # Some frameworks e.g. Qt put Info.plist in wrong place, so when it is
779      # missing in resources, copy it from other well known incorrect locations:
780      if(NOT EXISTS "${resolved_resources}/Info.plist")
781        # Check for Contents/Info.plist in framework root (older Qt SDK):
782        string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Contents/Info.plist" resolved_info_plist "${resolved_item}")
783        string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources/Info.plist" resolved_embedded_info_plist "${resolved_embedded_item}")
784        if(EXISTS "${resolved_info_plist}")
785          #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_info_plist}' '${resolved_embedded_info_plist}'")
786          execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_info_plist}" "${resolved_embedded_info_plist}")
787        endif()
788      endif()
789
790      # Check if framework is versioned and fix it layout
791      string(REGEX REPLACE "^.*/([^/]+)/[^/]+$" "\\1" resolved_embedded_version "${resolved_embedded_item}")
792      string(REGEX REPLACE "^(.*)/[^/]+/[^/]+$" "\\1" resolved_embedded_versions "${resolved_embedded_item}")
793      string(REGEX REPLACE "^.*/([^/]+)/[^/]+/[^/]+$" "\\1" resolved_embedded_versions_basename "${resolved_embedded_item}")
794      if(resolved_embedded_versions_basename STREQUAL "Versions")
795        # Ensure Current symlink points to the framework version
796        if(NOT EXISTS "${resolved_embedded_versions}/Current")
797          execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${resolved_embedded_version}" "${resolved_embedded_versions}/Current")
798        endif()
799        # Restore symlinks in framework root pointing to current framework
800        # binary and resources:
801        string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1" resolved_embedded_root "${resolved_embedded_item}")
802        string(REGEX REPLACE "^.*/([^/]+)$" "\\1" resolved_embedded_item_basename "${resolved_embedded_item}")
803        if(NOT EXISTS "${resolved_embedded_root}/${resolved_embedded_item_basename}")
804          execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "Versions/Current/${resolved_embedded_item_basename}" "${resolved_embedded_root}/${resolved_embedded_item_basename}")
805        endif()
806        if(NOT EXISTS "${resolved_embedded_root}/Resources")
807          execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "Versions/Current/Resources" "${resolved_embedded_root}/Resources")
808        endif()
809      endif()
810    endif()
811    if(UNIX AND NOT APPLE)
812      file(RPATH_REMOVE FILE "${resolved_embedded_item}")
813    endif()
814  endif()
815
816endfunction()
817
818
819function(fixup_bundle_item resolved_embedded_item exepath dirs)
820  # This item's key is "ikey":
821  #
822  get_item_key("${resolved_embedded_item}" ikey)
823
824  # Ensure the item is "inside the .app bundle" -- it should not be fixed up if
825  # it is not in the .app bundle... Otherwise, we'll modify files in the build
826  # tree, or in other varied locations around the file system, with our call to
827  # install_name_tool. Make sure that doesn't happen here:
828  #
829  get_dotapp_dir("${exepath}" exe_dotapp_dir)
830  string(LENGTH "${exe_dotapp_dir}/" exe_dotapp_dir_length)
831  string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length)
832  set(path_too_short 0)
833  set(is_embedded 0)
834  if(resolved_embedded_item_length LESS exe_dotapp_dir_length)
835    set(path_too_short 1)
836  endif()
837  if(NOT path_too_short)
838    string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring)
839    if("${exe_dotapp_dir}/" STREQUAL item_substring)
840      set(is_embedded 1)
841    endif()
842  endif()
843  if(NOT is_embedded)
844    message("  exe_dotapp_dir/='${exe_dotapp_dir}/'")
845    message("  item_substring='${item_substring}'")
846    message("  resolved_embedded_item='${resolved_embedded_item}'")
847    message("")
848    message("Install or copy the item into the bundle before calling fixup_bundle.")
849    message("Or maybe there's a typo or incorrect path in one of the args to fixup_bundle?")
850    message("")
851    message(FATAL_ERROR "cannot fixup an item that is not in the bundle...")
852  endif()
853
854  set(rpaths "${${ikey}_RPATHS}" "${${ikey}_RDEP_RPATHS}")
855
856  set(prereqs "")
857  get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}" "${rpaths}")
858
859  set(changes "")
860
861  foreach(pr ${prereqs})
862    # Each referenced item's key is "rkey" in the loop:
863    #
864    get_item_key("${pr}" rkey)
865
866    if(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
867      set(changes ${changes} "-change" "${pr}" "${${rkey}_EMBEDDED_ITEM}")
868    else()
869      message("warning: unexpected reference to '${pr}'")
870    endif()
871  endforeach()
872
873  if(BU_CHMOD_BUNDLE_ITEMS)
874    execute_process(COMMAND chmod u+w "${resolved_embedded_item}")
875  endif()
876
877  # CMAKE_INSTALL_NAME_TOOL may not be set if executed in script mode
878  # Duplicated from CMakeFindBinUtils.cmake
879  find_program(CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
880
881  # Only if install_name_tool supports -delete_rpath:
882  #
883  execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL}
884    OUTPUT_VARIABLE install_name_tool_usage
885    ERROR_VARIABLE  install_name_tool_usage
886    )
887  if(install_name_tool_usage MATCHES ".*-delete_rpath.*")
888    foreach(rpath ${${ikey}_RPATHS})
889      set(changes ${changes} -delete_rpath "${rpath}")
890    endforeach()
891  endif()
892
893  if(${ikey}_EMBEDDED_ITEM)
894    set(changes ${changes} -id "${${ikey}_EMBEDDED_ITEM}")
895  endif()
896
897  # Change this item's id and all of its references in one call
898  # to install_name_tool:
899  #
900  if(changes)
901    # Check for a script by extension (.bat,.sh,...) or if the file starts with "#!" (shebang)
902    file(READ ${resolved_embedded_item} file_contents LIMIT 5)
903    if(NOT "${resolved_embedded_item}" MATCHES "\\.(bat|c?sh|bash|ksh|cmd)$" AND
904       NOT file_contents MATCHES "^#!")
905      set(cmd ${CMAKE_INSTALL_NAME_TOOL} ${changes} "${resolved_embedded_item}")
906      execute_process(COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result)
907      if(NOT install_name_tool_result EQUAL 0)
908        string(REPLACE ";" "' '" msg "'${cmd}'")
909        message(FATAL_ERROR "Command failed:\n ${msg}")
910      endif()
911    endif()
912  endif()
913endfunction()
914
915
916function(fixup_bundle app libs dirs)
917  message(STATUS "fixup_bundle")
918  message(STATUS "  app='${app}'")
919  message(STATUS "  libs='${libs}'")
920  message(STATUS "  dirs='${dirs}'")
921
922  set(options)
923  set(oneValueArgs)
924  set(multiValueArgs IGNORE_ITEM)
925  cmake_parse_arguments(CFG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
926
927  message(STATUS "  ignoreItems='${CFG_IGNORE_ITEM}'")
928
929  get_bundle_and_executable("${app}" bundle executable valid)
930  if(valid)
931    get_filename_component(exepath "${executable}" PATH)
932
933    message(STATUS "fixup_bundle: preparing...")
934    get_bundle_keys("${app}" "${libs}" "${dirs}" keys IGNORE_ITEM "${CFG_IGNORE_ITEM}")
935
936    message(STATUS "fixup_bundle: copying...")
937    list(LENGTH keys n)
938    math(EXPR n ${n}*2)
939
940    set(i 0)
941    foreach(key ${keys})
942      math(EXPR i ${i}+1)
943      if("${${key}_COPYFLAG}" STREQUAL "2")
944        message(STATUS "${i}/${n}: linking '${${key}_RESOLVED_ITEM}' -> '${${key}_RESOLVED_EMBEDDED_ITEM}'")
945      elseif(${${key}_COPYFLAG})
946        message(STATUS "${i}/${n}: copying '${${key}_RESOLVED_ITEM}'")
947      else()
948        message(STATUS "${i}/${n}: *NOT* copying '${${key}_RESOLVED_ITEM}'")
949      endif()
950
951      set(show_status 0)
952      if(show_status)
953        message(STATUS "key='${key}'")
954        message(STATUS "item='${${key}_ITEM}'")
955        message(STATUS "resolved_item='${${key}_RESOLVED_ITEM}'")
956        message(STATUS "default_embedded_path='${${key}_DEFAULT_EMBEDDED_PATH}'")
957        message(STATUS "embedded_item='${${key}_EMBEDDED_ITEM}'")
958        message(STATUS "resolved_embedded_item='${${key}_RESOLVED_EMBEDDED_ITEM}'")
959        message(STATUS "copyflag='${${key}_COPYFLAG}'")
960        message(STATUS "")
961      endif()
962
963      if("${${key}_COPYFLAG}" STREQUAL "2")
964        link_resolved_item_into_bundle("${${key}_RESOLVED_ITEM}"
965          "${${key}_RESOLVED_EMBEDDED_ITEM}")
966      elseif(${${key}_COPYFLAG})
967        set(item "${${key}_ITEM}")
968        if(item MATCHES "[^/]+\\.framework/")
969          copy_resolved_framework_into_bundle("${${key}_RESOLVED_ITEM}"
970            "${${key}_RESOLVED_EMBEDDED_ITEM}")
971        else()
972          copy_resolved_item_into_bundle("${${key}_RESOLVED_ITEM}"
973            "${${key}_RESOLVED_EMBEDDED_ITEM}")
974        endif()
975      endif()
976    endforeach()
977
978    message(STATUS "fixup_bundle: fixing...")
979    foreach(key ${keys})
980      math(EXPR i ${i}+1)
981      if(APPLE)
982        message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
983        if(NOT "${${key}_COPYFLAG}" STREQUAL "2")
984          fixup_bundle_item("${${key}_RESOLVED_EMBEDDED_ITEM}" "${exepath}" "${dirs}")
985        endif()
986      else()
987        message(STATUS "${i}/${n}: fix-up not required on this platform '${${key}_RESOLVED_EMBEDDED_ITEM}'")
988      endif()
989    endforeach()
990
991    message(STATUS "fixup_bundle: cleaning up...")
992    clear_bundle_keys(keys)
993
994    message(STATUS "fixup_bundle: verifying...")
995    verify_app("${app}" IGNORE_ITEM "${CFG_IGNORE_ITEM}")
996  else()
997    message(SEND_ERROR "error: fixup_bundle: not a valid bundle")
998  endif()
999
1000  message(STATUS "fixup_bundle: done")
1001endfunction()
1002
1003
1004function(copy_and_fixup_bundle src dst libs dirs)
1005  execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${src}" "${dst}")
1006  fixup_bundle("${dst}" "${libs}" "${dirs}")
1007endfunction()
1008
1009
1010function(verify_bundle_prerequisites bundle result_var info_var)
1011  set(result 1)
1012  set(info "")
1013  set(count 0)
1014
1015  set(options)
1016  set(oneValueArgs)
1017  set(multiValueArgs IGNORE_ITEM)
1018  cmake_parse_arguments(CFG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
1019
1020  get_bundle_main_executable("${bundle}" main_bundle_exe)
1021
1022  get_bundle_all_executables("${bundle}" file_list)
1023  foreach(f ${file_list})
1024      get_filename_component(exepath "${f}" PATH)
1025      math(EXPR count "${count} + 1")
1026
1027      message(STATUS "executable file ${count}: ${f}")
1028
1029      set(prereqs "")
1030      get_filename_component(prereq_filename ${f} NAME)
1031
1032      if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM)
1033        get_item_rpaths(${f} _main_exe_rpaths)
1034        get_prerequisites("${f}" prereqs 1 1 "${exepath}" "${_main_exe_rpaths}")
1035
1036        # On the Mac,
1037        # "embedded" and "system" prerequisites are fine... anything else means
1038        # the bundle's prerequisites are not verified (i.e., the bundle is not
1039        # really "standalone")
1040        #
1041        # On Windows (and others? Linux/Unix/...?)
1042        # "local" and "system" prereqs are fine...
1043        #
1044
1045        set(external_prereqs "")
1046
1047        foreach(p ${prereqs})
1048          set(p_type "")
1049          gp_file_type("${f}" "${p}" p_type)
1050
1051          if(APPLE)
1052            if(NOT p_type STREQUAL "embedded" AND NOT p_type STREQUAL "system")
1053              set(external_prereqs ${external_prereqs} "${p}")
1054            endif()
1055          else()
1056            if(NOT p_type STREQUAL "local" AND NOT p_type STREQUAL "system")
1057              set(external_prereqs ${external_prereqs} "${p}")
1058            endif()
1059          endif()
1060        endforeach()
1061
1062        if(external_prereqs)
1063          # Found non-system/somehow-unacceptable prerequisites:
1064          set(result 0)
1065          set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
1066        endif()
1067      else()
1068        message(STATUS "Ignoring file: ${prereq_filename}")
1069      endif()
1070  endforeach()
1071
1072  if(result)
1073    set(info "Verified ${count} executable files in '${bundle}'")
1074  endif()
1075
1076  set(${result_var} "${result}" PARENT_SCOPE)
1077  set(${info_var} "${info}" PARENT_SCOPE)
1078endfunction()
1079
1080
1081function(verify_bundle_symlinks bundle result_var info_var)
1082  set(result 1)
1083  set(info "")
1084  set(count 0)
1085
1086  # TODO: implement this function for real...
1087  # Right now, it is just a stub that verifies unconditionally...
1088
1089  set(${result_var} "${result}" PARENT_SCOPE)
1090  set(${info_var} "${info}" PARENT_SCOPE)
1091endfunction()
1092
1093
1094function(verify_app app)
1095  set(verified 0)
1096  set(info "")
1097
1098  set(options)
1099  set(oneValueArgs)
1100  set(multiValueArgs IGNORE_ITEM)
1101  cmake_parse_arguments(CFG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
1102
1103  get_bundle_and_executable("${app}" bundle executable valid)
1104
1105  message(STATUS "===========================================================================")
1106  message(STATUS "Analyzing app='${app}'")
1107  message(STATUS "bundle='${bundle}'")
1108  message(STATUS "executable='${executable}'")
1109  message(STATUS "valid='${valid}'")
1110
1111  # Verify that the bundle does not have any "external" prerequisites:
1112  #
1113  verify_bundle_prerequisites("${bundle}" verified info IGNORE_ITEM "${CFG_IGNORE_ITEM}")
1114  message(STATUS "verified='${verified}'")
1115  message(STATUS "info='${info}'")
1116  message(STATUS "")
1117
1118  if(verified)
1119    # Verify that the bundle does not have any symlinks to external files:
1120    #
1121    verify_bundle_symlinks("${bundle}" verified info)
1122    message(STATUS "verified='${verified}'")
1123    message(STATUS "info='${info}'")
1124    message(STATUS "")
1125  endif()
1126
1127  if(NOT verified)
1128    message(FATAL_ERROR "error: verify_app failed")
1129  endif()
1130endfunction()
1131
1132cmake_policy(POP)
1133