Skip to content

Commit a4ad554

Browse files
committed
cache friendly deploy scripts
1 parent 13fc823 commit a4ad554

2 files changed

Lines changed: 122 additions & 101 deletions

File tree

de.peeeq.wurstscript/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ buildscript {
33
dependencies {
44
// used at configuration-time for version info
55
classpath 'org.eclipse.jgit:org.eclipse.jgit:5.7.+'
6+
classpath "javax.inject:javax.inject:1"
67
}
78
}
89

de.peeeq.wurstscript/deploy.gradle

Lines changed: 121 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import org.gradle.internal.os.OperatingSystem
2+
import javax.inject.Inject
23

34
// ----------------------- Publishing (kept from your original) -----------------------
45
publishing {
@@ -17,147 +18,171 @@ publishing {
1718
}
1819
}
1920

21+
// ----------------------- Common providers/locations ---------------------------------
22+
def os = OperatingSystem.current()
23+
def arch = System.getProperty("os.arch") // "amd64"/"x86_64"/"aarch64"
2024

21-
def toolJavaHome = {
22-
def svc = project.extensions.getByType(JavaToolchainService)
23-
def launcher = svc.launcherFor(project.java.toolchain)
24-
launcher.get().metadata.installationPath.asFile
25-
}
26-
27-
def toolExecutable = { String toolName ->
28-
def javaHome = System.getenv('JAVA_HOME') ? new File(System.getenv('JAVA_HOME')) : toolJavaHome()
29-
def ext = OperatingSystem.current().isWindows() ? ".exe" : ""
30-
new File(javaHome, "bin/${toolName}${ext}").absolutePath
31-
}
25+
def plat = os.isWindows() ? "win-x64"
26+
: os.isMacOsX() ? (arch == "aarch64" ? "macos-arm64" : "macos-x64")
27+
: "linux-x64"
3228

33-
34-
// ----------------------- Common providers/locations ---------------------------------
35-
def fatJar = tasks.named('shadowJar').flatMap { it.archiveFile }
29+
// Capture project version at configuration time (avoid Task.project at execution)
30+
def versionString = project.version.toString()
3631

3732
// Allow adding modules if reflection/ServiceLoader pulls them in:
3833
// ./gradlew jlinkRuntime25 -PextraJdkModules=java.desktop,jdk.crypto.ec
39-
def extraJdkModules = (project.findProperty("extraJdkModules") ?: "").toString().trim()
34+
def extraJdkModulesProvider = providers.gradleProperty("extraJdkModules").orElse("")
4035

41-
def jlinkWorkDir = layout.buildDirectory.dir("jlink")
42-
def modulesTxt = jlinkWorkDir.map { it.file("modules.txt") }
43-
def jreImageDir = layout.buildDirectory.dir("jre-wurst-25")
36+
def fatJar = tasks.named('shadowJar').flatMap { it.archiveFile }
4437

45-
def os = OperatingSystem.current()
46-
def arch = System.getProperty("os.arch") // "amd64"/"x86_64"/"aarch64"
47-
def plat = os.isWindows() ? "win-x64"
48-
: os.isMacOsX() ? (arch == "aarch64" ? "macos-arm64" : "macos-x64")
49-
: "linux-x64"
38+
def jlinkWorkDir = layout.buildDirectory.dir("jlink")
39+
def modulesRawTxt = jlinkWorkDir.map { it.file("modules.raw.txt") }
40+
def modulesTxt = jlinkWorkDir.map { it.file("modules.txt") }
41+
def jreImageDir = layout.buildDirectory.dir("jre-wurst-25")
5042

5143
def distRoot = layout.buildDirectory.dir("dist/slim-${plat}")
5244
def releasesDir = layout.buildDirectory.dir("releases")
5345

46+
// ----------------------- Toolchain / tool paths (providers) -------------------------
47+
def toolchainSvc = extensions.getByType(JavaToolchainService)
48+
def launcherProvider = toolchainSvc.launcherFor(java.toolchain)
49+
50+
// Prefer JAVA_HOME if set, otherwise toolchain
51+
def javaHomeProvider =
52+
providers.environmentVariable("JAVA_HOME")
53+
.map { new File(it) } // <-- no file()
54+
.orElse(launcherProvider.map { it.metadata.installationPath.asFile })
55+
56+
def jdepsPathProvider = javaHomeProvider.map { home ->
57+
new File(home, "bin/jdeps${os.isWindows() ? '.exe' : ''}").absolutePath
58+
}
59+
def jlinkPathProvider = javaHomeProvider.map { home ->
60+
new File(home, "bin/jlink${os.isWindows() ? '.exe' : ''}").absolutePath
61+
}
62+
def jmodsDirProvider = javaHomeProvider.map { home ->
63+
new File(home, "jmods").absolutePath
64+
}
65+
5466
// ----------------------- Tasks: jdeps → jlink → assemble → package ------------------
5567

