You can package a desktop application built in the Desktop Apps guides (starting with [Building a Desktop App](/docs/2.0/building-a-desktop-app)) into a standalone release that runs on machines without the SDK.

The release is a standalone folder on Windows and Linux— on macOS, it is an app bundle.

Packaging assumes the project links with the CMake package from [Linking to the Library](/docs/2.0/linking-to-the-library).

## Distribute Runtime Files

An installed application needs runtime files placed alongside the executable— it cannot run from the compiled binary alone.

### File Search Paths

The default file system loads `file:///` URLs from the `assets/` directory. `Settings::file_system_path` sets this root path and defaults to `./assets/`.

On Windows and Linux, the path is relative to the folder containing the executable. Inside a macOS app bundle, the path is relative to `Contents/Resources/`.

The renderer loads its own runtime data from `assets/resources/`.

### Required Files

Every release build requires the following files and directory layout:

| Item | Windows and Linux | macOS App Bundle | Contents |
| :--- | :--- | :--- | :--- |
| Shared libraries | Beside the executable | `Contents/MacOS/` | The four libraries from the SDK's `bin/` folder (`UltralightCore`, `WebCore`, `Ultralight`, and `AppCore`). |
| SDK resources | `assets/resources/` | `Contents/Resources/assets/resources/` | Runtime data including ICU data (`icudt67l.dat`), certificates (`cacert.pem`), and media controls. |
| Web content | `assets/` | `Contents/Resources/assets/` | HTML, CSS, JavaScript, and image files for the page. |
| Web Inspector (optional) | `assets/inspector/` | `Contents/Resources/assets/inspector/` | Web Inspector front end, included only when shipping developer tools to users. |

> 🚧 Missing runtime resources
>
> If an application runs from the build folder but fails once installed, the `assets/resources/` folder is almost always missing. Without `icudt67l.dat`, `App::Create()` displays an error message box and exits. Without `cacert.pem`, the library logs an error and all HTTPS requests fail.

## Package with CMake Install Rules

Calling `find_package(Ultralight)` defines path variables for the SDK's runtime files:

- `ULTRALIGHT_BINARY_DIR` — shared libraries.
- `ULTRALIGHT_RESOURCES_DIR` — runtime data files.
- `ULTRALIGHT_INSPECTOR_DIR` — Web Inspector front end.

### Windows and Linux Folders

Use CMake `install()` rules to assemble the executable and its runtime files into a standalone folder.

```cmake
install(TARGETS MyApp RUNTIME DESTINATION "MyApp")
install(DIRECTORY "${ULTRALIGHT_BINARY_DIR}/" DESTINATION "MyApp")
install(DIRECTORY "${ULTRALIGHT_RESOURCES_DIR}" DESTINATION "MyApp/assets")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/assets/"
        DESTINATION "MyApp/assets" OPTIONAL)
```

Running `cmake --install` populates the **MyApp** folder— ready to distribute directly or pass to an installer.

While `ultralight_copy_runtime_files()` from [Linking to the Library](/docs/2.0/linking-to-the-library) stages these files in the build folder during development, install rules package them for release.

To ship developer tools to users, add an install rule that copies `ULTRALIGHT_INSPECTOR_DIR` into `MyApp/assets`.

### macOS App Bundles

To target all three platforms in a single `CMakeLists.txt`, split the install rules with an `if (APPLE)` check.

```cmake
if (APPLE)
  install(TARGETS MyApp BUNDLE DESTINATION ".")
  install(DIRECTORY "${ULTRALIGHT_BINARY_DIR}/"
          DESTINATION "MyApp.app/Contents/MacOS")
  install(DIRECTORY "${ULTRALIGHT_RESOURCES_DIR}"
          DESTINATION "MyApp.app/Contents/Resources/assets")
  install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/assets/"
          DESTINATION "MyApp.app/Contents/Resources/assets" OPTIONAL)
else ()
  install(TARGETS MyApp RUNTIME DESTINATION "MyApp")
  install(DIRECTORY "${ULTRALIGHT_BINARY_DIR}/" DESTINATION "MyApp")
  install(DIRECTORY "${ULTRALIGHT_RESOURCES_DIR}" DESTINATION "MyApp/assets")
  install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/assets/"
          DESTINATION "MyApp/assets" OPTIONAL)
endif ()
```

For a complete example showing both layouts, see `samples/cmake/Common.cmake` in the SDK.

> 🚧 Set BUNDLE DESTINATION on macOS
>
> A target configured with `MACOSX_BUNDLE` requires `BUNDLE DESTINATION` in its `install(TARGETS)` rule. Specifying `RUNTIME DESTINATION` alone causes a CMake configuration error on macOS.

