# BundleUtilities

This module provides utility commands for assembling standalone, bundle-style applications with CMake, such as macOS `.app` bundles or similar directory-based application bundles on other operating systems.

Load this module in a CMake installation with:

    include(BundleUtilities)

Note

Do not use this module at configure time (from `CMakeLists.txt`). Instead, include it and invoke its commands from an `install(CODE)` or `install(SCRIPT)`.

## Commands

This module provides the following commands:

- `fixup_bundle()`

- `copy_and_fixup_bundle()`

- `verify_app()`

- `get_bundle_main_executable()`

- `get_dotapp_dir()`

- `get_bundle_and_executable()`

- `get_bundle_all_executables()`

- `get_item_key()`

- `get_item_rpaths()`

- `clear_bundle_keys()`

- `set_bundle_key_values()`

- `get_bundle_keys()`

- `copy_resolved_item_into_bundle()`

- `copy_resolved_framework_into_bundle()`

- `fixup_bundle_item()`

- `verify_bundle_prerequisites()`

- `verify_bundle_symlinks()`

fixup_bundle

  Prepares a bundle for distribution by fixing up its internal dependencies:

      fixup_bundle(<app> <libs> <dirs> [IGNORE_ITEM <files>...])

  This command modifies the `<app>` bundle in-place to make it self-contained and portable, so that it can be drag-n-drop copied to another machine and run there, assuming all of the system libraries are compatible.

  This command collects all dependencies (keys) for the executables and libraries in the bundle. For each dependency, it copies the required files into the bundle and adjusts them according to their own prerequisites. Once complete, it clears the collected keys and invokes the `verify_app()` command to ensure the final bundle is truly standalone.

  The arguments are:

  `<app>`

    The path to the bundle to fix. This can be an `.app` directory or direct path to an executable.

  `<libs>`

    A list of libraries that must be fixed up, but that cannot be automatically determined by the `otool` output analysis (i.e. `plugins`). If plugins are passed to this command as this parameter, they should be installed or copied into the bundle before calling this command.

  `<dirs>`

    A list of paths where libraries might be found. These paths are searched first when a target without any path info is given. Then standard system locations are also searched: `PATH`, Framework locations, `/usr/lib`, etc.

  `IGNORE_ITEM <files>...`

    Added in version 3.6.

    Optional list of file names to ignore (e.g. `IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"`).

copy_and_fixup_bundle

  Copies the bundle and fixes up the new copied bundle in-place:

      copy_and_fixup_bundle(<src> <dst> <libs> <dirs>)

  This command makes a copy of the bundle `<src>` at location `<dst>` and then fixes up the new copied bundle in-place at `<dst>`.

  The arguments are:

  `<src>`

    The directory of the bundle being copied.

  `<dst>`

    The destination directory of the bundle copy.

  `<libs>`

    A list of libraries that must be fixed up, but that cannot be automatically determined by the `otool` output analysis (i.e. `plugins`). If plugins are passed to this command as this parameter, they should be installed or copied into the bundle before calling this command.

  `<dirs>`

    A list of paths where libraries might be found. These paths are searched first when a target without any path info is given. Then standard system locations are also searched: `PATH`, Framework locations, `/usr/lib`, etc.

verify_app

  Verifies that an application bundle appears valid based on running analysis tools on it:

      verify_app(<app> [IGNORE_ITEM <files>...])

  If the application fails verification, a `message(FATAL_ERROR)` is issued, halting the installation process.

  The arguments are:

  `<app>`

    The path to the application to verify. This can be a `.app` directory or a standalone executable.

  `IGNORE_ITEM <files>...`

    Added in version 3.6.

    Optional list of file names to ignore (e.g. `IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"`).

get_bundle_main_executable

  Retrieves the main executable within a given application bundle:

      get_bundle_main_executable(<bundle> <result-var>)

  The result is stored in a `<result-var>` variable and will contain a full path name of the bundle's main executable file, or an `error:` prefixed string if it could not be determined.

get_dotapp_dir

  Locates the enclosing `.app` directory for the given executable:

      get_dotapp_dir(<exe> <dotapp-dir-var>)

  This command retrieves the nearest parent dir whose name ends with `.app` given the full path to an executable and stores it to the `<dotapp-dir-var>` variable. If there is no such parent dir, then it simply retrieves the directory containing the executable.

  The retrieved directory may or may not exist.

