Basics

Guides

API Reference

Menu

Basics

Guides

API Reference

Aussom Platform Installer Guide

The Aussom platform installer is a feature of apac that packages your Aussom application into a native installer for Linux (.deb, .rpm), Windows (.msi, .exe), and macOS (.pkg, .dmg). The produced installer carries its own JVM, the Aussom CLI runtime, and your .aus source files. End users double-click the installer and get a native app; they do not need Java or Aussom installed first.

This guide starts with the everyday flow (apac -ii to scaffold, apac -ib to build) and progresses to the per-platform details you reach for once the basic build works.

What It Is Good For

  • shipping an Aussom application as a native install on each major desktop
  • carrying a known-good JVM and Aussom CLI version inside the installer so the user's machine state cannot break your app
  • declaring everything the installer needs in one installer: block in package.yaml, alongside the rest of your apac project config
  • letting one source tree build for every platform the host can run jpackage on (no cross-compilation; you build each native installer on its own OS)

Key Concepts

Concept Meaning
apac -ii "Installer init" - scaffolds the installer: block in package.yaml plus a package-files/ directory tree. Run once.
apac -ib "Installer build" - reads the config, stages everything jpackage needs, runs jpackage, and drops the produced installer in target/installer/<plat>/.
installer: block A new top-level key inside package.yaml. Holds the app name, version, platform sections, and everything jpackage needs to know.
package-files/ A directory next to package.yaml. Holds icons, license text, optional Debian maintainer scripts, native libraries, and per-platform jpackage overrides.
Universal fields Fields inside installer: that apply to every platform (appName, vendor, mainScript, etc.).
Per-platform section linux:, windows:, or macos: - holds settings that vary by OS (installer types, signing, shortcuts, etc.).
jpackage The JDK tool that actually produces the native installer. apac wraps it and stages the inputs.
Fat vs core jar Two flavors of the Aussom CLI jar. The fat jar (~90 MB) includes JavaFX; the core jar (~20 MB) does not. apac picks one per build based on bundle.fx.
GTK4 native cluster The set of native libraries GTK4 needs at runtime (libgtk-4, libglib-2.0, libcairo, libpango-1.0, libharfbuzz, and their transitive deps). On macOS and Windows, apac copies this cluster out of the Aussom CLI install when bundle.gtk4: true. On Linux it relies on the system-installed GTK4.
Bundled runtime The trimmed JDK image that ships inside your installer. apac reuses the runtime apac itself is running on, so the same JDK version end users get matches what built the app.

Getting Started

These two commands cover the happy path:

apac -ii "MyApp"      # scaffold installer config + package-files/ tree
apac -ib               # validate, stage, and run jpackage

apac -ii takes one required argument: the user-facing application name (quoted if it has spaces). It writes the installer: block into the project's package.yaml, generating a minimal package.yaml first if one does not exist. It also creates the package-files/ directory next to it.

apac -ib reads the block, validates the inputs, stages everything jpackage needs under target/staging/<plat>/, and runs jpackage once per requested installer type. Output lands in target/installer/<plat>/.

By default apac -ib builds for the current OS only. Pass -t <type> to filter the type list (for example -t deb on Linux).

The Generated Layout

After apac -ii "MyApp" in an empty directory:

package.yaml                          # extended with the installer: block
package-files/
    README.md                         # in-tree docs about what each subdir is for
    license.txt                       # copy of LICENSE.txt if present, else placeholder
    icons/
        .gitkeep                      # drop app.png / app.ico / app.icns here
    deb/
        postinst                      # bash skeleton w/ /usr/bin/<slug> symlink, executable bit set
        prerm                         # bash skeleton that removes the symlink, executable bit set
    msi/wix-overrides/
        .gitkeep                      # drop WiX overrides here
    pkg/
        postinstall                   # bash skeleton w/ /usr/local/bin/<slug> symlink, executable bit set
    dmg/
        .gitkeep                      # drop macOS .dmg overrides here
    lib/
        .gitkeep                      # drop platform-specific native libs here
    extra-licenses/
        .gitkeep                      # one <jarname>.txt per bundle.extraJars entry