## Other Build Systems

Projects built without CMake must copy the runtime dependencies into the directory layouts shown above as a post-build step.

For step-by-step compiler and linker flags, see [Linking to the Library](/docs/2.0/linking-to-the-library).

The executable must be able to locate its shared libraries when launched. Windows searches the folder containing the executable automatically. On macOS, configure the linker to set the runtime search path (`rpath`) to `@executable_path/`. On Linux, set the `rpath` to `$ORIGIN`.

## Configure Platform Settings

Each OS introduces release requirements beyond the build flags and link libraries described in [Linking to the Library](/docs/2.0/linking-to-the-library).

### Windows

Target machines need the Microsoft Visual C++ Redistributable (x64)— an installer can bundle the package and run it during setup.

### macOS

Pass `MACOSX_BUNDLE` to `add_executable()` to create an app bundle, then configure its properties in CMake.

```cmake
set_target_properties(MyApp PROPERTIES
  MACOSX_BUNDLE_GUI_IDENTIFIER "com.acme.starlight"
  MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in")
```

CMake generates a default `Info.plist` for each bundle— you can point `MACOSX_BUNDLE_INFO_PLIST` at a custom template to override it. The SDK provides `samples/cmake/Info.plist.in` as a starting point to configure the bundle identifier, version string, and `NSHighResolutionCapable`.

The bundle identifier set in `MACOSX_BUNDLE_GUI_IDENTIFIER` also names the application's storage directories on macOS.

### Linux

Target machines must meet the Linux system requirements, including the supported glibc version and the X11 and fontconfig libraries needed by `libAppCore.so`. For the required dependencies, see [Linking to the Library](/docs/2.0/linking-to-the-library).

## Storage and Log Locations

Ultralight creates dedicated application directories to store runtime logs, profiler traces, and persistent session data like cookies and databases.

Directory paths are derived from `Settings::developer_name` and `Settings::app_name`, which default to `"MyCompany"` and `"MyApp"`. Set these properties to your company and application names before calling `App::Create()`.

| Platform | Session Data | Logs and Traces |
| :--- | :--- | :--- |
| Windows | `%APPDATA%\<developer_name>\<app_name>` | Same folder as session data |
| macOS | `~/Library/Application Support/<name>` | `~/Library/Caches/<name>` |
| Linux | `$XDG_CACHE_HOME/com.<developer_name>.<app_name>` | Same folder as session data |

On macOS, `<name>` is the bundle identifier, or `com.<developer_name>.<app_name>` when the bundle has none.

On Linux, the directory falls back to `~/.cache/com.<developer_name>.<app_name>` if `$XDG_CACHE_HOME` is unset.

To configure custom storage paths, see [Sessions and Site Data](/docs/2.0/sessions-and-site-data). For log output details, see [Logging and Console Messages](/docs/2.0/logging-and-console-messages).

## Prepare for Release

Before shipping an application, review runtime settings and test the packaged release on a clean machine.

### App Settings in `Settings`

`Settings` is the first argument passed to `App::Create()`.

Leave `Settings::enable_profiler` set to `false`, which is the default. Turning it on writes trace files on every run to the diagnostics folder rather than the session data folder— on macOS, this is `~/Library/Caches/<name>`.

The profiler requires the Pro edition or higher.

### Engine Options in `Config`

`Config` is the second argument passed to `App::Create()`.

Keep `Config::diagnostics.developer_mode` set to `false`. When developer mode is off, a release library ignores diagnostics environment variables such as `UL_JS_DIAGNOSTICS`— users cannot alter runtime behavior from their environment. For details, see [Developer Mode and Diagnostics](/docs/2.0/developer-mode-and-diagnostics).

> 🚧 Never ignore SSL errors in production
>
> Keep `Config::ignore_ssl_errors` set to `false`, which is the default. Setting it to `true` connects to servers with invalid certificates, creating a security risk in production.

### View Permissions in `ViewConfig`

Pass `ViewConfig` to `Window::AddPanel()` to configure settings for individual views.

Set `ViewConfig::allow_universal_access_from_file_urls` to `false` when you load local pages you don't fully control. The default value is `true`, which lets a `file:///` page read other frames and send requests to any origin. Setting it to `false` enforces standard cross-origin rules for local files.

### Test on a Clean Machine

Run the distributed application folder on a machine without the SDK or developer tools installed. This confirms that the executable finds its shared libraries and runtime files in their packaged locations.

Verify that `assets/resources/` is present beside the executable— in a macOS app bundle, this belongs in `Contents/Resources/assets/resources/`.
