Creating Release Packages

A finished executable is rarely the whole application. It may need DLLs or frameworks, a license, and a configuration file before someone else can run it. Erbsland Core can collect these pieces into a ZIP during CMake installation, so the archive you share is built from the same release configuration you just tested. This page gets a small application packaged on Windows or macOS. The following pages explain Registering Packages in CMake, Configuring Release Package Contents, and Signing and Notarizing Release Packages when you need more control.

What a Release Package Contains

For each registered package, the tool stages the named executable and its non-system runtime libraries in a temporary directory, creates a ZIP, and places it in the project’s packages directory. On Windows, DLLs sit beside the executable so the Windows loader can find them. On macOS, the default is an .app bundle: libraries and frameworks go into Contents/Frameworks, and the tool repairs their load paths before archiving the bundle. The exact libraries depend on what your executable links at build time.

Signing is a separate choice. On Windows, an Authenticode signature identifies the publisher of the executable and staged DLLs; a timestamp lets the signature remain useful after the signing certificate expires. On macOS, Developer ID signing identifies a directly distributed app, while Apple’s notarization service checks a submitted signed app and issues a ticket that can be attached to it. The tool can perform both steps when you provide signing credentials. You can first build an unsigned package, then set up Signing and Notarizing Release Packages for distribution.

Packaging is supported on Windows and macOS. On Linux, the same CMake call prints a warning during configuration and registers no package rule. Your executable and any separate Linux install rules still work, so a cross-platform project can keep one erbsland_core_package() call.

Register Your First Package

Suppose erbsland/core is a Git submodule in your project, as described in Integrate Core as a Git Submodule. After add_subdirectory(), the application setup helper and the package function are available in the same CMake tree. This complete root CMakeLists.txt registers one package named harbor:

cmake_minimum_required(VERSION 3.28)
project(harbor VERSION 1.4.0 LANGUAGES CXX)

add_subdirectory(erbsland/core)

add_executable(harbor_app src/main.cpp)
erbsland_core_setup_application(TARGET harbor_app)

erbsland_core_package(TARGET harbor_app NAME harbor)

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    install(TARGETS harbor_app RUNTIME DESTINATION bin)
endif()

The project() version supplies the package version by default. TARGET names an executable target that already exists at configure time; NAME becomes the package name. On Linux, CMake warns that no release ZIP is created; harbor_app remains a normal build target. The separate install(TARGETS ...) rule installs the executable under the chosen prefix’s bin directory. Your application can add further Linux install rules for its data and documentation.

On Windows or macOS, build and install the release configuration:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target harbor_app --config Release
cmake --install build --config Release --component Package-harbor

The install component selects just this package. The last command builds the package utility automatically, but the application executable must already have been built. Without --component, CMake runs all install rules, including every registered package and your ordinary install rules. Release and RelWithDebInfo create ZIPs; an install of another configuration warns and skips packaging. Package output does not follow CMAKE_INSTALL_PREFIX: it goes into packages below the root source directory.

On Linux, developers commonly build from source and install into a chosen prefix:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
cmake --install build --prefix "$HOME/.local"

This example puts the executable in $HOME/.local/bin. If you instead prepare a package for a particular Linux distribution, its packaging script can stage the same CMake install rules before assembling the distribution’s native package:

DESTDIR="$PWD/stage" cmake --install build --prefix /usr

The staging tree then contains stage/usr/bin/harbor_app. Your distribution packaging script decides how to turn that tree into its final package. The erbsland_core_package() call does not take over either Linux installation path.

With no package.elcl file, the resulting archive is named harbor-1.4.0-windows-x64.zip on a Windows x64 build or harbor-1.4.0-macos-arm64.zip on a macOS arm64 build. Architecture comes from the build configuration, so yours may differ. The following trees show the shape of each ZIP; runtime library names are examples:

harbor-1.4.0-windows-x64.zip
└── harbor-1.4.0/
    ├── harbor_app.exe
    └── harbor-runtime.dll       # Only when this non-system DLL is required.

harbor-1.4.0-macos-arm64.zip
└── harbor-1.4.0/
    └── harbor_app.app/
        └── Contents/
            ├── Info.plist
            ├── MacOS/harbor_app
            └── Frameworks/       # Populated when runtime libraries are needed.

The directory inside the ZIP and the ZIP basename have separate formats. This matters when you want a stable archive name but still want a versioned directory after extraction.

Add Your Own Files

Place package.elcl beside the root CMakeLists.txt to add a license and default settings:

[main]

*[main.files]*
path: "LICENSE.txt"
target: "docs"

The file is optional; the CMake call does not change when you add it. The source path is relative to package.elcl. After the next release install, both platform archives also contain harbor-1.4.0/docs/LICENSE.txt. You can add more file entries, change archive names, set platform overrides, or choose another version source in Configuring Release Package Contents. If you have several executables or want separate packages, Registering Packages in CMake shows how registration and install components fit together. When you are ready to distribute a trusted build, continue with Signing and Notarizing Release Packages.