get_bundle_and_executable

  Takes either a `.app` directory name or the name of an executable nested inside a `.app` directory and retrieves the path to the `.app` directory and the path to its main executable:

      get_bundle_and_executable(<app> <bundle-var> <executable-var> <valid-var>)

  The arguments are:

  `<app>`

    The name of the application being processed.

  `<bundle-var>`

    Variable name in which to store the resulting path to the `.app` directory. In case of any error, this variable will contain an error message prefixed with string `error:`.

  `<executable-var>`

    Variable name in which to store the resulting main executable. In case of any error, this variable will contain an error message prefixed with string `error:`.

  `<valid-var>`

    Variable name in which the boolean result is stored whether this command was successful or not.

get_bundle_all_executables

  Gets all executables of a given bundle:

      get_bundle_all_executables(<bundle> <exes-var>)

  This command scans `<bundle>` bundle recursively for all executable files and stores them into a variable `<exes-var>`.

get_item_key

  Generates a unique key for the given item:

      get_item_key(<item> <key-var>)

  Given `<item>` file name, this command generates `<key-var>` key that should be unique considering the set of libraries that need copying or fixing up to make a bundle standalone. This is essentially the file name including extension with `.` replaced by `_`.

  This key is used as a prefix for CMake variables so that a set of variables can be associated with a given item based on its key.

get_item_rpaths

  Gets RPATHS (run-time search paths) for the given item:

      get_item_rpaths(<item> <rpaths-var>)

  This command gets RPATHS of the `<item>` file name and stores them to the variable with provided name `<rpaths-var>`.

clear_bundle_keys

  Clears all variables associated with keys:

      clear_bundle_keys(<keys-var>)

  This command loops over the `<keys-var>` list of keys, clearing all the variables associated with each key. After the loop, it clears the list of keys itself. This command should be called after the `get_bundle_keys()` command, when done working with a list of keys.

set_bundle_key_values

  Adds a key to the list of keys:

      set_bundle_key_values(
        <keys-var>
        <context>
        <item>
        <exepath>
        <dirs>
        <copyflag>
        [<rpaths>]
      )

  This command adds the `<keys-var>` key to the list (if necessary) for the given item. If added, also set all the variables associated with that key.

  The arguments are:

  `<keys-var>`

    Variable name holding the name of the key to be added to the list for the given item.

  `<context>`

    The path to the top level loading path used for `@loader_path` replacement on Apple operating systems. When resolving item, `@loader_path` references will be resolved relative to the directory of the given context value (presumably another library).

  `<item>`

    The item for which to add the key.

  `<exepath>`

    The path to the top level executable used for `@executable_path` replacement on Apple operating systems.

  `<dirs>`

    A list of paths where libraries might be found. These paths are searched first when a target without any path info is given. Then standard system locations are also searched: `PATH`, Framework locations, `/usr/lib`, etc.

  `<copyflag>`

    If set to `1` library symlink structure will be preserved.

  `<rpaths>`

    Optional run-time search paths for an executable file or library to help find files.

get_bundle_keys

  Gets bundle keys:

      get_bundle_keys(<app> <libs> <dirs> <keys-var> [IGNORE_ITEM <files>...])

  This command loops over all the executable and library files within `<app>` bundle (and given as extra `<libs>`) and accumulate a list of keys representing them. It sets values associated with each key such that they can be looped over all of them and copies prerequisite libs into the bundle and then does appropriate `install_name_tool` fixups.

  The arguments are:

  `<app>`

    The path to the bundle to fix. This can be an `.app` directory or direct path to an executable.

  `<libs>`

    A list of libraries that must be fixed up, but that cannot be automatically determined by the `otool` output analysis (i.e. `plugins`). If plugins are passed to this command as this parameter, they should be installed or copied into the bundle before calling this command.

  `<dirs>`

    A list of paths where libraries might be found. These paths are searched first when a target without any path info is given. Then standard system locations are also searched: `PATH`, Framework locations, `/usr/lib`, etc.

  `<keys-var>`

    Variable name holding a list of keys that represent all executable and library files within the bundle.

  `IGNORE_ITEM <files>...`

    Added in version 3.6.

    Optional list of file names to ignore (e.g. `IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"`).

