1- import groovy.json.JsonSlurper
2- import org.gradle.kotlin.dsl.support.unzipTo
31import org.jreleaser.model.Active
4- import java.net.URI
52import java.net.http.HttpClient
6- import java.net.http.HttpRequest
7- import java.net.http.HttpResponse
8- import java.nio.file.Files
9- import java.security.DigestInputStream
10- import java.security.MessageDigest
113import java.time.LocalDate
12- import kotlin.io.path.createDirectories
13- import kotlin.io.path.exists
14- import kotlin.io.path.writeText
15- import kotlin.jvm.optionals.getOrNull
164
175plugins {
186 `java- platform`
@@ -142,196 +130,6 @@ jreleaser {
142130 }
143131}
144132
145- tasks.register(" downloadLatestNatives" ) {
146- val token = rootProject.findProperty(" overrungl.native.download.github.token" ) as String?
147- ? : System .getProperty(" OVERRUNGL_NATIVE_DOWNLOAD_GITHUB_TOKEN" )
148- ? : System .getenv(" OVERRUNGL_NATIVE_DOWNLOAD_GITHUB_TOKEN" )
149- val nativesPath = rootDir.resolve(" natives" ).toPath()
150- val nativesInfoPath = nativesPath.resolve(" info" )
151- val nativesInfoETagPath = nativesInfoPath.resolve(" etag.json" )
152- val nativesInfoBindingJsonMap = NativeBinding .entries.associateWith { binding ->
153- nativesInfoPath.resolve(" ${binding.bindingName} .json" )
154- }
155- val nativesZipETagPath = nativesPath.resolve(" etag.json" )
156- val nativesBindingZipMap = NativeBinding .entries.associateWith { binding ->
157- nativesPath.resolve(" ${binding.bindingName} .zip" )
158- }
159-
160- doLast {
161- suggestGitHubToken(token)
162-
163- nativesPath.createDirectories()
164- nativesInfoPath.createDirectories()
165-
166- println (" Downloading latest native library builds" )
167-
168- val httpClient = HttpClient .newBuilder()
169- .followRedirects(HttpClient .Redirect .NORMAL )
170- .build()
171-
172- // https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28
173-
174- // ETag
175- val infoETagMap = mutableMapOf<String , String >()
176- if (nativesInfoETagPath.exists()) {
177- val map = JsonSlurper ().parse(nativesInfoETagPath) as Map <* , * >
178- map.forEach { (k, v) -> infoETagMap[k as String ] = v as String }
179- }
180- val zipETagMap = mutableMapOf<String , String >()
181- if (nativesZipETagPath.exists()) {
182- val map = JsonSlurper ().parse(nativesZipETagPath) as Map <* , * >
183- map.forEach { (k, v) -> zipETagMap[k as String ] = v as String }
184- }
185-
186- // fetch release info
187- NativeBinding .entries.forEach { binding ->
188- println (" Fetching release information for ${binding.bindingName} from ${binding.repo} " )
189-
190- val request = HttpRequest .newBuilder()
191- .uri(URI .create(" https://api.github.com/repos/${binding.repo} /releases/latest" ))
192- .header(" Accept" , " application/vnd.github+json" )
193- .also {
194- if (token != null ) {
195- it.header(" Authorization" , " Bearer $token " )
196- }
197- }
198- .header(" X-GitHub-Api-Version" , " 2022-11-28" )
199- .also {
200- infoETagMap[binding.bindingName].also { eTag ->
201- if (eTag != null && nativesInfoBindingJsonMap[binding]!! .exists()) {
202- it.header(" if-none-match" , " \" $eTag \" " )
203- }
204- }
205- }
206- .build()
207- val response = httpClient.send(request, HttpResponse .BodyHandlers .ofString())
208-
209- if (response.statusCode() == 304 ) {
210- println (" Skipped for ${binding.bindingName} .json because it is not modified" )
211- return @forEach
212- }
213-
214- if (response.statusCode() != 200 ) {
215- System .err.println (" warning: failed to download ${binding.bindingName} .json; response code: ${response.statusCode()} " )
216- return @forEach
217- }
218-
219- val eTag = response.headers().firstValue(" etag" ).getOrNull()
220- if (eTag != null ) {
221- infoETagMap[binding.bindingName] = eTag.substring(1 , eTag.length - 1 )
222- }
223-
224- nativesInfoBindingJsonMap[binding]!! .writeText(response.body())
225- }
226-
227- // cache info ETag
228- println (" Caching release information" )
229- nativesInfoETagPath.writeText(buildString {
230- append(" {" )
231- infoETagMap.entries.joinTo(this , separator = " ," ) {
232- " \" ${it.key} \" :\" ${it.value} \" "
233- }
234- append(" }" )
235- })
236-
237-
238- // download assets
239- val invalidZips = mutableListOf<String >()
240- NativeBinding .entries.forEach { binding ->
241- println (" Downloading asset for ${binding.bindingName} " )
242-
243- val map = JsonSlurper ().parse(nativesInfoBindingJsonMap[binding]!! ) as Map <* , * >
244- val assets = (map[" assets" ] as List <* >).let { it.map { e -> e as Map <* , * > } }
245- val asset = assets.first { it[" name" ] == " ${binding.bindingName} .zip" }
246- val zipPath = nativesBindingZipMap[binding]!!
247-
248- val request = HttpRequest .newBuilder()
249- .uri(URI .create(asset[" url" ] as String ))
250- .header(" Accept" , " application/octet-stream" )
251- .also {
252- if (token != null ) {
253- it.header(" Authorization" , " Bearer $token " )
254- }
255- }
256- .header(" X-GitHub-Api-Version" , " 2022-11-28" )
257- .also {
258- zipETagMap[binding.bindingName].also { eTag ->
259- if (eTag != null && zipPath.exists()) {
260- it.header(" if-none-match" , " \" $eTag \" " )
261- }
262- }
263- }
264- .build()
265- val response = httpClient.send(request, HttpResponse .BodyHandlers .ofInputStream())
266-
267- if (response.statusCode() == 304 ) {
268- println (" Skipped for ${binding.bindingName} .zip because it is not modified" )
269- response.body().close()
270- return @forEach
271- }
272-
273- if (response.statusCode() != 200 ) {
274- System .err.println (" warning: failed to download ${binding.bindingName} .zip; response code: ${response.statusCode()} " )
275- response.body().close()
276- return @forEach
277- }
278-
279- val eTag = response.headers().firstValue(" etag" ).getOrNull()
280- if (eTag != null ) {
281- zipETagMap[binding.bindingName] = eTag.substring(1 , eTag.length - 1 )
282- }
283-
284- Files .newOutputStream(zipPath).buffered().use { os ->
285- response.body().buffered().use { it.transferTo(os) }
286- }
287-
288- // validate
289- println (" Validating ${binding.bindingName} .zip" )
290- val sha256 = (asset[" digest" ] as String? )?.substringAfter(" sha256:" )
291- if (sha256 == null ) {
292- System .err.println (" warning: digest for ${binding.bindingName} .zip is null" )
293- return @forEach
294- }
295- val md = MessageDigest .getInstance(" SHA-256" )
296- DigestInputStream (Files .newInputStream(zipPath), md).use {
297- it.readAllBytes()
298- }
299- val byteArray = sha256.windowed(2 , step = 2 )
300- .map { it.toUByte(16 ).toByte() }
301- .toByteArray()
302- val digest = md.digest()
303- if (! byteArray.contentEquals(digest)) {
304- invalidZips.add(binding.bindingName)
305- }
306- }
307-
308- // cache zip ETag
309- println (" Caching zip information" )
310- nativesZipETagPath.writeText(buildString {
311- append(" {" )
312- zipETagMap.entries.joinTo(this , separator = " ," ) {
313- " \" ${it.key} \" :\" ${it.value} \" "
314- }
315- append(" }" )
316- })
317-
318- if (invalidZips.isNotEmpty()) {
319- throw IllegalStateException (
320- " these files are incomplete or broken; please delete them and rerun this task:\n ${
321- invalidZips.joinToString(separator = " \n " ) { " $it .zip" }
322- } "
323- )
324- }
325-
326-
327- // extract files
328- NativeBinding .entries.forEach { binding ->
329- println (" Extracting ${binding.bindingName} .zip" )
330- unzipTo(nativesPath.toFile(), nativesBindingZipMap[binding]!! .toFile())
331- }
332- }
333- }
334-
335133tasks.register(" updateKhr" ) {
336134 val token = rootProject.findProperty(" overrungl.native.download.github.token" ) as String?
337135 ? : System .getProperty(" OVERRUNGL_NATIVE_DOWNLOAD_GITHUB_TOKEN" )
@@ -352,3 +150,35 @@ tasks.register("updateKhr") {
352150 httpClient.downloadRepoFile(token, " KhronosGroup" , " OpenGL-Registry" , " xml/gl.xml" , glXml)
353151 }
354152}
153+
154+ ext[" lwjglNatives" ] = Pair (
155+ System .getProperty(" os.name" )!! ,
156+ System .getProperty(" os.arch" )!!
157+ ).let { (name, arch) ->
158+ when {
159+ " FreeBSD" .equals(name) ->
160+ " natives-freebsd"
161+
162+ arrayOf(" Linux" , " SunOS" , " Unit" ).any { name.startsWith(it) } ->
163+ if (arrayOf(" arm" , " aarch64" ).any { arch.startsWith(it) })
164+ " natives-linux${if (arch.contains(" 64" ) || arch.startsWith(" armv8" )) " -arm64" else " -arm32" } "
165+ else if (arch.startsWith(" ppc" ))
166+ " natives-linux-ppc64le"
167+ else if (arch.startsWith(" riscv" ))
168+ " natives-linux-riscv64"
169+ else
170+ " natives-linux"
171+
172+ arrayOf(" Mac OS X" , " Darwin" ).any { name.startsWith(it) } ->
173+ " natives-macos${if (arch.startsWith(" aarch64" )) " -arm64" else " " } "
174+
175+ arrayOf(" Windows" ).any { name.startsWith(it) } ->
176+ if (arch.contains(" 64" ))
177+ " natives-windows${if (arch.startsWith(" aarch64" )) " -arm64" else " " } "
178+ else
179+ " natives-windows-x86"
180+
181+ else ->
182+ throw Error (" Unrecognized or unsupported platform. Please set \" lwjglNatives\" manually" )
183+ }
184+ }
0 commit comments