@@ -41,30 +41,124 @@ apply plugin: 'com.palantir.jdks.latest'
4141group = ' com.palantir.onb'
4242version gitVersion()
4343
44+ def javafxVersion = " 21.0.9"
45+ def javafxModules = [' base' , ' controls' , ' fxml' , ' graphics' ]
46+
47+ // Per-platform configs hold the classifier-specific native jars that get folded
48+ // into the shadow jar. Attributes are required: openjfx publishes Gradle Module
49+ // Metadata with multiple variants (winRuntime/linuxRuntime/macRuntime/etc.) and
50+ // without these, Gradle can't pick one and resolution fails with an ambiguity
51+ // error. The classifier in the coordinate pins the file *after* variant
52+ // selection; attributes drive the variant selection itself.
53+ //
54+ // Apple Silicon only on macOS. Intel Macs (pre-2020) are unsupported; users on
55+ // those machines can build from source against the :mac classifier themselves.
56+ configurations {
57+ javafxWin {
58+ attributes {
59+ attribute(Usage . USAGE_ATTRIBUTE , objects. named(Usage , Usage . JAVA_RUNTIME ))
60+ attribute(OperatingSystemFamily . OPERATING_SYSTEM_ATTRIBUTE , objects. named(OperatingSystemFamily , ' windows' ))
61+ attribute(MachineArchitecture . ARCHITECTURE_ATTRIBUTE , objects. named(MachineArchitecture , ' x86-64' ))
62+ }
63+ }
64+ javafxLinux {
65+ attributes {
66+ attribute(Usage . USAGE_ATTRIBUTE , objects. named(Usage , Usage . JAVA_RUNTIME ))
67+ attribute(OperatingSystemFamily . OPERATING_SYSTEM_ATTRIBUTE , objects. named(OperatingSystemFamily , ' linux' ))
68+ attribute(MachineArchitecture . ARCHITECTURE_ATTRIBUTE , objects. named(MachineArchitecture , ' x86-64' ))
69+ }
70+ }
71+ javafxMacAarch64 {
72+ attributes {
73+ attribute(Usage . USAGE_ATTRIBUTE , objects. named(Usage , Usage . JAVA_RUNTIME ))
74+ attribute(OperatingSystemFamily . OPERATING_SYSTEM_ATTRIBUTE , objects. named(OperatingSystemFamily , ' macos' ))
75+ attribute(MachineArchitecture . ARCHITECTURE_ATTRIBUTE , objects. named(MachineArchitecture , ' aarch64' ))
76+ }
77+ }
78+ }
79+
80+ def addJavafx = { String classifier , String configName ->
81+ javafxModules. each { m ->
82+ dependencies. add(configName, " org.openjfx:javafx-${ m} :${ javafxVersion} :${ classifier} " )
83+ }
84+ }
85+ addJavafx(' win' , ' javafxWin' )
86+ addJavafx(' linux' , ' javafxLinux' )
87+ addJavafx(' mac-aarch64' , ' javafxMacAarch64' )
88+
4489jar {
4590 manifest {
4691 attributes ' Main-Class' : ' com.palantir.onb.Core'
4792 }
4893}
4994
95+ shadowJar {
96+ archiveClassifier. set(' all' )
97+ manifest {
98+ attributes ' Main-Class' : ' com.palantir.onb.Core'
99+ }
100+ mergeServiceFiles()
101+
102+ // Reproducible builds: same git SHA -> byte-identical jar
103+ preserveFileTimestamps = false
104+ reproducibleFileOrder = true
105+
106+ // Bundle natives for every supported platform. The openjfx plugin is
107+ // configured with `configuration = 'compileOnly'` below so runtimeClasspath
108+ // does NOT contain host-arch natives — output is independent of build host.
109+ configurations = [
110+ project. configurations. runtimeClasspath,
111+ project. configurations. javafxWin,
112+ project. configurations. javafxLinux,
113+ project. configurations. javafxMacAarch64,
114+ ]
115+ }
116+
117+ // Fail the build if shadowJar is missing natives for any supported platform.
118+ // Catches future regressions (renamed config, wrong classifier, etc.) at build
119+ // time rather than at a user's desktop.
120+ tasks. register(' verifyShadowJarNatives' ) {
121+ dependsOn shadowJar
122+ def jarProvider = shadowJar. archiveFile
123+ doLast {
124+ def jar = jarProvider. get(). asFile
125+ def entries = new java.util.zip.ZipFile (jar). withCloseable { zf ->
126+ zf. entries(). collect { it. name }
127+ }
128+ def checks = [
129+ win : entries. any { it. endsWith(' glass.dll' ) },
130+ linux : entries. any { it. endsWith(' libglass.so' ) },
131+ macAarch64 : entries. any { it. endsWith(' libglass.dylib' ) },
132+ ]
133+ def missing = checks. findAll { ! it. value }. collect { it. key }
134+ if (missing) {
135+ throw new GradleException (" shadowJar is missing JavaFX natives for: ${ missing} . " +
136+ " Check the per-platform configurations and openjfx plugin setup." )
137+ }
138+ logger. lifecycle(" shadowJar natives present for: ${ checks.keySet().join(', ')} " )
139+ }
140+ }
141+ tasks. named(' build' ) { dependsOn ' verifyShadowJarNatives' }
142+
50143java {
51144 modularity. inferModulePath = true
52145}
53146
54147// Generate version.properties file from git tags
55148tasks. register(' generateVersionProperties' ) {
56- def outputDir = file( " ${ buildDir } / generated/resources" )
149+ def outputDir = layout . buildDirectory . dir( ' generated/resources' )
57150 outputs. dir outputDir
151+ def versionString = project. version. toString()
58152 doLast {
59- outputDir. mkdirs()
60- def propsFile = file( " ${ outputDir } /version.properties " )
61- propsFile . text = " version=${ project.version } \n "
153+ def dir = outputDir. get() . asFile
154+ dir . mkdirs( )
155+ new File (dir, ' version.properties ' ) . text = " version=${ versionString } \n "
62156 }
63157}
64158
65159processResources {
66160 dependsOn generateVersionProperties
67- from(" ${ buildDir } / generated/resources" ) {
161+ from(layout . buildDirectory . dir( ' generated/resources' ) ) {
68162 include ' version.properties'
69163 }
70164}
@@ -114,12 +208,13 @@ dependencyUpdates {
114208 }
115209}
116210
211+ // `configuration = 'compileOnly'` keeps JavaFX natives off runtimeClasspath
212+ // so the shadow jar's contents are a pure function of the explicit per-platform
213+ // configs above — not of whatever host architecture happened to run gradle.
117214javafx {
118- version = " 21.0.9"
119- modules = [
120- ' javafx.controls' ,
121- ' javafx.fxml'
122- ]
215+ version = javafxVersion
216+ modules = [' javafx.controls' , ' javafx.fxml' ]
217+ configuration = ' compileOnly'
123218}
124219
125220tasks. withType(Test ) {
@@ -172,19 +267,20 @@ def tomcatVersion = '10.1.49'
172267dependencies {
173268 implementation group : ' com.palantir.isofilereader' , name : ' isofilereader' , version : ' 0.6.2'
174269
175- // For dependencies
176270 implementation ' com.google.code.gson:gson:2.13.2'
177271
178- implementation group : ' org.apache.tomcat.embed' , name : ' tomcat-embed-core' , version : " $tomcatVersion "
272+ implementation group : ' org.apache.tomcat.embed' , name : ' tomcat-embed-core' , version : " $tomcatVersion "
179273 implementation group : ' org.apache.tomcat.embed' , name : ' tomcat-embed-jasper' , version : " $tomcatVersion "
180- implementation group : ' org.apache.tomcat' , name : ' tomcat-jasper' , version : " $tomcatVersion "
181- implementation group : ' org.apache.tomcat' , name : ' tomcat-jasper-el' , version : " $tomcatVersion "
182- implementation group : ' org.apache.tomcat' , name : ' tomcat-jsp-api' , version : " $tomcatVersion "
274+ implementation group : ' org.apache.tomcat' , name : ' tomcat-jasper' , version : " $tomcatVersion "
275+ implementation group : ' org.apache.tomcat' , name : ' tomcat-jasper-el' , version : " $tomcatVersion "
276+ implementation group : ' org.apache.tomcat' , name : ' tomcat-jsp-api' , version : " $tomcatVersion "
183277
184278 testImplementation(' io.github.glytching:junit-extensions:2.6.0' )
185279 testImplementation(' org.hamcrest:hamcrest-integration:1.3' )
186280 testImplementation(platform(' org.junit:junit-bom:6.0.1' ))
187281 testImplementation(' org.junit.jupiter:junit-jupiter' )
282+
283+ // JavaFX per-platform natives are wired up earlier via addJavafx(...).
188284}
189285
190286test {
0 commit comments