copy_resolved_item_into_bundle

  Copies a resolved item into the bundle if necessary:

      copy_resolved_item_into_bundle(<resolved-item> <resolved-embedded-item>)

  Copy is not necessary, if the `<resolved-item>` is "the same as" the `<resolved-embedded-item>`.

copy_resolved_framework_into_bundle

  Copies a resolved framework into the bundle if necessary:

      copy_resolved_framework_into_bundle(<resolved-item> <resolved-embedded-item>)

  Copy is not necessary, if the `<resolved-item>` is "the same as" the `<resolved-embedded-item>`.

  The following variables can be set before invoking this command:

  `BU_COPY_FULL_FRAMEWORK_CONTENTS`

    By default, this variable is not set. If full frameworks should be embedded in the bundles, set this variable to boolean true before calling the `fixup_bundle()` command. By default, this command copies the framework dylib itself plus the framework `Resources` directory.

fixup_bundle_item

  Fixes up bundle item:

      fixup_bundle_item(<resolved-embedded-item> <exepath> <dirs>)

  This command gets the direct/non-system prerequisites of the `<resolved-embedded-item>` and for each prerequisite, it changes the way it is referenced to the value of the `_EMBEDDED_ITEM` keyed variable for that prerequisite. Most likely changing to an `@executable_path` style reference.

  This command requires that the `<resolved-embedded-item>` be `inside` the bundle already. In other words, if plugins are passed to `fixup_bundle()` command as its `<libs>` parameter, they should be installed or copied into the bundle before calling the `fixup_bundle()` command.

  Also, it changes the id of the item being fixed up to its own `_EMBEDDED_ITEM` value.

  Changes are accumulated in a local variable and one call is made to `install_name_tool` command-line tool at the end of this command with all the changes at once.

  The arguments are:

  `<resolved-embedded-item>`

    The bundle item to be fixed up.

  `<exepath>`

    The path to the top level executable used for `@executable_path` replacement on Apple operating systems.

  `<dirs>`

    A list of paths where libraries might be found. These paths are searched first when a target without any path info is given. Then standard system locations are also searched: `PATH`, Framework locations, `/usr/lib`, etc.

  The following variables can be set before invoking this command:

  `BU_CHMOD_BUNDLE_ITEMS`

    If this variable is set to boolean true value then bundle items will be marked writable before `install_name_tool` tool tries to change them.

verify_bundle_prerequisites

  Verifies that the sum of all prerequisites of all files inside the bundle are contained within the bundle or are `system` libraries, presumed to exist everywhere:

      verify_bundle_prerequisites(
        <bundle>
        <result-var>
        <info-var>
        [IGNORE_ITEM <files>...]
      )

  The arguments are:

  `<bundle>`

    Name of the bundle being verified.

  `<result-var>`

    Name of the variable in which to store a boolean result of whether a verification was successful.

  `<info-var>`

    Name of the variable holding any informational messages produced by the verification.

  `IGNORE_ITEM <files>...`

    Added in version 3.6.

    Optional list of file names to ignore (e.g. `IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe"`).

verify_bundle_symlinks

  Verifies that any symlinks found in the specified bundle point to other files that are already also in the bundle:

      verify_bundle_symlinks(<bundle> <result-var> <info-var>)

  Anything that points to an external file causes this command to fail the verification.

  The arguments are:

  `<bundle>`

    Name of the bundle being verified.

  `<result-var>`

    Name of the variable in which to store a boolean result of whether a verification was successful.

  `<info-var>`

    Name of the variable holding any informational messages produced by the verification.

## Examples

Using this module inside the installation code that is executed at the installation phase:

`CMakeLists.txt`

    # ...

    install(CODE "
      include(BundleUtilities)
      set(BU_CHMOD_BUNDLE_ITEMS TRUE)
      fixup_bundle(
        \"${fixup_exe}\"
        \"${plugins}\"
        \"${bin_dir};${library_dir};${binary_dir}\"
      )
    ")