11import org.gradle.internal.os.OperatingSystem
2+ import javax.inject.Inject
23
34// ----------------------- Publishing (kept from your original) -----------------------
45publishing {
@@ -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
5143def distRoot = layout. buildDirectory. dir(" dist/slim-${ plat} " )
5244def 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)
162187tasks. 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
176202tasks. 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
190214tasks. 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