Reference
AppBundler is structured around two concepts: a product spec, which describes how to build a Julia application, and a bundle format, which describes how to package it for distribution on a target platform. These are combined through the bundle function:
bundle(spec, format, destination; password = "")This is also the function exposed to the command-line API, where command-line parameters configure the spec and format fields.
Product Specs
A product spec defines how the application is compiled or staged. There are two kinds:
spec = JuliaImgBundle(project; kwargs...) # stages a self-contained Julia image
spec = JuliaCBundle(project; kwargs...) # compiles a native executable via juliacBoth specs can be staged directly into a directory for inspection before packaging via stage(spec, destination). For JuliaImgBundle, staging is platform-agnostic as long as compilation is not required — cross-platform staging is possible by setting precompile=false and sysimg_packages = [], with the target platform specified via the platform keyword (defaults to HostPlatform()). For JuliaCBundle, the platform is fixed to the host, as determined by the juliac executable on PATH.
Bundle Formats
A bundle format defines the packaging target. The three supported formats are DMG (macOS), MSIX (Windows), and Snap (Linux). They are instantiated from a project directory:
dmg = DMG(project; arch = Sys.ARCH, kwargs...)
msix = MSIX(project; arch = Sys.ARCH, kwargs...)
snap = Snap(project; arch = Sys.ARCH, kwargs...)Each format reads configuration file overrides from the corresponding project/meta/<format> directory and carries architecture information that determines the destination platform. Bundle formats can also be staged independently via stage(format, destination) to produce the directory structure before compression and signing.
Low-Level Bundle API
For packaging non-Julia applications, or when you need full control over what goes into the bundle, the lower-level bundle do-block API can be used directly:
bundle(format, destination; password = "") do staging_dir
# copy or compile application files into staging_dir
endThe password argument is the certificate password used to decrypt the signing certificate and perform code signing during the pack step. Non-Julia applications can be bundled this way as well, as long as the user takes care of building and installing the application files in the setup callback.
Types
AppBundler.JuliaImg.JuliaImgBundle — Type
JuliaImgBundle(project; kwargs...)Build specification for staging a Julia application as a self-contained image.
The Julia version is read from Manifest.toml in project (falling back to the running Julia version). Incremental compilation defaults to true when no sysimage packages are requested; building a sysimage forces incremental = false because a new sysimage invalidates all existing pkgimage caches.
Arguments
project::String: Path to the application directory containingProject.tomlandManifest.toml
Keyword Arguments
precompile::Bool = true: Precompile packages during staging. Whenfalse, precompilation is deferred to the first launch on the target systemincremental::Bool = isempty(sysimg_packages): Whether to reuse existing pkgimage caches. Automatically set tofalsewhensysimg_packagesis non-emptysysimg_packages::Vector{String} = []: Packages to bake into the system image. Triggers a full sysimage rebuild, which invalidates all pkgimage cachessysimg_args::Cmd =``: Extra command-line arguments forwarded to the sysimage compilerremove_sources::Bool = false: Strip.jlsource files from the staged package tree (only meaningful when all relevant packages are baked into the sysimage)asset_spec::Dict{Symbol,Vector{String}} = Dict(): Selective asset inclusion rules. When empty, all artifacts are included and indexed viapkgoriginasset_rpath::String = "assets": Destination subdirectory for assets whenasset_specis setstartup_file::String: Path to thestartup.jltemplate. Resolved frommeta/startup.jlinsideproject, falling back to$(pkgdir(AppBundler))/recipes/startup.jl
Examples
# Default: precompile everything, incremental caches
pkg = JuliaImgBundle("path/to/app")
# Skip precompilation — compile on first launch instead
pkg = JuliaImgBundle("path/to/app"; precompile = false)
# Bake heavy dependencies into a sysimage (forces non-incremental rebuild)
pkg = JuliaImgBundle("path/to/app"; sysimg_packages = ["Plots", "DifferentialEquations"])AppBundler.JuliaC.JuliaCBundle — Type
JuliaCBundle(project; kwargs...)Build specification for compiling a Julia application into a native executable via juliac.
Unlike JuliaImgBundle, which stages a full Julia runtime alongside precompiled package images, JuliaCBundle ahead-of-time compiles the application into a standalone native executable. The juliac tool must be installed and is looked up in bin/juliac under each entry of DEPOT_PATH, with ~/.julia/bin as a final fallback.
Arguments
project::String: Path to the application directory containingProject.toml
Keyword Arguments
juliac_cmd::Cmd = Cmd([juliac()]): Command used to invokejuliac. Defaults to the firstjuliacexecutable found onDEPOT_PATHexecutable_name::String: Name of the produced executable. Defaults to the lowercase module name derived fromProject.tomltrim::Bool = false: Whentrue, passes--trim=safetojuliac, removing unreachable code from the output binaryargs::Cmd =`: Additional arguments forwarded verbatim tojuliac`asset_spec::Dict{Symbol,Vector{String}} = Dict(): Selective asset inclusion rules. When empty, no assets are copied into the bundleasset_rpath::String = "assets": Destination subdirectory for assets insidedestination
Examples
# Minimal: compile with defaults
pkg = JuliaCBundle("path/to/app")
# Enable dead-code trimming and a custom executable name
pkg = JuliaCBundle("path/to/app"; executable_name = "myapp", trim = true)AppBundler.DMG — Type
DMG([overlay]; arch, compress, windowed, kwargs...)Create a DMG configuration object for macOS application packaging.
When overlay is provided, configuration files are searched in overlay, then overlay/meta, then the built-in recipes directory. Application parameters (APP_NAME, APP_VERSION, etc.) are read from overlay/Project.toml, and packaging defaults (selfsign, compression, etc.) are read from overlay/LocalPreferences.toml. Without overlay, only the built-in recipes and the active project's LocalPreferences.toml are used.
Arguments
overlay: Path to a project directory containingProject.toml, optionalLocalPreferences.toml, and optionalmeta/dmg/overrides
Keyword Arguments
prefix = joinpath(dirname(@__DIR__), "recipes"): Base directory or array of directories to search for configuration files in sequential ordericon = get_path(prefix, ["dmg/icon.icns", "dmg/icon.png", "icon.icns"]): Path to application icon (.icns or .png)info_config = get_path(prefix, "dmg/Info.plist"): Path to Info.plist template with app metadataentitlements = get_path(prefix, "dmg/Entitlements.plist"): Path to entitlements file for code signingdsstore = get_path(prefix, ["dmg/DS_Store.toml", "dmg/DS_Store"]): Path to DS_Store file or TOML template for Finder window appearanceselfsign: Iftrue, generate a temporary self-signed certificate instead of usingpfx_cert; defaults toselfsignpreferencepfx_cert = get_path(prefix, "dmg/certificate.pfx"): Path to code signing certificateshallow_signing: Iftrue, sign only the top-level bundle rather than all nested binaries; defaults todmg_shallow_signingpreferencehardened_runtime: Iftrue, enable hardened runtime during signing (required for notarization); defaults todmg_hardened_runtimepreferencesandboxed_runtime: Iftrue, enable the App Sandbox entitlement; defaults todmg_sandboxed_runtimepreferencemain_launcher: Path to the Julia entry-point script. When set, a native redirect launcher is installed atContents/MacOS/<app_name>and the script itself atContents/Libraries/main; resolved from prefix using the bundler predicate; omitted if not foundhfsplus = false: Iftrue, use HFS+ filesystem when building the disk image otherwise uses ISOwindowed: Iftrue, the application runs without a console window; defaults towindowedpreferencecompress: Iftrue, pack the staging directory into a.dmgdisk image; defaults tocompresspreferencecompression: Compression algorithm for the disk image (:lzma,:bzip2,:zlib, or:lzfse); defaults todmg_compressionpreferencearch = Sys.ARCH: Target CPU architecturepredicate: Bundler predicate used for hook selection; defaults tobundlerpreferenceparameters: Dictionary of parameters for Mustache template rendering. Whenoverlayis provided, pre-populated fromProject.tomland preferences:APP_NAME,APP_DISPLAY_NAME,APP_VERSION,BUILD_NUMBER,APP_SUMMARY,APP_DESCRIPTION,BUNDLE_IDENTIFIER,PUBLISHER_DISPLAY_NAME,MODULE_NAME(Julia-based bundles only),WINDOWED, andSANDBOXED_RUNTIME
Examples
DMG() # default recipes only
DMG(app_dir) # overlay with Project.toml parameters
DMG(app_dir; hardened_runtime = false) # overlay with keyword overrides
DMG(; prefix = ["custom/", "recipes/"]) # explicit search pathAppBundler.MSIX — Type
MSIX([overlay]; arch, compress, windowed, kwargs...)Create an MSIX configuration object for Windows application packaging.
When overlay is provided, configuration files are searched in overlay, then overlay/meta, then the built-in recipes directory. Application parameters (APP_NAME, APP_VERSION, etc.) are read from overlay/Project.toml, and packaging defaults (path_length_threshold, selfsign, etc.) are read from overlay/LocalPreferences.toml. Without overlay, only the built-in recipes and the active project's LocalPreferences.toml are used.
Arguments
overlay: Path to a project directory containingProject.toml, optionalLocalPreferences.toml, and optionalmeta/msix/overrides
Keyword Arguments
prefix = joinpath(dirname(@__DIR__), "recipes"): Base directory or array of directories to search for configuration files in sequential ordericon = get_path(prefix, ["msix/Assets", "msix/icon.png", "icon.png"]; dir = true): Path to application icon file or Assets directoryappxmanifest = get_path(prefix, "msix/AppxManifest.xml"): Path to MSIX application manifest templateresources_pri = get_path(prefix, "msix/resources.pri"): Path to package resource index filemsixinstallerdata = get_path(prefix, "msix/MSIXAppInstallerData.xml"): Path to installer configuration templatepath_length_threshold: Maximum allowed path length; defaults tomsix_path_length_thresholdpreferenceskip_long_paths: Iftrue, skip files exceeding path length threshold; iffalse, throw an error; defaults tomsix_skip_long_pathspreferenceskip_symlinks: Iftrue, skip file and directory symlinks; defaults tomsix_skip_symlinkspreferenceskip_unicode_paths: Iftrue, skip files with non-ASCII paths; defaults tomsix_skip_unicode_pathspreferenceselfsign: Iftrue, generate a temporary self-signed certificate instead of usingpfx_cert; defaults toselfsignpreferencepublisher: Publisher string embedded in the manifest; defaults tomsix_publisherpreferencepfx_cert = get_path(prefix, "msix/certificate.pfx"): Path to code signing certificatewindowed: Iftrue, the application runs without a console window; defaults towindowedpreferencecompress: Iftrue, pack the staging directory into an.msixarchive; defaults tocompresspreferencearch = Sys.ARCH: Target CPU architecturepredicate: Bundler predicate used for hook selection; defaults tobundlerpreferenceparameters: Dictionary of parameters for Mustache template rendering. Whenoverlayis provided, pre-populated fromProject.tomland preferences:APP_NAME,APP_DISPLAY_NAME,APP_VERSION,BUILD_NUMBER,APP_SUMMARY,APP_DESCRIPTION,BUNDLE_IDENTIFIER,PUBLISHER_DISPLAY_NAME,MODULE_NAME(Julia-based bundles only),WINDOWED, andPUBLISHER
Examples
MSIX() # default recipes only
MSIX(app_dir) # overlay with Project.toml parameters
MSIX(app_dir; skip_long_paths = true) # overlay with keyword overrides
MSIX(; prefix = ["custom/", "recipes/"]) # explicit search pathAppBundler.Snap — Type
Snap([overlay]; arch, compress, windowed, kwargs...)Create a Snap configuration object for Linux application packaging.
When overlay is provided, configuration files are searched in overlay, then overlay/meta, then the built-in recipes directory. Application parameters (APP_NAME, APP_VERSION, etc.) are read from overlay/Project.toml, and packaging defaults (windowed, compress, etc.) are read from overlay/LocalPreferences.toml. Without overlay, only the built-in recipes and the active project's LocalPreferences.toml are used.
Arguments
overlay: Path to a project directory containingProject.toml, optionalLocalPreferences.toml, and optionalmeta/snap/overrides
Keyword Arguments
prefix = joinpath(dirname(@__DIR__), "recipes"): Base directory or array of directories to search for configuration files in sequential ordericon = get_path(prefix, ["snap/icon.png", "icon.png"]): Path to application icon filesnap_config = get_path(prefix, "snap/snap.yaml"): Path to Snap package metadata templatedesktop_launcher = get_path(prefix, "snap/main.desktop"): Path to desktop entry file template for GUI integrationconfigure_hook: Path to configuration hook script run onsnap set; resolved from prefix using the bundler predicate; omitted if not foundmain_launcher: Path to main launcher script installed intobin/; resolved from prefix using the bundler predicate; omitted if not foundwindowed: Iftrue, the application runs without a console window; defaults towindowedpreferencecompress: Iftrue, pack the staging directory into a.snaparchive; defaults tocompresspreferencearch = Sys.ARCH: Target CPU architecturepredicate: Bundler predicate used for hook selection; defaults tobundlerpreferenceparameters: Dictionary of parameters for Mustache template rendering. Whenoverlayis provided, pre-populated fromProject.tomland preferences:APP_NAME,APP_DISPLAY_NAME,APP_VERSION,BUILD_NUMBER,APP_SUMMARY,APP_DESCRIPTION,BUNDLE_IDENTIFIER,PUBLISHER_DISPLAY_NAME,MODULE_NAME(Julia-based bundles only), andWINDOWED
Examples
Snap() # default recipes only
Snap(app_dir) # overlay with Project.toml parameters
Snap(app_dir; windowed = false) # overlay with keyword overrides
Snap(; prefix = ["custom/", "recipes/"]) # explicit search pathFunctions
AppBundler.stage — Method
stage(product::JuliaImgBundle, destination::String;
platform::AbstractPlatform = HostPlatform(),
cpu_target = get_cpu_target(platform),
runtime_mode = "MIN",
app_name = "",
bundle_identifier = "")Stage a Julia application into destination, ready for packaging and distribution.
The staging process:
- Downloads and extracts the Julia runtime for
platform(version fromManifest.toml) - Copies all non-stdlib packages and artifacts into the stdlib tree
- Configures
startup.jl,DEPOT_PATH, andLOAD_PATHviaAppEnv - Optionally compiles a system image from
product.sysimg_packages - Optionally precompiles pkgimages for all project dependencies
When product.precompile is true, platform must match the host OS and architecture (see cross-compilation constraints below).
Arguments
product::JuliaImgBundle: Staging configurationdestination::String: Directory in which the staged application is assembled
Keyword Arguments
platform::AbstractPlatform: Target platform; defaults to the current hostcpu_target: LLVM CPU target string; derived fromplatformby defaultruntime_mode: AppEnv runtime mode string passed toAppEnv.save_configapp_name: Application name embedded in the AppEnv configbundle_identifier: Bundle identifier embedded in the AppEnv config (e.g. reverse-DNS on macOS)
Cross-Compilation Constraints
Precompilation runs native code, so cross-OS and cross-architecture compilation is not supported when product.precompile = true:
- Windows: host must be Windows
- macOS: host must be macOS;
x86_64 → aarch64is not supported - Linux: host must be Linux with a matching architecture
Set precompile = false and sysimg_packages = [] in JuliaImgBundle to stage for a different platform without compilation.
Examples
pkg = JuliaImgBundle("src/MyApp")
# Stage for the current machine
stage(pkg, "build/staging")
# Stage for macOS arm64 (must run on Apple Silicon with precompile=false, or natively)
stage(pkg, "build/MyApp.app/Contents/Resources/julia";
platform = Platform("aarch64", "macos"))
# Embed app identity in the AppEnv config
stage(pkg, "build/staging"; app_name = "MyApp", bundle_identifier = "com.example.myapp")AppBundler.stage — Method
stage(spec::JuliaCBundle, destination::String;
runtime_mode = "MIN",
app_name = get_module_name(spec.project),
bundle_identifier = "")Compile a Julia application into a native executable and assemble it in destination.
The staging process:
- Saves an AppEnv config to
destination/configwith runtime identity and load-path settings - Installs assets from
spec.asset_specintodestination/<asset_rpath> - Writes a pkgorigin index to
destination/indexfor asset resolution at runtime - Invokes
juliacto AOT-compile the application and bundle the result intodestination
Unlike JuliaImgBundle, no Julia runtime tarball is downloaded — juliac produces a self-contained native binary. The host toolchain must be compatible with the target.
Arguments
spec::JuliaCBundle: Compilation and asset configurationdestination::String: Directory in which the compiled application is assembled
Keyword Arguments
runtime_mode: AppEnv runtime mode string passed toAppEnv.save_configapp_name: Application name embedded in the AppEnv config; defaults to the module namebundle_identifier: Bundle identifier embedded in the AppEnv config (e.g. reverse-DNS on macOS)
Examples
pkg = JuliaCBundle("src/MyApp")
# Stage into a directory
stage(pkg, "build/myapp";
app_name = "MyApp", bundle_identifier = "com.example.myapp")
# Stage with a custom runtime mode
stage(pkg, "build/staging"; runtime_mode = "SANDBOX")AppBundler.stage — Method
stage(config, destination::String; [dsstore=false])Stage package metadata and directory structure into destination in preparation for bundling.
config is a format-specific configuration object — MSIX, DMG, or Snap — that carries the template files, parameters, and settings for the target platform. Mustache-rendered templates are written using the parameters stored in config.
stage is called automatically by bundle, but can be used directly when you need to inspect or modify the staging directory before compression and signing.
Staged layout by format
MSIX
Assets/— application icons (generated from source or copied verbatim if already a directory)AppxManifest.xml— rendered package manifestresources.pri— package resource indexMsix.AppInstaller.Data/MSIXAppInstallerData.xml— rendered installer configuration
DMG
Contents/Resources/icon.icns— application iconContents/Info.plist— rendered application metadataContents/MacOS/<app-name>(optional) — native launcher whenmain_launcheris set
When dsstore = true, also writes into the parent of destination:
Applications— symlink to/Applicationsfor drag-and-drop installation.DS_Store— custom Finder window appearance
Snap
meta/icon.png— application iconmeta/snap.yaml— rendered Snap package metadatameta/gui/<app-name>.desktop— rendered desktop launchermeta/hooks/configure(optional) — configuration hook whenconfigure_hookis setbin/<app-name>(optional) — main launcher script whenmain_launcheris set
Examples
stage(MSIX(app_dir), "build/msix_staging")
stage(DMG(app_dir), "build/MyApp.app"; dsstore = true)
stage(Snap(app_dir), "build/snap_staging")AppBundler.bundle — Method
bundle(setup::Function, config, destination::String; force=false, [password=""])Stage, populate, and optionally compress an application bundle for distribution.
config is a format-specific configuration object — MSIX (Windows), DMG (macOS), or Snap (Linux) — and destination is the path of the final artifact (e.g. "MyApp.msix", "MyApp.dmg", "MyApp.snap") or an uncompressed staging directory.
The function follows three steps:
- Stage — writes platform metadata and directory structure into a staging area via
stage. - Setup — calls
setup(staging_dir), where you copy or compile the application files that should be included in the bundle. - Pack — when
config.compressistrue(the default whendestinationcarries the format extension), compresses the staging area into the final artifact and performs code signing.
Set force = true to overwrite an existing destination path.
Code signing
MSIX and DMG sign the bundle automatically during the pack step. Pass the certificate password via the password keyword argument (defaults to ""). When config.selfsign is true, a temporary self-signed certificate is generated instead of using the one in the configuration. DMG entitlements are rendered from the template stored in the configuration. Snap packages are not signed locally; they are verified by the Snap Store after upload.
Examples
bundle(MSIX(app_dir), "MyApp.msix") do staging_dir
# copy or compile application files into staging_dir
end
bundle(DMG(app_dir), "MyApp.dmg") do staging_dir
# copy or compile application files into staging_dir
end
bundle(Snap(app_dir), "MyApp.snap") do staging_dir
# copy or compile application files into staging_dir
endAppBundler.bundle — Method
bundle(product, packaging, destination; force=false, [password=""])Bundle a Julia application product into a platform-specific package at destination by calling bundle(setup, config, destination) with product staged as the setup step.
Arguments
product: The application artifact to bundle. Either aJuliaImgBundleor aJuliaCBundlepackaging: The target package format. One of:DMG— macOS disk image (.dmg). Must be built on macOS.Snap— Linux Snap package. Must be built on Linux.MSIX— Windows app package (.msix). Must be built on Windows.
destination: Output path for the produced package file.
Keyword Arguments
force=false: Overwrite an existing package atdestinationiftrue.password="": Code-signing certificate password. Applicable toDMGandMSIXtargets only.
A warning is emitted when the host OS does not match the target platform, as cross-platform packaging is not supported.
See also stage, bundle(setup, config, destination).