11import org.apache.tools.ant.taskdefs.condition.Os
22
3+ import org.tukaani.xz.LZMA2Options
4+ import org.tukaani.xz.XZOutputStream
5+
6+ import java.nio.file.Files
7+ buildscript {
8+ dependencies {
9+ classpath ' org.tukaani:xz:1.10'
10+ }
11+ }
12+
313plugins {
414 id ' multiloader-common'
515 id ' net.neoforged.moddev'
616}
717
18+ def mac (arch ) {
19+ [triple : " ${ arch} -apple-darwin" , arch : arch, os : " macos" , ext : " dylib" , lib : true ]
20+ }
21+ def linux (arch ) {
22+ [triple : " ${ arch} -unknown-linux-gnu" , arch : arch, os : " linux" , ext : " so" , lib : true ]
23+ }
24+ def freebsd (arch ) {
25+ [triple : " ${ arch} -unknown-freebsd" , arch : arch, os : " freebsd" , ext : " .so" , lib : true ]
26+ }
27+ def windows (arch ) {
28+ [triple : " ${ arch} -pc-windows-msvc" , arch : arch, os : " windows" , ext : " dll" , lib : false ]
29+ }
30+
31+ def supportedTargets = [
32+ mac(" x86_64" ),
33+ mac(" aarch64" ),
34+ linux(" x86_64" ),
35+ linux(" aarch64" ),
36+ windows(" x86_64" ),
37+ windows(" aarch64" ),
38+ freebsd(" x86_64" ),
39+ // freebsd("aarch64"),
40+ ]
41+ def pinnedRustVersion = ' nightly-2026-01-29'
842def rustRootDir = file(" src/main/rust" )
943def rustProjectDir = file(" $rustRootDir /rapier" )
1044def nativesDir = file(" src/main/resources/natives/sable_rapier" )
1145def cargoCacheDir = project. layout. buildDirectory. file(" cargo" ). get(). asFile
1246def docker = Os . isFamily(Os . FAMILY_MAC ) ? " /usr/local/bin/docker" : " docker"
13- def dockerCommand = [
47+ def xWinImage = ' sable-build-xwin'
48+ def zigbuildImage = ' sable-build-zigbuild'
49+
50+ def baseDockerCommand = [
1451 docker,
1552 ' run' ,
1653 ' --rm' ,
@@ -22,8 +59,9 @@ def dockerCommand = [
2259 " $cargoCacheDir /registry:/usr/local/cargo/registry" ,
2360 ' -w' ,
2461 ' /io/rapier' ,
25- ' sable-build'
2662]
63+ project. ext. zigbuildCargo = [baseDockerCommand, zigbuildImage, ' cargo' ]. flatten()
64+ project. ext. xWinCargo = [baseDockerCommand, xWinImage, ' cargo' ]. flatten()
2765
2866tasks. register(' createContainersDirectory' ) {
2967 doLast {
@@ -33,116 +71,112 @@ tasks.register('createContainersDirectory') {
3371 }
3472}
3573
36- tasks. register(' buildImage' , Exec ) {
74+
75+ tasks. register(' buildZigbuildImage' , Exec ) {
3776 group = ' rust'
3877 workingDir rustRootDir
39- commandLine docker, ' build' , ' -t' , ' sable-build ' , ' . '
78+ commandLine docker, ' build' , ' -t' , zigbuildImage, ' container/zigbuild ' , ' --build-arg ' , " RUST_VERSION= ${ pinnedRustVersion } "
4079 dependsOn createContainersDirectory
4180}
42-
43- tasks. register(' cleanRust' , Exec ) {
81+ tasks. register(' buildXWinImage' , Exec ) {
4482 group = ' rust'
45- workingDir rustProjectDir
46- commandLine dockerCommand
47- args ' cargo' , ' clean'
83+ workingDir rustRootDir
84+ commandLine docker, ' build' , ' -t' , xWinImage, ' container/xwin' , ' --build-arg' , " RUST_VERSION=${ pinnedRustVersion} "
4885 dependsOn createContainersDirectory
4986}
5087
51- tasks. register(' compileRust ' , Exec ) {
88+ tasks. register(' buildImages ' ) {
5289 group = ' rust'
53- workingDir rustProjectDir
54- commandLine dockerCommand
55- args ' cargo' , ' zigbuild'
56- dependsOn createContainersDirectory
57- finalizedBy copyRustNativesDev
90+ dependsOn buildZigbuildImage, buildXWinImage
5891}
5992
60- tasks. register(' compileRustMacAArch64 ' , Exec ) {
93+ tasks. register(' cleanRust ' , Exec ) {
6194 group = ' rust'
6295 workingDir rustProjectDir
63- commandLine dockerCommand
64- args ' cargo ' , ' zigbuild ' , ' --release ' , ' --target ' , ' aarch64-apple-darwin '
96+ commandLine project . ext . zigbuildCargo // only need to use zigbuild cargo since both containers share the same target folder
97+ args ' clean '
6598 dependsOn createContainersDirectory
6699}
67100
68- tasks. register(' compileRustMacX86' , Exec ) {
69- group = ' rust'
70- workingDir rustProjectDir
71- commandLine dockerCommand
72- args ' cargo' , ' zigbuild' , ' --release' , ' --target' , ' x86_64-apple-darwin'
73- dependsOn createContainersDirectory
101+ def compileRustTaskName = { target ->
102+ " compileRust-${ target.os} -${ target.arch} "
74103}
75104
76- tasks. register(' compileRustLinuxAArch64' , Exec ) {
77- group = ' rust'
78- workingDir rustProjectDir
79- commandLine dockerCommand
80- args ' cargo' , ' zigbuild' , ' --release' , ' --target' , ' aarch64-unknown-linux-gnu.2.17'
81- dependsOn createContainersDirectory
82- }
105+ def commandLineForTarget (target ) {
106+ if (target. triple. contains(' msvc' )) {
107+ [project. ext. xWinCargo, ' xwin' , ' build' , ' --release' , ' --target' , target. triple]. flatten()
108+ // support meme platforms, this does nothing by default because the target is disabled
109+ } else if (target. triple == ' aarch64-unknown-freebsd' ) {
110+ [project. ext. zigbuildCargo, ' zigbuild' , ' -Z' , ' build-std' , ' --release' , ' --target' , target. triple]. flatten()
111+ } else {
112+ [project. ext. zigbuildCargo, ' zigbuild' , ' --release' , ' --target' , target. triple]. flatten()
113+ }
83114
84- tasks. register(' compileRustLinux' , Exec ) {
85- group = ' rust'
86- workingDir rustProjectDir
87- commandLine dockerCommand
88- args ' cargo' , ' zigbuild' , ' --release' , ' --target' , ' x86_64-unknown-linux-gnu.2.17'
89- dependsOn createContainersDirectory
90115}
91-
92- tasks. register(' compileRustWindows' , Exec ) {
93- group = ' rust'
94- workingDir rustProjectDir
95- commandLine dockerCommand
96- args ' cargo' , ' zigbuild' , ' --release' , ' --target' , ' x86_64-pc-windows-gnu'
97- dependsOn createContainersDirectory
116+ def nativesNameForTarget (target ) {
117+ " sable_rapier_${ target.arch} _${ target.os} .${ target.ext} "
118+ }
119+ supportedTargets. forEach { target ->
120+ tasks. register(compileRustTaskName(target), Exec ) {
121+ group = ' rust'
122+ workingDir rustProjectDir
123+ description = " Cross-compiles natives for the ${ target.triple} target"
124+ commandLine = commandLineForTarget(target)
125+ }
98126}
99127
100128tasks. register(' buildRustNatives' ) {
101129 group = ' build'
102130 description = ' Compiles all Rust natives and moves them to resources.'
103131
104- dependsOn compileRustMacAArch64, compileRustMacX86, compileRustLinux, compileRustLinuxAArch64, compileRustWindows
132+ dependsOn = supportedTargets . stream() . map(compileRustTaskName) . collect()
105133 finalizedBy copyRustNatives
106134}
107135
108136tasks. register(' copyRustNatives' , Copy ) {
109137 group = ' rust'
110-
111138 into nativesDir
112139 mustRunAfter(buildRustNatives)
113140
114- def moves = [
115- [src : " target/aarch64-apple-darwin/release/libsable_rapier.dylib" , dest : " sable_rapier_aarch64_macos.dylib" ],
116- [src : " target/x86_64-apple-darwin/release/libsable_rapier.dylib" , dest : " sable_rapier_x86_64_macos.dylib" ],
117- [src : " target/x86_64-unknown-linux-gnu/release/libsable_rapier.so" , dest : " sable_rapier_x86_64_linux.so" ],
118- [src : " target/aarch64-unknown-linux-gnu/release/libsable_rapier.so" , dest : " sable_rapier_aarch64_linux.so" ],
119- [src : " target/x86_64-pc-windows-gnu/release/sable_rapier.dll" , dest : " sable_rapier_x86_64_windows.dll" ]
120- ]
121-
122- moves. forEach { map ->
123- from(file(" $rustRootDir /${ map.src} " )) {
124- rename { map. dest }
141+ supportedTargets. forEach { target ->
142+ from(file(" $rustRootDir /target/${ target.triple} /release/${ if(target.lib) {"lib"} else {""}} sable_rapier.${ target.ext} " )) {
143+ rename { nativesNameForTarget(target) }
125144 }
126145 }
146+ finalizedBy packRustNatives
127147}
128148
129149tasks. register(' copyRustNativesDev' , Copy ) {
130150 group = ' rust'
151+ into nativesDir
131152
153+ supportedTargets. forEach { target ->
154+ from(file(" $rustRootDir /target/debug/${ if(target.lib) {"lib"} else {""}} sable_rapier.${ target.ext} " )) {
155+ rename { nativesNameForTarget(target) }
156+ }
157+ }
158+ finalizedBy packRustNatives
159+ }
160+ tasks. register(' packRustNatives' , Tar ) {
161+ group = ' rust'
162+ archiveFile. set file(" ${ nativesDir} /sable_rapier_binaries.tar.xz" )
132163 into nativesDir
133- mustRunAfter(compileRust)
134-
135- def moves = [
136- [src : " target/debug/libsable_rapier.dylib" , dest : " sable_rapier_aarch64_macos.dylib" ],
137- [src : " target/debug/libsable_rapier.dylib" , dest : " sable_rapier_x86_64_macos.dylib" ],
138- [src : " target/debug/libsable_rapier.so" , dest : " sable_rapier_aarch64_linux.so" ],
139- [src : " target/debug/libsable_rapier.so" , dest : " sable_rapier_x86_64_linux.so" ],
140- [src : " target/debug/sable_rapier.dll" , dest : " sable_rapier_aarch64_windows.dll" ],
141- [src : " target/debug/sable_rapier.dll" , dest : " sable_rapier_x86_64_windows.dll" ]
142- ]
143- moves. forEach { map ->
144- from(file(" $rustRootDir /${ map.src} " )) {
145- rename { map. dest }
164+
165+ mustRunAfter copyRustNatives, copyRustNativesDev
166+ supportedTargets. forEach { target ->
167+ var f = nativesNameForTarget(target);
168+ // No, you can't use rename here in case you were wondering.
169+ from(file(" ${ nativesDir} /${ f} " )) { eachFile { setPath f } }
170+ }
171+ doLast {
172+ byte [] bytes = Files . readAllBytes(getArchiveFile(). get(). asFile. toPath());
173+ try (var f = new FileOutputStream (getArchiveFile(). get(). asFile)) {
174+ try (var x = new XZOutputStream (f, new LZMA2Options (LZMA2Options . PRESET_MAX ))) {
175+ x. write(bytes);
176+ }
177+ }
178+ supportedTargets. forEach { t ->
179+ delete(file(" ${ nativesDir} /${ nativesNameForTarget(t)} " ))
146180 }
147181 }
148182}
0 commit comments