The empty .gitkeep files mark directories that ship empty by default. For deb/ and dmg/, apac only wires the directory into jpackage via --resource-dir when it has actionable content (a file other than .gitkeep or README.md); an empty dir is silently skipped so jpackage uses its built-in defaults. For msi/wix-overrides/ and pkg/ the behavior is different — apac always stages a default WiX template (MSI / EXE) or postinstall script (PKG / DMG) at build time so a freshly-scaffolded project gets the PATH integration and the /usr/local/bin/<slug> symlink without any extra setup. Anything the user drops in those directories overlays on top.

Example 1: Build Your First Installer

This walkthrough starts from an empty directory and ends with a working .deb you can install on Debian or Ubuntu.

Create a project directory and scaffold the installer config:

mkdir hello-world
cd hello-world
apac -ii "MyApp"

This generates package.yaml (with sensible defaults) and the package-files/ directory. The scaffolded mainScript defaults to myapp.aus — the slug-form of "MyApp" (lowercased, with non-alphanumeric runs collapsed to single hyphens) — so the entry script, the deb package name, and the installed launcher path all share one name. For an appName with spaces, like "Hello JavaFX", the slug would be hello-javafx and the scaffolded mainScript would be hello-javafx.aus.

Create that entry script:

cat > myapp.aus <<'EOF'
class Hello {
    public Hello() { }
    public main() {
        c.println("hello from MyApp");
    }
}
EOF

Build:

apac -ib

apac prints progress through each phase, then jpackage takes over and produces the .deb under target/installer/linux/. Install it with sudo dpkg -i target/installer/linux/myapp_1.0.0_amd64.deb, then run myapp from any shell to see hello from MyApp. The /usr/bin/myapp entry comes from the scaffolded package-files/deb/postinst.

Example 2: Add an Icon

The scaffold leaves the icon path commented out in package.yaml. To turn it on, drop an icon into package-files/icons/ and uncomment the matching line in the platform section.

For Linux:

linux:
    # icon: package-files/icons/app.png    # uncomment + drop a 1024x1024 PNG here
    types:
    - deb

After uncommenting and providing package-files/icons/app.png, re-run apac -ib. The produced .deb now installs with the icon registered in the desktop menu.

Use app.png for Linux, app.ico for Windows (multi-resolution), and app.icns for macOS. Tools like ImageMagick and png2icns convert between them.

Example 3: Bundle JavaFX

By default the produced installer ships the slim core Aussom CLI jar without the JavaFX ecosystem. If your app uses include fx.*; anywhere, turn on FX in each platform section that needs it:

linux:
    types:
    - deb
    bundle:
        fx: true            # use the fat Aussom CLI jar with JavaFX shaded in

The bundle gets roughly 70 MB larger. On macOS, apac does NOT add -XstartOnFirstThread for FX-only apps — JavaFX Glass spawns its own Cocoa main-loop thread and the FX window fails to appear when the flag is set. (GTK4 needs the opposite — see Example 4.)

Example 4: Bundle GTK4 on macOS and Windows

If your app uses include gtk.gtk; (or any other generated GTK4 binding), the Aussom CLI's binding code is already inside the CLI jar - but the GTK4 native libraries it calls into need to be present on the end user's machine. Linux end users almost always have GTK4 installed through the distro package manager; macOS and Windows users typically do not.

Turn on bundle.gtk4 for the platforms where you want apac to ship the GTK4 native cluster inside the installer:

linux:
    types:
    - deb
    bundle:
        gtk4: true          # accepted but no-op; deb depends on libgtk-4-1

windows:
    types:
    - msi
    bundle:
        gtk4: true          # apac copies the GTK4 DLL cluster into the bundle

macos:
    types:
    - pkg
    bundle:
        gtk4: true          # apac copies the GTK4 dylib cluster into the bundle

On macOS and Windows builds, apac reads a per-platform manifest shipped inside the Aussom CLI install at $APPDIR/packaging/gtk4-libs-macos.txt (or gtk4-libs-windows.txt), matches each glob in the manifest against the files in the CLI's lib/ directory, and copies the matches into your installer's lib/. The macOS bundle gains roughly 100 MB; the Windows bundle gains roughly 80 MB.

On Linux, bundle.gtk4: true is accepted but no-op — no libs are copied. End users are expected to have GTK4 already installed via their distro's package manager (libgtk-4-1 on Debian / Ubuntu, gtk4 on Fedora / Arch); the system loader resolves the imports at runtime. apac does not add this as a declared package dependency to the produced .deb or .rpm — if you want that, list it in linux.raw (e.g. ["--linux-package-deps", "libgtk-4-1"]).

