construo
July 21, 2026 · View on GitHub
Construo is a gradle plugin to cross compile JVM apps.
Setup
Add the plugin to your project
Kotlin DSL
plugins {
id("io.github.fourlastor.construo") version "2.2.1"
}
Groovy DSL
plugins {
id "io.github.fourlastor.construo" version "2.2.1"
}
Configuration
General config
These are the base options to set when using construo.
construo {
// name of the executable
name.set("game")
// human-readable name, used for example in the `.app` name for macOS
humanName.set("Game")
// Optional, defaults to application.mainClass or jar task main class
mainClass.set("io.github.fourlastor.gdx.Main")
// Optional, defaults to $buildDir/construo/dist
// where to put the packaged zips
outputDir.set(rootProject.file("dist"))
// Optional, an alternative jar task name to base the build upon
jarTask.set("myJarTaskName")
// Optional, a folder to use as the root in the zip output file
zipFolder.set("game-v1.0.0")
// Optional tasks which must run before every package task
prePackageTasks.addAll("generateAssets")
// Optional local files to include, mapped to their package-relative path
packageFiles.set(mapOf(
"runtime/common.txt" to layout.projectDirectory.file("assets/common.txt"),
))
}
JLink options
You can customize how the minimized image is generated with the jlink block.
construo {
jlink {
// add arbitrary modules to be included when running jlink
modules.addAll("jdk.zipfs")
// guess the modules from the jar using jdeps, defaults to true
guessModulesFromJar.set(false)
// include default crypto modules, defaults to true
includeDefaultCryptoModules.set(false)
}
}
Roast options
Construo uses roast to run the application, a few options can be specified and will be set in its config.json.
construo {
roast {
// Roast release coordinates, defaults to v1.6.0 on GitHub Releases
version.set("v1.6.0")
baseUrl.set("https://github.com/fourlastor-alexandria/roast/releases/download")
// MacOS only, whether to run the jvm on the main thread, defaults to true
runOnFirstThread.set(false)
// use ZGC garbage collector, defaults to true
useZgc.set(false)
// use the main class as the context class loader, defaults to false, useful for compose apps
useMainAsContextClassLoader.set(true)
// vm startup options
vmArgs.addAll("-Xmx1G")
}
}
Defining targets
Targets define the output bundles Construo will generate. Each target must define its architecture and a JDK URL (a JRE is not sufficient). Optional SHA-256 digests can pin the JDK and Roast downloads; when configured, each digest is verified before its archive is extracted.
By default, packaging tools such as jlink and jdeps come from the host JDK for compatibility. Set packagingToolJdk to Target.PackagingToolJdk.TARGET_JDK to run the tools from the downloaded target JDK when its operating system and architecture are executable on the build host. archiveFile can override the complete output ZIP path for a target. A target-specific roastUrl can override the URL derived from the global Roast coordinates.
Windows
Windows targets support only the X86_64 architecture.
The useGpuHint option specifies whether the packaged app will use the discrete GPU in hybrid systems (defaults to true).
The useConsole option specifies whether the packaged app will print output to a terminal (defaults to false). Note that useConsole overrides useGpuHint.
The icon option specifies an icon to be used for the executable, this must be a PNG image.
Macos
Mac-specific options are mainly used to generate key-value pairs for the Info.plist file. For more information see https://developer.apple.com/documentation/bundleresources/information-property-list
The identifier option is mandatory when building an app bundle.
Set appBundle to false to package a raw macOS command-line executable and its runtime at the root of the archive instead of generating a .app bundle. It defaults to true.
Optional but highly recommended are buildNumber and versionNumber. They are both used for the Info.plist file. buildNumber is for CFBundleVersion, versionNumber is for CFBundleShortVersionString
An icon can be optionally specified with macOsIcon on each target.
Human-readable copyright notice can be added with copyright option.
To define app category use categoryName option. For possible values see https://developer.apple.com/documentation/bundleresources/information-property-list/lsapplicationcategorytype
If you need any other key-value pairs in your Info.plist, use additionalInfoFile option. This should point to an xml file containing key-value pairs, formatted as they are in the Info.plist file.
Optional entitlementsFile can be used to pick a file to define entitlements. This file will be copied into the app bundle and renamed to myAppName.entitlements . For possible values see https://developer.apple.com/documentation/bundleresources/entitlements
Kotlin DSL
import io.github.fourlastor.construo.Target
construo {
targets {
create<Target.Linux>("linuxX64") {
architecture.set(Target.Architecture.X86_64)
jdkUrl.set("https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.11_9.tar.gz")
jdkSha256.set("<jdk-sha256>")
roastSha256.set("<roast-sha256>")
}
create<Target.MacOs>("macM1") {
architecture.set(Target.Architecture.AARCH64)
jdkUrl.set("https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_aarch64_mac_hotspot_17.0.11_9.tar.gz")
jdkSha256.set("<jdk-sha256>")
roastSha256.set("<roast-sha256>")
// macOS needs an identifier
identifier.set("io.github.fourlastor.Game")
// Optional but highly recommended; app version number
buildNumber.set("1.0.0")
versionNumber.set("1.0")
// Optional: icon for macOS
macIcon.set(project.file("path/to/mac-icon.icns"))
// Optional: set copyright
copyright.set("© 2025 Fourlastor")
// Optional: set application category
categoryName.set("public.app-category.developer-tools")
// Optional: file to be used as entitlements file.
entitlementsFile.set(project.file("path/to/mac-entitlements.xml"))
// Optional: an xml file containing additional key-value pairs to be added to the Info.plist file
additionalInfoFile.set(project.file("path/to/mac-additional.xml"))
}
create<Target.Windows>("winX64") {
architecture.set(Target.Architecture.X86_64)
jdkUrl.set("https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_windows_hotspot_17.0.11_9.zip")
jdkSha256.set("<jdk-sha256>")
roastSha256.set("<roast-sha256>")
// use executable with GPU hints, defaults to true
useGpuHint.set(false)
// Optional tasks and files used only by this target
prePackageTasks.add("generateWindowsAssets")
packageFiles.set(mapOf(
"runtime/windows.dll" to layout.projectDirectory.file("assets/windows.dll"),
))
}
}
}
Groovy DSL
import io.github.fourlastor.construo.Target
construo {
targets.configure {
create("linuxX64", Target.Linux) {
architecture.set(Target.Architecture.X86_64)
jdkUrl.set("https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.11_9.tar.gz")
jdkSha256.set("<jdk-sha256>")
roastSha256.set("<roast-sha256>")
}
create("macM1", Target.MacOs) {
architecture.set(Target.Architecture.AARCH64)
jdkUrl.set("https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_aarch64_mac_hotspot_17.0.11_9.tar.gz")
jdkSha256.set("<jdk-sha256>")
roastSha256.set("<roast-sha256>")
// macOS needs an identifier
identifier.set("io.github.fourlastor.Game")
// Optional but highly recommended; app version number
def projectVersionNumber = project.properties.getOrDefault("version","1.0.0").toString()
buildNumber.set(projectVersionNumber)
versionNumber.set(projectVersionNumber)
// Optional: icon for macOS
macIcon.set(project.file("path/to/mac-icon.icns"))
// Optional: set copyright
copyright.set("© 2025 Fourlastor")
// Optional: set application category
categoryName.set("public.app-category.developer-tools")
// Optional: file to be used as entitlements file.
entitlementsFile.set(project.file("path/to/mac-entitlements.xml"))
// Optional: an xml file containing additional key-value pairs to be added to the Info.plist file
additionalInfoFile.set(project.file("path/to/mac-additional.xml"))
}
create("winX64", Target.Windows) {
architecture.set(Target.Architecture.X86_64)
jdkUrl.set("https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_windows_hotspot_17.0.11_9.zip")
jdkSha256.set("<jdk-sha256>")
roastSha256.set("<roast-sha256>")
// use executable with GPU hints, defaults to true
useGpuHint.set(false)
// Optional tasks and files used only by this target
prePackageTasks.add("generateWindowsAssets")
packageFiles.set([
"runtime/windows.dll": layout.projectDirectory.file("assets/windows.dll"),
])
}
}
}
Using ProGuard
You can set a ProguardTask as the jarTask name, in that case, you will also have to set mainClass to the main class name (see general config).
Packaging the targets
Each defined target will generate a packageXXX task, where XXX is the capitalized name of the target (for example: packageLinuxX64). Running the task will produce a ZIP inside the outputDir folder containing the fully packaged app, unless that target sets an explicit archiveFile.