construo

July 21, 2026 · View on GitHub

Download

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"),
    ))
}

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.