You do not need to manage the per-lib list yourself. When the Aussom CLI ships a new GTK4 dependency, the manifest is updated in the same release, and your next apac -ib picks the new file up automatically.

Example 5: Build for One Platform Type Only

apac -ib defaults to every type listed under the current platform. Pass -t to narrow to one:

apac -ib -t deb           # only the .deb, even if rpm is also listed
apac -ib -t app-image     # plain runtime image, no installer

app-image is the fastest type to iterate on - no installer wrapping, no host packaging tools needed (no dpkg-deb, no WiX, no pkgbuild). The output is a directory you can launch directly.

Example 6: Add Debian Maintainer Hooks

The scaffold drops a postinst and prerm into package-files/deb/ that come pre-wired to symlink the bundled launcher into /usr/bin/, so the app shows up on $PATH for every shell as soon as the .deb installs:

#!/bin/sh
# postinst - Debian package hook for this app.
# Runs as root. Edit as needed.
set -e
ln -s /opt/hello-javafx/bin/hello-javafx /usr/bin/hello-javafx
exit 0
#!/bin/sh
# prerm - Debian package hook for this app.
# Runs as root. Edit as needed.
set -e
rm /usr/bin/hello-javafx
exit 0

The path is derived from your appName: APAC lowercases it and folds non-alphanumeric runs to hyphens, matching the install path jpackage produces (/opt/<slug>/). For appName: "Hello JavaFX" the slug is hello-javafx; for appName: "hellojavafx" it stays hellojavafx.

To layer extra install-time steps on top of the symlink, edit the scaffolded postinst in place and add your commands above exit 0.

apac wires the whole package-files/deb/ directory to jpackage via --resource-dir. jpackage looks up these filenames inside that directory: postinst, prerm, preinst, postrm, control, copyright. Any file you supply replaces the matching jpackage default. Important: the scaffolded postinst IS already a replacement for jpackage's default, so anything jpackage's default would have run (notably xdg-desktop-menu install for .desktop registration) does not happen unless you add it back. See the jpackage override-resources docs for the list of behaviors jpackage's bundled defaults provide.

Universal Options

These keys live at the top of the installer: block and apply to every platform.

Field Required Description
enabled no Set to false to disable the installer entirely without removing the block. Defaults to true.
appName yes User-facing application name. Used for the launcher name and the installer title. Quote it if it has spaces. Does not inherit from the package's name: field.
version no App version. Defaults to the top-level package version: when omitted.
vendor yes Company or author name shown in the OS package metadata.
copyright yes One-line copyright notice.
description yes Short product description shown by the OS package tools.
mainScript yes Bare filename of the entry .aus (no path). apac stages the directory holding this file as the app's root. The scaffolded default is <appName-slug>.aus (e.g. appName: "Hello JavaFX"hello-javafx.aus).
appArgs no List of CLI arguments appended after the mainScript path when the launcher runs.
javaOptions no List of JVM arguments to pass to the bundled runtime. Per-platform javaOptions lists are appended to this one at build time.

mainScript looks up the entry script in two places, in this order: src/main/aus/<package-name>/<mainScript> (Maven-style layout), then <cwd>/<mainScript> (flat layout). The first match wins. The script's parent directory becomes the staged app/ root, so any sibling .aus files travel with it.

Per-Platform Configuration

Each platform section holds the fields that vary by OS. linux:, windows:, and macos: are independent - declare only the ones you want to build for.

Shared Per-Platform Fields

These fields appear under every platform section.

