Skip to content

Commit 8d942d3

Browse files
authored
Merge pull request #137 from Over-Run/lwjgl3
2 parents a9ee4fe + 1f564f7 commit 8d942d3

91 files changed

Lines changed: 704 additions & 4944 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/publish.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ jobs:
3535
uses: gradle/actions/setup-gradle@v4
3636
with:
3737
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
38-
- name: Download natives
39-
run: ./gradlew downloadLatestNatives
4038
- name: Build with Gradle Wrapper
4139
run: ./gradlew build
4240
- name: Stage

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
## Overview
1010

11+
[//]: # (TODO: new API)
12+
1113
```java
1214
void main() {
1315
// use MemorySegment to represent memory addresses
@@ -48,14 +50,13 @@ which enables cross-platform access to a set of C library bindings, providing va
4850

4951
[LWJGL 3](https://github.com/LWJGL/lwjgl3) is also a Java library that enables cross-platform access.
5052

51-
LWJGL 3 uses JNI to access native functions, in OverrunGL, however,
52-
the [FFM API](https://openjdk.org/jeps/454) is used, which makes the linking to native functions more convenient
53-
as well as providing better memory management.
53+
LWJGL 3.4.0 still uses JNI-style APIs, but delegates to the FFM backend when on JDK 25 or later.
54+
OverrunGL provides FFM-style APIs, where raw pointers are represented as `MemorySegment`.
5455

5556
## Getting Started
5657

5758
You can check the [wiki](https://github.com/Over-Run/overrungl/wiki) or
58-
the [samples](modules/samples/src/test/java/overrungl/demo). To run samples, check [run.md](doc/build/run.md).
59+
the [samples](modules/samples/src/test/java/overrungl/demo).
5960

6061
Feel free to ask questions in [discussions](https://github.com/Over-Run/overrungl/discussions) in case you have trouble in setup works, such as environment configuring.
6162

@@ -76,7 +77,8 @@ dependencies {
7677
implementation(platform("io.github.over-run:overrungl-bom:<VERSION>"))
7778
implementation("io.github.over-run:overrungl")
7879
implementation("io.github.over-run:overrungl-glfw")
79-
runtimeOnly("io.github.over-run:overrungl-glfw::natives-<OS>-<ARCH>")
80+
implementation(platform("org.lwjgl:lwjgl-bom:<VERSION>"))
81+
runtimeOnly("org.lwjgl:lwjgl-glfw::natives-<OS>-<ARCH>")
8082
}
8183
```
8284

@@ -89,7 +91,7 @@ Add this maven repository to use snapshot versions:
8991

9092
```kotlin
9193
repositories {
92-
maven("https://central.sonatype.com/repository/maven-snapshots/")
94+
maven("https://central.sonatype.com/repository/maven-snapshots")
9395
}
9496
```
9597

SECURITY.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
Only the latest version will be supported as we are currently working on 0.x versions.
66

7-
| Version | Supported |
8-
|---------|--------------------|
9-
| 0.1.x | :white_check_mark: |
7+
| Version | Supported |
8+
|---------------|--------------------|
9+
| | |
10+
| 0.3.x | :heavy_check_mark: |
11+
| 0.1.x ~ 0.2.x | :x: |
1012

1113
## Reporting a Vulnerability
1214

build.gradle.kts

Lines changed: 32 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
1-
import groovy.json.JsonSlurper
2-
import org.gradle.kotlin.dsl.support.unzipTo
31
import org.jreleaser.model.Active
4-
import java.net.URI
52
import 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
113
import 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

175
plugins {
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-
335133
tasks.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+
}

buildSrc/src/main/kotlin/OverrunGLModuleExtension.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ import org.gradle.api.provider.Property
77
abstract class OverrunGLModuleExtension {
88
abstract val artifactName: Property<String>
99
abstract val publishInfo: Property<Artifact>
10-
abstract val nativeBinding: Property<NativeBinding>
10+
abstract val lwjglArtifactName: Property<String>
1111
}

0 commit comments

Comments
 (0)