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