Field Description
types List of installer types to build for this platform. At least one is required. See per-platform tables below for valid values.
icon Path to the icon file. Optional; jpackage uses a generic icon when absent. The scaffold leaves this commented out with a hint at the conventional location.
shortcut Boolean. Linux: write a .desktop entry. Windows: write desktop + start-menu shortcuts. Ignored on macOS (apps install to /Applications).
console Windows only. When true, apac passes --win-console to jpackage so the launcher attaches a console window and the app's stdout/stderr are visible. Scaffolded default true for CLI apps; flip to false for pure-GUI Windows apps. Ignored on Linux and macOS.
fileAssociations List of {extension, description, mimeType} entries. apac synthesizes one --file-associations properties file per entry.
launchers Additional native launchers next to the main one. Each entry needs a name and mainScript; optional appArgs, icon, description, javaOptions.
javaOptions JVM options appended to the universal javaOptions list for this platform only.
bundle.fx Bundle the JavaFX ecosystem. Defaults to false (smaller bundle).
bundle.gtk4 macOS and Windows: copy the GTK4 native lib cluster out of the Aussom CLI install into the produced installer. Accepted on Linux but no-op (Linux uses system GTK4). Defaults to false. See Example 4.
bundle.extraJars List of paths to third-party JARs to drop into the installer's libs/ directory. See Bundling Third-Party Jars.
bundle.extraResources List of directories to copy verbatim into the staging tree. Useful for embedded data, templates, or config trees.
signing.enabled Turn on code signing. macOS only. Setting it true on Windows currently fails validation; see Code Signing.
signing.identity Platform-specific signing identity (Apple Developer ID on macOS).
raw List of literal extra jpackage CLI args appended to that platform's invocation. Escape hatch for anything apac does not surface by name.

Linux

Valid types: deb, rpm, app-image.

Field Description
(inherits all shared fields above)

Host tools required: dpkg-deb and fakeroot for .deb; rpmbuild for .rpm. Both ship in the standard distro repositories. app-image requires no extra host tools.

Drop maintainer scripts and templates into package-files/deb/: postinst, prerm, preinst, postrm, control, copyright. Each filename is literal and case-sensitive. See Example 6 for the override contract.

Windows

Valid types: msi, exe, app-image.

Field Description
console Scaffolded to true because Aussom apps are predominantly CLI tools. With console: true apac passes --win-console to jpackage and the launcher attaches a visible terminal — every c.println / c.err line appears in that window. Flip to false for pure-GUI apps that should run windowed with no console attached.
signing.identity Currently rejected at validation time (jpackage 23 does not expose a Windows code-signing flag). Sign the produced .msi or .exe externally with signtool.exe.

Out of the box, the produced MSI / EXE installer also adds the install directory to the per-user PATH environment variable. Once installed, the user can invoke the app from any cmd / PowerShell by typing the appName (the launcher exe is <AppName>.exe under C:\Program Files\<AppName>\). This mirrors the Linux deb's /usr/bin/<slug> symlink and the macOS pkg's /usr/local/bin/<slug> symlink.

apac wires this by staging a default main.wxs template into a generated resource directory when package-files/msi/wix-overrides/ holds only marker files. The template mirrors jpackage's bundled default and adds a single <Component> with a WiX <Environment> entry that appends [INSTALLDIR] to the per-user PATH (HKCU, removed on uninstall). The component's GUID is derived deterministically from the appName via a Type-3 UUID so MSI upgrades match the prior install's component identity.

The moment you drop ANY non-marker file into package-files/msi/wix-overrides/, apac switches over to passing that directory to jpackage and stops staging its default. Drop a fresh main.wxs to fully replace the WiX project; you own PATH wiring (and everything else) from that point.

Host tools required: WiX 3.x. WiX 4 and 5 are not compatible with the JDK 23 jpackage and apac's host-tool check rejects them with a pointer to the WiX 3.14 download. Make sure WiX 3.14's bin/ directory is first on PATH (the executables are candle.exe and light.exe).

macOS

Valid types: pkg, dmg, app-image.

