Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@
"windows": {
"command": ".\\gradlew.bat browserJar; Copy-Item build\\libs\\MazeGame-browser-1.0.0.jar ..\\docs\\MazeGame.jar -Force"
}
},
{
"label": "Build Native Installer",
"type": "shell",
"command": "./gradlew jpackage",
"options": { "cwd": "${workspaceFolder}/MazeGame" },
"problemMatcher": ["$javac"],
"detail": "Create standalone .exe with bundled JRE (no Java needed)"
}
]
}
65 changes: 65 additions & 0 deletions MazeGame/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,71 @@ tasks.register('browserJar', Jar) {
destinationDirectory = layout.buildDirectory.dir('libs')
}

// --- Native installer (jpackage) ---
// Creates a platform-specific installer that bundles a private JRE.
// Recipients do NOT need Java installed.
// Usage:
// ./gradlew jpackage → app-image (folder with .exe / .app)
// ./gradlew jpackage -PinstallerType=msi → MSI installer (Windows, needs WiX 3)
// ./gradlew jpackage -PinstallerType=exe → EXE installer (Windows, needs WiX 3)
// ./gradlew jpackage -PinstallerType=dmg → DMG disk image (macOS)
// ./gradlew jpackage -PinstallerType=deb → DEB package (Linux)
tasks.register('jpackage') {
description = 'Create a native installer that bundles a private JRE'
dependsOn 'installDist'
doLast {
// Resolve jpackage from the toolchain JDK
def javaInstallDir = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(21)
}.get().metadata.installationPath.asFile
def isWindows = System.getProperty('os.name').toLowerCase().contains('win')
def jpackageExec = new File(javaInstallDir, "bin/jpackage${isWindows ? '.exe' : ''}")

def installLib = layout.buildDirectory.dir('install/MazeGame/lib').get().asFile
def outputDir = layout.buildDirectory.dir('installers').get().asFile

// Clean previous output so jpackage doesn't fail on existing directory
def prevOutput = new File(outputDir, 'WesleysWayOut')
if (prevOutput.exists()) {
prevOutput.deleteDir()
}
outputDir.mkdirs()

def type = project.findProperty('installerType') ?: 'app-image'

// Use the favicon.ico from docs/ as the Windows icon
def iconFile = file("${rootProject.projectDir}/docs/favicon.ico")

def args = [
jpackageExec.path,
'--type', type,
'--name', 'WesleysWayOut',
'--app-version', project.version.toString(),
'--input', installLib.path,
'--main-jar', "MazeGame-${project.version}.jar".toString(),
'--main-class', 'mazegame.Start',
'--dest', outputDir.path,
'--description', 'Wesley\'s Way Out - a maze puzzle game',
'--vendor', 'Wesley'
]
if (isWindows && iconFile.exists()) {
args.add('--icon')
args.add(iconFile.path)
}

def pb = new ProcessBuilder(args.collect { it.toString() })
pb.inheritIO()
def process = pb.start()
def exitCode = process.waitFor()
if (exitCode != 0) {
throw new GradleException("jpackage failed with exit code ${exitCode}")
}

println ""
println "Installer created in: ${outputDir.path}"
}
}

// --- Local browser testing ---
// Builds the browser JAR, copies it to docs/, and starts a local HTTP server.
// Usage: ./gradlew runBrowser
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ cd MazeGame
| `gradlew jar` | Build the JAR (output: `build/libs/MazeGame-<version>.jar`) |
| `gradlew browserJar` | Build Java-8 JAR for CheerpJ browser play |
| `gradlew runBrowser` | Build browser JAR, copy to `docs/`, and start local HTTP server |
| `gradlew jpackage` | Create a native app image with bundled JRE (no Java needed to run) |
| `gradlew clean` | Delete all build artifacts |

## Running the JAR Directly
Expand Down Expand Up @@ -146,6 +147,21 @@ cd MazeGame
./gradlew run
```

### Native Installer (jpackage)

Create a standalone desktop app that bundles a private JRE — recipients **don't need Java installed**.

```bash
# Build an app image (folder with .exe / .app)
./gradlew jpackage
# Output: build/installers/WesleysWayOut/

# Build a Windows MSI installer (requires WiX Toolset 3.x)
./gradlew jpackage -PinstallerType=msi
```

The task recompiles sources automatically, so the output always reflects the latest code.

### Browser Build (CheerpJ)

The game runs in the browser via [CheerpJ 3.0](https://cheerpj.com/), which requires Java 8 bytecode.
Expand Down