Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native libraries related changes #53

Merged
merged 2 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 44 additions & 19 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import org.gradle.plugins.ide.idea.model.IdeaModel

plugins {
Expand Down Expand Up @@ -75,7 +74,7 @@ artifactNameMap.forEach { (subprojectName, artifactName) ->

tasks.named<Jar>("jar") {
manifestContentCharset = "utf-8"
setMetadataCharset("utf-8")
metadataCharset = "utf-8"
manifest.attributes(
"Specification-Title" to projName,
"Specification-Vendor" to "Overrun Organization",
Expand Down Expand Up @@ -118,7 +117,6 @@ artifactNameMap.forEach { (subprojectName, artifactName) ->
allprojects {
tasks.withType<Javadoc> {
options {
verbose()
if (this is CoreJavadocOptions) {
if (jdkEnablePreview.toBoolean()) {
addBooleanOption("-enable-preview", true)
Expand Down Expand Up @@ -160,6 +158,47 @@ allprojects {
}
}

Artifact.values().forEach {
project(it.subprojectName) {
val javaComponent = components.findByName("java") as AdhocComponentWithVariants
// Add a different runtime variant for each platform
it.nativeBinding?.platforms?.forEach { platform ->
val nativeFileName = it.nativeFileName(platform)
val file = File("${rootProject.projectDir}/natives/$nativeFileName")

if (file.exists()) {
val archiveTaskName = "${it.nativeBinding?.bindingName}${platform.classifier}Jar"

val nativeJar = tasks.register<Jar>(archiveTaskName) {
archiveBaseName.set(it.artifactName)
archiveClassifier.set(platform.classifier)
from(file) { into(File(nativeFileName).parent) }
}

val nativeRuntimeElements = configurations.create(platform.classifier + "RuntimeElements") {
isCanBeConsumed = true; isCanBeResolved = false
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJavaVersion)
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attributes.attribute(
OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE,
objects.named(platform.osFamilyName)
)
attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named(platform.osArch))
}
outgoing.artifact(tasks.named("jar"))
outgoing.artifact(nativeJar)
extendsFrom(configurations["runtimeElements"])
}
javaComponent.addVariantsFromConfiguration(nativeRuntimeElements) {}
}
}
}
}

publishing.publications {
fun MavenPom.setupPom(pomName: String, pomDescription: String, pomPackaging: String) {
name.set(pomName)
Expand Down Expand Up @@ -192,26 +231,12 @@ publishing.publications {
}

Artifact.values().forEach { module ->
create<MavenPublication>("maven${module.mavenName}") {
create<MavenPublication>("maven${module.name}") {
groupId = projGroupId
artifactId = module.artifactName
version = projVersion
description = module.projectDescription
project(module.subprojectName) {
from(components["java"])
}
module.nativeBinding?.platforms?.forEach {
val nativeName = module.nativeFileName(it)!!
val file = File("${rootProject.projectDir}/natives/$nativeName")
if (file.exists()) {
val nativeParent = File(nativeName).parent
artifact(tasks.register<Jar>("nativeJar${module.mavenName}${it.taskSuffix}") {
archiveBaseName.set(module.artifactName)
archiveClassifier.set(it.classifier)
from(file) { into(nativeParent) }
})
}
}
from(project(module.subprojectName).components["java"])
pom {
setupPom(module.projectName, module.projectDescription, "jar")
}
Expand Down
46 changes: 21 additions & 25 deletions buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@
enum class NativePlatform(
val osFamilyName: String,
val osArch: String,
classifier: String,
val nativeLibPrefix: String,
val nativeLibSuffix: String,
val taskSuffix: String
classifier: String = "$osFamilyName-$osArch",
val nativeLibPrefix: String = "lib",
val nativeLibSuffix: String = ".so"
) {
WIN_64("windows", "x64", "windows", "", ".dll", "Win64"),
WIN_ARM64("windows", "arm64", "windows-arm64", "", ".dll", "WinArm64"),
LINUX_64("linux", "x64", "linux", "lib", ".so", "Linux64"),
LINUX_ARM32("linux", "arm32", "linux-arm32", "lib", ".so", "LinuxArm32"),
LINUX_ARM64("linux", "arm64", "linux-arm64", "lib", ".so", "LinuxArm64"),
MACOS("macos", "x64", "macos", "lib", ".dylib", "Macos"),
MACOS_ARM64("macos", "arm64", "macos-arm64", "lib", ".dylib", "MacosArm64");
FREEBSD_X64("freebsd", "x64", classifier = "freebsd"),
LINUX_64("linux", "x64", classifier = "linux"),
LINUX_ARM32("linux", "arm32"),
LINUX_ARM64("linux", "arm64"),
LINUX_PPC64LE("linux", "ppc64le"),
LINUX_RISCV64("linux", "riscv64"),
MACOS("macos", "x64", classifier = "macos", nativeLibSuffix = ".dylib"),
MACOS_ARM64("macos", "arm64", nativeLibSuffix = ".dylib"),
WIN_64("windows", "x64", classifier = "windows", nativeLibPrefix = "", nativeLibSuffix = ".dll"),
WIN_ARM64("windows", "arm64", nativeLibPrefix = "", nativeLibSuffix = ".dll");

companion object {
val ALL = values()
Expand All @@ -42,12 +44,7 @@ enum class NativeBinding(
val basename: String,
vararg val platforms: NativePlatform
) {
GLFW(
"glfw", "glfw3",
NativePlatform.WIN_64,
NativePlatform.LINUX_64, NativePlatform.LINUX_ARM64,
NativePlatform.MACOS, NativePlatform.MACOS_ARM64
),
GLFW("glfw", "glfw", *NativePlatform.ALL),
NFD("nfd", "nfd", *NativePlatform.ALL),
STB("stb", "stb", *NativePlatform.ALL)
}
Expand All @@ -57,38 +54,37 @@ enum class Artifact(
val projectName: String,
val projectDescription: String,
val subprojectName: String,
val mavenName: String,
val nativeBinding: NativeBinding? = null
) {
CORE(
"overrungl", "OverrunGL",
"The OverrunGL core library.",
":core", "Core"
":core"
),
GLFW(
"overrungl-glfw", "OverrunGL - GLFW bindings",
"A multi-platform library for OpenGL, OpenGL ES and Vulkan development on the desktop. It provides a simple API for creating windows, contexts and surfaces, receiving input and events.",
":glfw", "Glfw", NativeBinding.GLFW
":glfw", NativeBinding.GLFW
),
JOML(
"overrungl-joml", "OverrunGL - JOML native access",
"A Java math library for OpenGL rendering calculations",
":joml", "Joml"
":joml"
),
NFD(
"overrungl-nfd", "OverrunGL - Native File Dialog",
"A tiny, neat C library that portably invokes native file open and save dialogs.",
":nfd", "Nfd", NativeBinding.NFD
":nfd", NativeBinding.NFD
),
OPENGL(
"overrungl-opengl", "OverrunGL - OpenGL bindings",
"The most widely adopted 2D and 3D graphics API in the industry, bringing thousands of applications to a wide variety of computer platforms.",
":opengl", "Opengl"
":opengl"
),
STB(
"overrungl-stb", "OverrunGL - stb bindings",
"Single-file public domain libraries for fonts, images, ogg vorbis files and more.",
":stb", "Stb", NativeBinding.STB
":stb", NativeBinding.STB
),
// VULKAN("overrungl-vulkan", "OverrunGL - Vulkan bindings",
// "A new generation graphics and compute API that provides high-efficiency, cross-platform access to modern GPUs used in a wide variety of devices from PCs and consoles to mobile phones and embedded platforms.",
Expand All @@ -97,6 +93,6 @@ enum class Artifact(

fun nativeFileName(platform: NativePlatform): String? {
return if (nativeBinding == null) null
else "${nativeBinding.bindingName}/${platform.osFamilyName}/${platform.osArch}/${platform.nativeLibPrefix}${nativeBinding.basename}${platform.nativeLibSuffix}"
else "${nativeBinding.bindingName}/${platform.osFamilyName}-${platform.osArch}/${platform.nativeLibPrefix}${nativeBinding.basename}${platform.nativeLibSuffix}"
}
}
79 changes: 28 additions & 51 deletions doc/internal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

To publish this library, you need a GPG key and the write permission of Maven Central.

### CI

- [glfw](https://github.com/Over-Run/glfw-ci)
- [nfd](https://github.com/Over-Run/nfd-ci)
- [stb](https://github.com/Over-Run/stb-ci)

### Packing Natives

The build script packs the native libraries into jars.
Expand All @@ -12,56 +18,27 @@ The tree structure of the native libraries is:

```text
natives
├─ glfw
│ ├─ linux
│ │ ├─ arm64
│ │ │ └─ libglfw3.so
│ │ └─ x64
│ │ └─ libglfw3.so
│ ├─ macos
│ │ ├─ arm64
│ │ │ └─ libglfw3.dylib
│ │ └─ x64
│ │ └─ libglfw3.dylib
│ └─ windows
│ └─ x64
│ └─ glfw3.dll
├─ nfd https://github.com/Over-Run/nfd-ci
│ ├─ linux
│ │ ├─ arm32
│ │ │ └─ libnfd.so
│ │ ├─ arm64
│ │ │ └─ libnfd.so
│ │ └─ x64
│ │ └─ libnfd.so
│ ├─ macos
│ │ ├─ arm64
│ │ │ └─ libnfd.dylib
│ │ └─ x64
│ │ └─ libnfd.dylib
│ └─ windows
│ ├─ arm64
│ │ └─ nfd.dll
│ └─ x64
│ └─ nfd.dll
└─ stb https://github.com/Over-Run/stb-ci
├─ linux
│ ├─ arm32
│ │ └─ libstb.so
│ ├─ arm64
│ │ └─ libstb.so
│ └─ x64
│ └─ libstb.so
├─ macos
│ ├─ arm64
│ │ └─ libstb.dylib
│ └─ x64
│ └─ libstb.dylib
└─ windows
├─ arm64
│ └─ stb.dll
└─ x64
└─ stb.dll
└─ <module-name>
├─ freebsd-x64
│ └─ lib<basename>.so
├─ linux-arm32
│ └─ lib<basename>.so
├─ linux-arm64
│ └─ lib<basename>.so
├─ linux-ppc64le
│ └─ lib<basename>.so
├─ linux-riscv64
│ └─ lib<basename>.so
├─ linux-x64
│ └─ lib<basename>.so
├─ macos-arm64
│ └─ lib<basename>.dylib
├─ macos-x64
│ └─ lib<basename>.dylib
├─ windows-arm64
│ └─ <basename>.dll
└─ windows-x64
└─ <basename>.dll
```

The `natives` directory is in the project directory.
The `natives` directory is in the root project directory.
6 changes: 3 additions & 3 deletions modules/overrungl.core/src/main/java/overrungl/OverrunGL.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ public final class OverrunGL {
/**
* The version of GLFW native libraries.
*/
public static final String GLFW_VERSION = "3.4.0.0";
public static final String GLFW_VERSION = "3.5.0.0";
/**
* The version of NFD native libraries.
*/
public static final String NFD_VERSION = "0.1.0.0";
public static final String NFD_VERSION = "1.1.1.0";
/**
* The version of STB native libraries.
*/
public static final String STB_VERSION = "0.1.0.3";
public static final String STB_VERSION = "0.1.0.4";
private static final Consumer<String> DEFAULT_LOGGER = System.err::println;
private static Consumer<String> apiLogger = DEFAULT_LOGGER;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public final class RuntimeHelper {
* The native linker.
*/
private static final Linker LINKER = Linker.nativeLinker();
private static final Path tmpdir = Path.of(System.getProperty("java.io.tmpdir"));
private static final Path tmpdir = Path.of(System.getProperty("java.io.tmpdir"))
.resolve(STR."overrungl\{System.getProperty("user.name")}");
private static final StackWalker STACK_WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
private static final Map<String, MemoryLayout> CANONICAL_LAYOUTS = LINKER.canonicalLayouts();
/**
Expand Down Expand Up @@ -102,24 +103,23 @@ public static SymbolLookup load(String module, String basename, String version)
uri = localFile;
} else {
// 2. Load from classpath
var file = tmpdir.resolve(STR."overrungl\{System.getProperty("user.name")}");
try {
if (!Files.exists(file)) {
if (!Files.exists(tmpdir)) {
// Create directory
Files.createDirectories(file);
} else if (!Files.isDirectory(file)) {
Files.createDirectories(tmpdir);
} else if (!Files.isDirectory(tmpdir)) {
// Remove
Files.delete(file);
Files.delete(tmpdir);
// Create directory
Files.createDirectories(file);
Files.createDirectories(tmpdir);
}
} catch (IOException e) {
throw new IllegalStateException(STR."Couldn't create directory: \{file}; try setting -Doverrungl.natives to a valid path", e);
throw new IllegalStateException(STR."Couldn't create directory: \{tmpdir}; try setting -Doverrungl.natives to a valid path", e);
}
var libFile = file.resolve(STR."\{basename}-\{version}\{suffix}");
var libFile = tmpdir.resolve(STR."\{basename}-\{version}\{suffix}");
if (!Files.exists(libFile)) {
// Extract
final String fromPath = STR."\{module}/\{os.familyName()}/\{Architecture.current()}/\{path}";
final String fromPath = STR."\{module}/\{os.familyName()}-\{Architecture.current()}/\{path}";
try (var is = STACK_WALKER.getCallerClass().getClassLoader().getResourceAsStream(fromPath)) {
Files.copy(Objects.requireNonNull(is, STR."File not found in classpath: \{fromPath}"), libFile);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class Handles {
static final SymbolLookup lookup;

static {
final Supplier<SymbolLookup> lib = () -> RuntimeHelper.load("glfw", "glfw3", OverrunGL.GLFW_VERSION);
final Supplier<SymbolLookup> lib = () -> RuntimeHelper.load("glfw", "glfw", OverrunGL.GLFW_VERSION);
final var function = Configurations.GLFW_SYMBOL_LOOKUP.get();
lookup = function != null ? function.apply(lib) : lib.get();
}
Expand Down
Loading