Field Description
signing.identity Apple Developer ID identity (the certificate's "common name"). Passed to jpackage as --mac-signing-key-user-name when signing.enabled: true.

Host tools required: pkgbuild and hdiutil. Both ship with Xcode command-line tools.

apac auto-injects -XstartOnFirstThread into the JVM args when macos.bundle.gtk4: true, NEVER for FX-only apps. The two macOS UI stacks disagree about thread-0 ownership:

  • GTK4 hard-requires main() on tid 0; without the flag, gtk_window_present aborts with 'NSWindow should only be instantiated on the main thread!'.
  • JavaFX Glass spawns its own Cocoa main-loop thread and the FX window fails to appear when the flag is set (Glass and main fight over tid 0).

For hybrid FX + GTK4 builds (uncommon) apac still adds the flag because GTK's failure is a hard crash while FX's is a soft no-window. If you also list -XstartOnFirstThread in macos.javaOptions, the dedup check (whitespace-insensitive) makes sure only one copy reaches the produced launcher.

apac also sets two macOS-specific jpackage flags for every macOS build so each produced .app has a distinct LaunchServices / NSApplication identity:

Flag apac default Why
--mac-package-identifier the appName slug (e.g. hello-javafx) Becomes CFBundleIdentifier. Without an explicit value, jpackage falls back to --main-class, which is ALWAYS com.lehman.aussom.Main for apac-built apps. Every apac-produced .app on the same machine would then share that identifier, and macOS would treat them all as the same registered app — JavaFX windows fail to appear (Glass calls NSApplication, gets the wrong bundle state back, nothing shows).
--mac-package-name the case-preserving sanitized appName, truncated to 15 chars Becomes CFBundleName (the menu-bar title). jpackage rejects names of 16 chars or longer with this flag, so apac always truncates.

To override either, list the flag and value in macos.raw:

installer:
  macos:
    raw:
      - "--mac-package-identifier"
      - "com.acme.myapp"

apac detects the user-supplied value in raw and skips its own auto-add for that flag so jpackage doesn't see a duplicate.

Notarization is out of scope. If you need it, run xcrun notarytool yourself against the produced .pkg.

Drop installer assets (background image, custom welcome text) into package-files/pkg/ for .pkg builds or package-files/dmg/ for .dmg builds.

A working postinstall script ships with every produced .pkg out of the box. It runs as root after macOS Installer has placed the app bundle at /Applications/<AppName>.app and creates a symlink so the app shows up on $PATH for every shell. For an appName of "Hello JavaFX", the rendered body is:

#!/bin/sh
# postinstall - macOS pkg post-install hook for Hello-JavaFX.
# Runs as root after macOS Installer has placed the app bundle
# at /Applications/Hello-JavaFX.app. Edit by dropping your own
# postinstall into package-files/pkg/ (your copy wins on
# collision with the apac default).
set -e
APP="/Applications/Hello-JavaFX.app"

# PATH symlink. ln -sf overwrites any prior symlink so a
# re-install or upgrade refreshes the link without erroring.
ln -sf "$APP/Contents/MacOS/Hello-JavaFX" /usr/local/bin/hello-javafx
exit 0

APP uses the case-preserving sanitized appName (matches the .app bundle and launcher binary jpackage produces); the /usr/local/bin/ link uses the lowercase slug for a shell-friendly command name, matching the Linux deb's /usr/bin/<slug>.

apac -ii also drops a copy of this script at package-files/pkg/postinstall so the default is visible and editable in your project. apac stages the default at build time regardless — so older projects that were scaffolded before the postinstall existed still get the symlink without re-running apac -ii. To customize, edit the file in package-files/pkg/ (or drop a fresh postinstall there); your copy wins on collision with the apac default. Any other files you drop alongside (background image, license RTF, etc.) ride along into jpackage's resource directory unchanged.

The package-files/ Directory

Each subdirectory under package-files/ has a specific contract.

Subdir What goes in it
icons/ App icons. Reference them via the per-platform icon: field. apac does not pick a default; if icon: is unset, jpackage uses its generic icon.
deb/ Debian maintainer scripts (postinst, prerm, preinst, postrm) and control templates (control, copyright). Filenames are literal.
msi/wix-overrides/ WiX template overrides for the Windows MSI build.
pkg/ macOS .pkg installer assets, plus the scaffolded postinstall script (literal filename — pkgbuild runs it as root during install).
dmg/ macOS .dmg installer assets.
lib/ Platform-specific native libraries (.so, .dll, .dylib) the app loads at runtime.
extra-licenses/ One <jarname>.txt per entry in bundle.extraJars. apac concatenates these into THIRD_PARTY_LICENSES.txt.

For deb/ and dmg/, apac wires the directory to jpackage via --resource-dir only when it contains a file other than .gitkeep or README.md; an empty directory is silently skipped and jpackage uses its built-in defaults. For msi/wix-overrides/ and pkg/, apac always passes a --resource-dir — when the directory is empty, apac stages its own defaults (the WiX template that adds INSTALLDIR to the per-user PATH on Windows, and the postinstall that symlinks the launcher into /usr/local/bin/<slug> on macOS); user files overlay on top.

Native Libraries

Native libraries (.so on Linux, .dll on Windows, .dylib on macOS) are not a schema concern. apac has no opinion on which libraries your app needs - that depends on the host platform, architecture, and runtime version, none of which apac can read from package.yaml.

Convention: drop the libraries for the current build target into package-files/lib/ and apac copies the contents verbatim into the installer's lib/ directory (skipping the .gitkeep marker). The installed app's launcher arranges for that directory to be on the system loader's search path so the libs resolve at runtime.

A typical multi-platform release pipeline has a per-platform CI step that stages the right libraries into package-files/lib/ before invoking apac -ib. If you need different libraries on Linux vs Windows, your pipeline manages that swap - apac does not.

GTK4 is the one exception. Because the GTK4 native cluster is large (hundreds of files), version-coupled to the Aussom CLI's own bindings, and identical from app to app, apac ships it as a managed bundle: set bundle.gtk4: true and apac copies the right files out of the Aussom CLI install for you on macOS and Windows. You do not need to drop any GTK4 libs into package-files/lib/ yourself, and the two paths compose - any libs you put in package-files/lib/ ship in addition to the GTK4 cluster apac copies.

Bundling Third-Party Jars

To ship a third-party JAR (a JDBC driver, an ORM, anything not already in the Aussom CLI), list its path under bundle.extraJars:

linux:
    types:
    - deb
    bundle:
        extraJars:
        - libs/sqlite-jdbc-3.45.0.0.jar
        - libs/postgresql-42.7.3.jar

For each entry, drop a matching <jarname>.txt into package-files/extra-licenses/ containing the JAR's license text. apac concatenates these into a THIRD_PARTY_LICENSES.txt file that ships inside the installer.

The build fails if any extraJars entry is missing its license file. The goal is to make license-compliance impossible to skip silently.

apac copies each JAR into the installer's libs/ directory. jpackage's recursive input scan picks up every .jar it finds under the staged input tree and adds it to the launcher's startup classpath, so the extra jars are immediately available to include-resolved code with no extra setup on your side.

Extra Resources

Use bundle.extraResources to ship arbitrary directory trees alongside the app:

linux:
    types:
    - deb
    bundle:
        extraResources:
        - data
        - templates

Each listed directory gets copied verbatim into the staging tree under its original name. The launched app sees it at $APPDIR/data/, $APPDIR/templates/, etc.

Code Signing

Platform Status
macOS Supported. Set macos.signing.enabled: true and macos.signing.identity to your Apple Developer ID. apac passes them to jpackage.
Windows Not yet supported. jpackage 23 does not expose a Windows code-signing flag. Setting windows.signing.enabled: true fails validation up front; sign the produced .msi or .exe externally with signtool.exe.
Linux N/A. .deb and .rpm do not use code signatures the way macOS and Windows installers do; package signing is a registry-side concern.

Validation and Exit Codes

apac -ib validates the config and the host before running jpackage. It exits with these codes:

Code Meaning
0 All requested installers built successfully.
1 One or more jpackage invocations failed. The last failing invocation's output appears just above the error line.
2 Config error: a field is missing, a referenced file does not exist, the mainScript is not a bare filename, etc. The error message names the field and the expected value.
3 Host tool missing or wrong version (for example, WiX 4 on Windows). The error message names the tool and how to install the right one.

Common config errors and what they mean:

  • installer.mainScript 'main.aus' not found - apac looked at src/main/aus/<pkg>/main.aus and <cwd>/main.aus; neither exists. Create the file or fix the value.
  • installer.<plat>.icon '...' not found - the icon file is missing. Drop the file at the listed path or comment the icon: line out.
  • extra jar '...' is missing its license file - drop the matching <jarname>.txt into package-files/extra-licenses/.
  • installer.mainScript must be a bare filename - the value contains / or \\. apac stages the script's parent directory as the app root; subdir paths would silently change what gets staged. Move the file or refactor.

Dry Run

Pass --dry-run to validate, stage, and print the jpackage command line without actually invoking jpackage:

apac -ib --dry-run

The printed command is shell-quoted, so you can copy-paste it to debug jpackage output directly. The staging tree under target/staging/<plat>/ is still produced and is safe to inspect.

This is the fastest way to confirm a config change before paying for a full installer build.

Multi-Platform Builds

apac does not cross-compile. To produce a .deb, .msi, and .pkg from the same source tree, you need to run apac -ib on each OS in turn. The typical pattern is a CI matrix with one job per OS, each checking out the source, staging the right platform-specific native libraries into package-files/lib/, and running apac -ib.

The staging tree under target/staging/<plat>/ is per-platform isolated, so simultaneous builds on a shared filesystem do not collide.