56-
// 1) Detect JDK modules via jdeps (from the fat jar)
57-
tasks.register("jdepsModules") {
58-
description = "Detects required JDK modules for the fat compiler.jar via jdeps"
59-
group = "distribution"
68+
// 1) Run jdeps and capture raw module deps
6069

61-
inputs.file(fatJar)
62-
outputs.file(modulesTxt)
70+
abstract class JdepsRawTask extends DefaultTask {
71+
@Inject abstract ExecOperations getExecOps()
6372

64-
doLast {
65-
ExecOperations execOps = project.services.get(ExecOperations)
66-
def jdeps = toolExecutable("jdeps")
67-
def outBuf = new ByteArrayOutputStream()
73+
@InputFile
74+
abstract RegularFileProperty getInputJar()
6875

69-
modulesTxt.get().asFile.parentFile.mkdirs()
76+
@Input
77+
abstract Property<String> getJdepsPath()
78+
79+
@OutputFile
80+
abstract RegularFileProperty getOutFile()
7081

82+
@TaskAction
83+
void runJdeps() {
84+
outFile.get().asFile.parentFile.mkdirs()
85+
86+
def outBuf = new ByteArrayOutputStream()
7187
execOps.exec {
72-
commandLine jdeps,
73-
"--multi-release", "25",
88+
executable = jdepsPath.get()
89+
args "--multi-release", "25",
7490
"--ignore-missing-deps",
7591
"--print-module-deps",
76-
fatJar.get().asFile.absolutePath
92+
"--ignore-missing-deps",
93+
inputJar.get().asFile.absolutePath
7794
standardOutput = outBuf
7895
}
96+
outFile.get().asFile.text = outBuf.toString("UTF-8").trim() + "\n"
97+
}
98+
}
99+
100+
def fatJarProvider = tasks.named("shadowJar").flatMap { it.archiveFile } // Provider<RegularFile>
79101

80-
def detected = outBuf.toString().trim()
81-
if (detected.isEmpty()) detected = "java.base"
102+
tasks.register("jdepsRaw", JdepsRawTask) {
103+
group = "distribution"
104+
description = "Runs jdeps and writes raw module deps to a file."
105+
dependsOn("shadowJar")
106+
107+
// IMPORTANT: RegularFileProperty expects a Provider<RegularFile>
108+
inputJar.set(fatJarProvider)
109+
110+
jdepsPath.set(jdepsPathProvider) // Provider<String>
111+
outFile.set(modulesRawTxt) // Provider<RegularFile>
112+
}
113+
114+
115+
// 1b) Normalize jdeps output into modules.txt (add jdwp + extra modules)
116+
tasks.register("jdepsModules") {
117+
description = "Detects required JDK modules for the fat compiler.jar via jdeps"
118+
group = "distribution"
119+
120+
dependsOn("jdepsRaw")
121+
122+
inputs.file(modulesRawTxt)
123+
inputs.property("extraJdkModules", extraJdkModulesProvider)
124+
outputs.file(modulesTxt)
125+
126+
// capture provider values at configuration time (avoid Task.project at execution)
127+
def extraMods = extraJdkModulesProvider.get().toString().trim()
128+
129+
doLast {
130+
def raw = modulesRawTxt.get().asFile.text.trim()
131+
def detected = raw.isEmpty() ? "java.base" : raw
82132

83133
def jdwpModule = "jdk.jdwp.agent"
84134
if (!detected.split(",").contains(jdwpModule)) {
85135
detected = detected + "," + jdwpModule
86136
}
137+
if (!extraMods.isEmpty()) {
138+
detected = detected + "," + extraMods
139+
}
87140

88-
if (!extraJdkModules.isEmpty()) detected = detected + "," + extraJdkModules
89-
141+
modulesTxt.get().asFile.parentFile.mkdirs()
90142
modulesTxt.get().asFile.text = detected
91143
logger.lifecycle("[jdeps] Using modules: ${detected}")
92144
}
93145
}
94146

95-
// 2) Build slim runtime with jlink (overwrite if exists)
96-
// 2) Build slim runtime with jlink (overwrite if exists)
97-
tasks.register("jlinkRuntime25") {
147+
// 2) Build slim runtime with jlink (output dir must not exist)
148+
tasks.register("jlinkRuntime25", Exec) {
98149
description = "Builds a slim Java 25 runtime image containing only the needed modules"
99150
group = "distribution"
100-
dependsOn("shadowJar", "jdepsModules")
151+
152+
dependsOn("jdepsModules")
101153

102154
inputs.file(modulesTxt)
155+
inputs.property("javaHome", javaHomeProvider)
156+
inputs.property("jmodsDir", jmodsDirProvider)
103157
outputs.dir(jreImageDir)
104158

105-
doLast {
106-
ExecOperations execOps = project.services.get(ExecOperations)
107-
108-
// Resolve Java home from toolchain (fallback to JAVA_HOME if set)
109-
def svc = project.extensions.getByType(JavaToolchainService)
110-
def launcher = svc.launcherFor(project.java.toolchain)
111-
File javaHome = System.getenv('JAVA_HOME') ? new File(System.getenv('JAVA_HOME')) : launcher.get().metadata.installationPath.asFile
112-
113-
def jlink = new File(javaHome, "bin/${OperatingSystem.current().isWindows() ? 'jlink.exe' : 'jlink'}").absolutePath
114-
def jmodsDir = new File(javaHome, "jmods").absolutePath
115-
def mods = modulesTxt.get().asFile.text.trim()
116-
def outDir = jreImageDir.get().asFile
117-
159+
doFirst {
160+
def mods = modulesTxt.get().asFile.text.trim()
118161
if (!mods) throw new GradleException("No modules detected for jlink.")
119162

120-
// jlink requires the output dir to NOT exist
163+
def outDir = jreImageDir.get().asFile
121164
if (outDir.exists()) {
122-
project.delete(outDir)
165+
outDir.deleteDir() // no project access
123166
}
124167
outDir.parentFile.mkdirs()
125168

126-
logger.lifecycle("[jlink] Using: ${jlink}")
127-
logger.lifecycle("[jlink] JAVA_HOME: ${javaHome}")
128-
logger.lifecycle("[jlink] jmods: ${jmodsDir}")
169+
logger.lifecycle("[jlink] Using: ${jlinkPathProvider.get()}")
170+
logger.lifecycle("[jlink] JAVA_HOME: ${javaHomeProvider.get()}")
171+
logger.lifecycle("[jlink] jmods: ${jmodsDirProvider.get()}")
129172
logger.lifecycle("[jlink] Modules: ${mods}")
130173

131-
def errBuf = new ByteArrayOutputStream()
132-
def outBuf = new ByteArrayOutputStream()
133-
134-
def result = execOps.exec {
135-
commandLine jlink,
136-
"--verbose",
137-
"--module-path", jmodsDir,
138-
"--add-modules", mods,
139-
"--no-header-files",
140-
"--no-man-pages",
141-
"--strip-debug",
142-
"--compress=zip-6",
143-
"--output", outDir.absolutePath
144-
errorOutput = errBuf
145-
standardOutput = outBuf
146-
ignoreExitValue = true
147-
}
148-
149-
if (result.exitValue != 0) {
150-
logger.lifecycle("[jlink][stdout]\n${outBuf.toString()}")
151-
logger.error("[jlink][stderr]\n${errBuf.toString()}")
152-
throw new GradleException("jlink failed with exit ${result.exitValue}")
153-
}
154-
155-
logger.lifecycle("[jlink] Runtime created at: ${outDir}")
174+
executable = jlinkPathProvider.get()
175+
args "--verbose",
176+
"--module-path", jmodsDirProvider.get(),
177+
"--add-modules", mods,
178+
"--no-header-files",
179+
"--no-man-pages",
180+
"--strip-debug",
181+
"--compress=zip-6",
182+
"--output", outDir.absolutePath
156183
}
157184
}
158185

159-
160-
161186
// 3) Assemble folder layout: jre + compiler.jar (no manifest)
162187
tasks.register("assembleSlimCompilerDist", Copy) {
163188
description = "Assembles dist folder with slim JRE and compiler.jar (no manifest)."
@@ -173,29 +198,27 @@ tasks.register("assembleSlimCompilerDist", Copy) {
173198
}
174199
}
175200

201+
// Add launch scripts
176202
tasks.named("assembleSlimCompilerDist", Copy) { t ->
177203
if (os.isWindows()) {
178204
from("../Wurstpack/wurstscript/wurstscript.cmd") { into(".") }
179-
from("../Wurstpack/wurstscript/wurstscript") { into(".") }
180-
from("../Wurstpack/wurstscript/grill.cmd") { into(".") }
181-
from("../Wurstpack/wurstscript/grill") { into(".") }
205+
from("../Wurstpack/wurstscript/wurstscript") { into(".") }
206+
from("../Wurstpack/wurstscript/grill.cmd") { into(".") }
207+
from("../Wurstpack/wurstscript/grill") { into(".") }
182208
} else {
183-
from("../Wurstpack/wurstscript/wurstscript") {
184-
into(".")
185-
}
209+
from("../Wurstpack/wurstscript/wurstscript") { into(".") }
186210
}
187211
}
188212

189213
// 4) Package ZIP on all platforms
190214
tasks.register("packageSlimCompilerDistZip", Zip) {
191215
description = "Packages slim dist as a ZIP archive (all platforms)."
192216
group = "distribution"
193-
enabled = true
194217
dependsOn("assembleSlimCompilerDist")
195218

196219
from(distRoot)
197220
destinationDirectory.set(releasesDir)
198-
archiveFileName.set("wurst-compiler-${project.version}-${plat}.zip")
221+
archiveFileName.set("wurst-compiler-${versionString}-${plat}.zip")
199222
}
200223

201224
// OS-aware convenience wrapper now just points to ZIP
@@ -204,6 +227,3 @@ tasks.register("packageSlimCompilerDist") {
204227
group = "distribution"
205228
dependsOn("packageSlimCompilerDistZip")
206229
}
207-
208-
209-

0 commit comments

Comments
 (0)