macOS Targets¶
Nucleus supports two macOS installer formats and universal (fat) binaries.
Formats¶
| Format | Extension | Auto-Update | Sandboxed |
|---|---|---|---|
| DMG | .dmg |
Yes | No |
| PKG | .pkg |
Yes | Yes (App Sandbox) |
General macOS Settings¶
nativeDistributions {
macOS {
// Bundle identifier (reverse DNS notation)
bundleID = "com.example.myapp"
// Dock display name
dockName = "MyApp"
// App Store category
appCategory = "public.app-category.utilities"
// Minimum macOS version
minimumSystemVersion = "12.0"
// Traditional icon
iconFile.set(project.file("icons/app.icns"))
// Layered icon for macOS 26+ (dynamic tilt/depth effects)
layeredIconDir.set(project.file("icons/MyApp.icon"))
// Entitlements
entitlementsFile.set(project.file("entitlements.plist"))
runtimeEntitlementsFile.set(project.file("runtime-entitlements.plist"))
// Custom Info.plist entries (raw XML appended to Info.plist)
infoPlist {
extraKeysRawXml = """
<key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access.</string>
""".trimIndent()
}
}
}
DMG Customization¶
Window Appearance¶
Control the DMG window title, icon sizes, position, and dimensions:
macOS {
dmg {
title = "${productName} ${version}"
iconSize = 128
iconTextSize = 12
window {
x = 400
y = 100
width = 540
height = 380
}
}
}
Background¶
Set a background image or a solid color for the DMG window:
dmg {
background.set(project.file("packaging/dmg-background.png"))
// or use a solid color instead:
// backgroundColor = "#FFFFFF"
}
Format and Badge Icon¶
Choose a DMG format and optionally overlay a badge icon on the volume icon:
dmg {
format = DmgFormat.UDZO // UDRW, UDRO, UDCO, UDZO, UDBZ, ULFO
badgeIcon.set(project.file("icons/badge.icns"))
}
Content Positioning¶
Use content() to place icons at specific coordinates inside the DMG window. The typical pattern is one entry for the app and one entry for an Applications symlink so the user can drag-and-drop to install:
dmg {
content(x = 130, y = 220, type = DmgContentType.File, name = "MyApp.app")
content(x = 410, y = 220, type = DmgContentType.Link, path = "/Applications")
}
Each content() call adds an entry with an (x, y) position and a DmgContentType:
| Type | Description |
|---|---|
DmgContentType.File |
An existing file in the DMG (e.g. the .app bundle). Set name to match the file. |
DmgContentType.Link |
A symlink. Set path to the link target (usually /Applications). |
DmgContentType.Dir |
A directory inside the DMG. |
Mapping from create-dmg
If you are migrating from a create-dmg shell script, the content() DSL maps directly to the --icon and --app-drop-link flags:
create-dmg flag |
Nucleus equivalent |
|---|---|
--icon "MyApp.app" 130 220 |
content(x = 130, y = 220, type = DmgContentType.File, name = "MyApp.app") |
--app-drop-link 410 220 |
content(x = 410, y = 220, type = DmgContentType.Link, path = "/Applications") |
Layered Icons (macOS 26+)¶
macOS 26 introduced layered icons that support dynamic tilt and depth effects on the Dock and Spotlight.
macOS {
// Traditional icon (fallback for older macOS)
iconFile.set(project.file("icons/app.icns"))
// Layered icon for macOS 26+
layeredIconDir.set(project.file("icons/MyApp.icon"))
}
Creating a .icon directory¶
A .icon directory contains an icon.json manifest and image assets:
Create one using Xcode 26+ or Apple Icon Composer:
- Open Xcode, create or open an Asset Catalog
- Add a new App Icon asset
- Configure layers (front, back)
- Export the
.icondirectory
Requirements:
- Xcode Command Line Tools with actool 26.0+
- Only effective on macOS build hosts
- If actool is missing, a warning is logged and the build continues without layered icons
macOS 26 Window Appearance (Liquid Glass)¶
macOS 26 introduces a refreshed window chrome with Liquid Glass: larger traffic light buttons and more rounded window corners. These visual changes are applied automatically by AppKit — but only if the application binary's LC_BUILD_VERSION Mach-O header declares macOS SDK 26.0.
Automatic SDK patching¶
Nucleus automatically patches the app launcher's LC_BUILD_VERSION via vtool so that AppKit enables Liquid Glass. This works with any JDK — a JDK compiled with Xcode 26 is no longer required.
The patching is controlled by the macOsSdkVersion DSL property (defaults to "26.0"):
nucleus {
nativeDistributions {
macOS {
macOsSdkVersion = "26.0" // default — enables Liquid Glass
// macOsSdkVersion = null // disable SDK version patching
}
}
}
What gets patched:
| Task | How |
|---|---|
createDistributable / createSandboxedDistributable |
Launcher patched in the app bundle before signing |
packageDmg / packagePkg |
Derived from the patched distributable |
runDistributable |
Runs the patched app bundle |
run |
Uses a cached patched copy of the JVM binary |
Requirements:
- Xcode Command Line Tools must be installed (
vtoolandcodesignmust be available at/usr/bin/). If missing, a warning is logged and patching is skipped. - Only effective on macOS; ignored on other platforms.
How it works
vtool modifies the LC_BUILD_VERSION load command in the Mach-O binary, setting the SDK version to 26.0. This is the same header that the linker writes when you compile with -sdk_version 26.0. The modification only affects metadata — no code is changed. For distributable builds, the launcher is patched before signing, so the code signature covers the patched binary. For the run task, a patched copy of the JVM is cached at ~/Library/Caches/nucleus/patched-jvm/ and invalidated when the source JDK changes.
GraalVM Native Image¶
For applications compiled with GraalVM Native Image, the native binary is linked directly by the system toolchain. Select Xcode 26 before building:
- name: Select Xcode 26
if: runner.os == 'macOS'
run: sudo xcode-select -s /Applications/Xcode_26.0.app/Contents/Developer
- name: Build GraalVM native image
run: ./gradlew :myapp:packageGraalvmNative --no-daemon
No custom JDK is needed at runtime since the output is a standalone native binary. Xcode 26 at build time is sufficient. The macOsSdkVersion patching does not apply to GraalVM native images — they get the SDK version from the linker directly.
Universal Binaries¶
Nucleus supports creating universal (fat) macOS binaries that run natively on both Apple Silicon and Intel. This requires building on both architectures and merging with lipo.
See CI/CD for the GitHub Actions workflow.
App Sandbox (PKG)¶
PKG targets automatically use the sandboxed build pipeline. The plugin extracts native libraries from JARs, signs them individually, and injects JVM arguments so all native code loads from signed, pre-extracted locations.
Default sandbox entitlements grant network access and user-selected file access. Override them for additional capabilities:
macOS {
entitlementsFile.set(project.file("packaging/sandbox-entitlements.plist"))
runtimeEntitlementsFile.set(project.file("packaging/sandbox-runtime-entitlements.plist"))
}
For Mac App Store builds (PKG), add provisioning profiles:
macOS {
provisioningProfile.set(project.file("packaging/MyApp.provisionprofile"))
runtimeProvisioningProfile.set(project.file("packaging/MyApp_Runtime.provisionprofile"))
}
Note
PKG is always treated as an App Store format. Sandbox entitlements, "3rd Party Mac Developer"
certificates, and productsign signing are applied automatically — no appStore flag needed.
See Sandboxing for full details.
Signing & Notarization¶
See Code Signing for full details.
macOS {
signing {
sign.set(true)
identity.set("Developer ID Application: My Company (TEAMID)")
keychain.set("/path/to/keychain.keychain-db")
}
notarization {
appleID.set("dev@example.com")
password.set("@keychain:AC_PASSWORD")
teamID.set("TEAMID")
}
}
Installation Path¶
The installationPath property controls where the application is installed on disk. It defaults to /Applications.
- PKG installers — passed as the
installLocationto electron-builder and toproductbuildfor App Store builds. When the user chooses the local system domain during installation, the app is placed ininstallationPath(e.g./Applications). When a home directory installation is chosen, the app is placed in$HOME/Applicationsinstead. - DMG — used as the symlink target in the native DMG builder, so the drag-and-drop arrow points to the correct directory.
macOS {
// Default — installs into /Applications
installationPath = "/Applications"
// Custom — installs into /Applications/MyCompany
installationPath = "/Applications/MyCompany"
}
Note
This property is macOS-only. Windows and Linux installers do not use it.
Full macOS DSL Reference¶
macOS { }¶
| Property | Type | Default | Description |
|---|---|---|---|
iconFile |
RegularFileProperty |
— | .icns icon file |
bundleID |
String? |
null |
macOS bundle identifier |
dockName |
String? |
null |
Name displayed in the Dock |
setDockNameSameAsPackageName |
Boolean |
true |
Use packageName as dock name |
appCategory |
String? |
null |
App Store / Finder category |
appStore |
Boolean |
false |
Deprecated — PKG is always built for the App Store. This property is ignored. |
minimumSystemVersion |
String? |
null |
Minimum macOS version |
layeredIconDir |
DirectoryProperty |
— | .icon directory for macOS 26+ |
packageName |
String? |
null |
Override package name |
packageVersion |
String? |
null |
Override version |
packageBuildVersion |
String? |
null |
CFBundleVersion |
dmgPackageVersion |
String? |
null |
DMG-specific version |
dmgPackageBuildVersion |
String? |
null |
DMG-specific build version |
pkgPackageVersion |
String? |
null |
PKG-specific version |
pkgPackageBuildVersion |
String? |
null |
PKG-specific build version |
entitlementsFile |
RegularFileProperty |
— | Entitlements plist |
runtimeEntitlementsFile |
RegularFileProperty |
— | Runtime entitlements plist |
provisioningProfile |
RegularFileProperty |
— | Provisioning profile |
runtimeProvisioningProfile |
RegularFileProperty |
— | Runtime provisioning profile |
installationPath |
String? |
/Applications |
The install location used by PKG installers and as the DMG symlink target (see below) |
macOS { signing { } }¶
| Property | Type | Default | Description |
|---|---|---|---|
sign |
Property<Boolean> |
false |
Enable code signing |
identity |
Property<String> |
— | Signing identity |
keychain |
Property<String> |
— | Keychain path |
prefix |
Property<String> |
— | Signing prefix |
macOS { notarization { } }¶
| Property | Type | Default | Description |
|---|---|---|---|
appleID |
Property<String> |
— | Apple ID email |
password |
Property<String> |
— | App-specific password |
teamID |
Property<String> |
— | Developer Team ID |
macOS { dmg { } }¶
| Property | Type | Default | Description |
|---|---|---|---|
title |
String? |
null |
DMG window title |
iconSize |
Int? |
null |
Icon size in DMG window |
iconTextSize |
Int? |
null |
Icon text size |
format |
DmgFormat? |
null |
DMG format enum (UDZO, UDBZ, etc.) |
size |
String? |
null |
DMG size |
shrink |
Boolean? |
null |
Shrink DMG |
sign |
Boolean |
false |
Sign the DMG |
background |
RegularFileProperty |
— | Background image |
backgroundColor |
String? |
null |
Background color (hex) |
icon |
RegularFileProperty |
— | DMG volume icon |
badgeIcon |
RegularFileProperty |
— | Badge overlay icon |
DmgFormat Enum¶
UDRW (read/write), UDRO (read-only), UDCO (ADC compressed), UDZO (zlib compressed), UDBZ (bzip2), ULFO (lzfse)
dmg { window { } }¶
| Property | Type | Default | Description |
|---|---|---|---|
x |
Int? |
null |
Window x position on screen |
y |
Int? |
null |
Window y position on screen |
width |
Int? |
null |
Window width |
height |
Int? |
null |
Window height |
dmg { content() }¶
Adds an icon entry to the DMG window layout. Call multiple times to position several items.
fun content(
x: Int,
y: Int,
type: DmgContentType? = null,
name: String? = null,
path: String? = null,
)
| Parameter | Type | Required | Description |
|---|---|---|---|
x |
Int |
Yes | Horizontal position inside the DMG window |
y |
Int |
Yes | Vertical position inside the DMG window |
type |
DmgContentType? |
No | Kind of content entry (File, Link, or Dir) |
name |
String? |
No | File name to match (used with File / Dir) |
path |
String? |
No | Target path (used with Link, e.g. /Applications) |
DmgContentType Enum¶
| Value | Serialized ID | Description |
|---|---|---|
Link |
link |
A symlink to a target path |
File |
file |
An existing file in the DMG |
Dir |
dir |
A directory in the DMG |