Skip to content

Commit

Permalink
Chore/roll up native lib in java build (#112)
Browse files Browse the repository at this point in the history
* wip(java): spike for rolling up the native libs into the java build


---------

Co-authored-by: sighphyre <liquidwicked64@gmail.com>
  • Loading branch information
Christopher Kolstad and sighphyre authored Dec 5, 2023
1 parent 0b3fa7e commit f735936
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 66 deletions.
16 changes: 7 additions & 9 deletions .github/workflows/build-java.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,25 @@ on:
branches:
- main
paths:
- 'java-engine/**.java'
- 'java-engine/src/**/*.java'
- 'java-engine/build.gradle.kts'
# Build in main if anything changes in yggdrasil or the ffi layer
- 'unleash-yggdrasil/src/**.rs'
- 'yggdrasilffi/src/**.rs'
- 'unleash-yggdrasil/src/**/*.rs'
- 'yggdrasilffi/src/**/*.rs'
pull_request:
paths:
- 'java-engine/**.java'
- 'java-engine/**/*.java'

jobs:
build-java:
runs-on: ubuntu-latest
strategy:
matrix:
version: [8,11,20]
permissions:
contents: read
security-events: write
actions: read
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

## begin build yggdrasil (maybe this can be a separate job)
- name: Install rust
Expand All @@ -51,7 +49,7 @@ jobs:
- name: Setup Java
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.version }}
java-version: 21
cache: 'gradle'
distribution: 'temurin'
- name: Check Code Format
Expand Down
88 changes: 88 additions & 0 deletions .github/workflows/publish-java.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
name: Publish Java Engine
on:
push:
tags:
- "java-engine-*"
workflow_dispatch:

jobs:
release-linux:
if: startsWith(github.ref, 'refs/tags/java-engine')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build Rust library for linux
run: |
cargo build --release
- name: Deploy release
uses: burrunan/gradle-cache-action@v1
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingPassphrase: ${{ secrets.GPG_PASSPHRASE }}
with:
job-id: release-linux
arguments: publishToSonatype closeAndReleaseSonatypeStagingRepository
working-directory: java-engine
release-windows:
if: startsWith(github.ref, 'refs/tags/java-engine')
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true

- name: Build Rust Library for Windows
run: |
cd yggdrasilffi
cargo build --release --target x86_64-pc-windows-gnu
working-directory: ${{ github.workspace }}
- name: Deploy release
uses: burrunan/gradle-cache-action@v1
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingPassphrase: ${{ secrets.GPG_PASSPHRASE }}
with:
job-id: release-linux
arguments: publishToSonatype closeAndReleaseSonatypeStagingRepository
working-directory: java-engine
release-macos:
if: startsWith(github.ref, 'refs/tags/java-engine')
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Build Rust Library for macOS
run: |
cd yggdrasilffi
cargo build --release --target x86_64-apple-darwin
working-directory: ${{ github.workspace }}
- name: Deploy release
uses: burrunan/gradle-cache-action@v1
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingPassphrase: ${{ secrets.GPG_PASSPHRASE }}
with:
job-id: release-linux
arguments: publishToSonatype closeAndReleaseSonatypeStagingRepository
working-directory: java-engine
125 changes: 111 additions & 14 deletions java-engine/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
plugins {
// Apply the java-library plugin for API and implementation separation.
`java-library`
id("com.diffplug.spotless") version "6.13.0"
`maven-publish`
signing
id("com.diffplug.spotless") version "6.23.2"
id("com.github.johnrengelman.shadow") version "7.1.0"
id("io.github.gradle-nexus.publish-plugin").version("1.3.0")
id("pl.allegro.tech.build.axion-release").version("1.16.0")
id("com.google.osdetector").version("1.7.3")
}

val tagVersion = System.getenv("GITHUB_REF")?.split('/')?.last()
scmVersion {
repository {
type.set("git")
directory.set("$rootDir/..")
remote.set("origin")
}
tag {
prefix.set("java-engine-")
}
}
project.version = scmVersion.version

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
Expand All @@ -31,9 +49,18 @@ dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.2")
}

val copyNativeLibs by tasks.registering(Copy::class) {
from("$rootDir/../target/release/libyggdrasilffi.so", "$rootDir/../target/release/libyggdrasil.dll", "$rootDir/../target/release/libyggdrasil.dylib")
into(layout.buildDirectory.dir("resources/main"))
}

tasks.named<ProcessResources>("processResources") {
dependsOn(copyNativeLibs)
}

spotless {
java {
googleJavaFormat("1.7").aosp()
googleJavaFormat("1.18.1").aosp()
removeUnusedImports()
importOrder()
}
Expand All @@ -52,23 +79,93 @@ tasks.jar {
manifest {
attributes(
"Implementation-Title" to project.name,
"Implementation-Version" to project.version
"Implementation-Version" to project.version,
"Implementation-Platform" to osdetector.classifier
)
}
// Optionally, include other configurations if needed
}

tasks.shadowJar {
archiveBaseName.set("unleash-engine")
manifest {
attributes["Implementation-Title"] = project.name
attributes["Implementation-Version"] = project.version
}
// Include or exclude specific dependencies or files if needed
}


tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
}

val sonatypeUsername: String? by project
val sonatypePassword: String? by project
val group: String? by project

publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
groupId = group
artifactId = "yggdrasil-engine-${osdetector.os}"
version = "${version}"
pom {
name.set("Unleash Yggdrasil Engine")
description.set("Yggdrasil engine for computing feature toggles")
url.set("https://docs.getunleash.io/yggdrasil-engine/index.html")
licenses {
license {
name.set("MIT")
url.set("https://opensource.org/license/mit/")
}
}
developers {
developer {
id.set("chrkolst")
name.set("Christopher Kolstad")
email.set("chriswk@getunleash.io")
}
developer {
id.set("ivarconr")
name.set("Ivar Conradi Østhus")
email.set("ivarconr@getunleash.io")
}
developer {
id.set("gastonfournier")
name.set("Gaston Fournier")
email.set("gaston@getunleash.io")
}
developer {
id.set("sighphyre")
name.set("Simon Hornby")
email.set("simon@getunleash.io")
}
}
scm {
connection.set("scm:git:https://github.com/Unleash/yggdrasil")
developerConnection.set("scm:git:ssh://git@github.com:Unleash/yggdrasil")
url.set("https://github.com/Unleash/yggdrasil")
}
}
}
}
repositories {
maven {
url = uri(layout.buildDirectory.dir("repo"))
name = "test"
}
}
}

nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
username.set(sonatypeUsername)
password.set(sonatypePassword)
}
}
}

val signingKey: String? by project
val signingPassphrase: String? by project
signing {
if (signingKey != null && signingPassphrase != null) {
useInMemoryPgpKeys(signingKey, signingPassphrase)
sign(publishing.publications["mavenJava"])
}
}
1 change: 1 addition & 0 deletions java-engine/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
group=io.getunleash
2 changes: 1 addition & 1 deletion java-engine/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion java-engine/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0"
}

rootProject.name = "unleash-engine"
rootProject.name = "yggdrasil-engine"
57 changes: 39 additions & 18 deletions java-engine/src/main/java/io/getunleash/engine/YggdrasilFFI.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import java.nio.file.Paths;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -39,29 +44,45 @@ class YggdrasilFFI {
private final UnleashFFI ffi;
private final Pointer enginePtr;

/** If we want singleton we just make the constructors private */
YggdrasilFFI() {
this(System.getenv("YGGDRASIL_LIB_PATH"));
}

static UnleashFFI loadLibrary(String libraryPath) {
if (libraryPath == null) {
libraryPath = "."; // assume it's accessible in current path
}
log.info("Loading library from " + Paths.get(libraryPath).toAbsolutePath());
String libImpl = "libyggdrasilffi.so";
static UnleashFFI loadLibrary() {
String libName;
if (Platform.isMac()) {
libImpl = "libyggdrasilffi.dylib";
libName = "libyggdrasilffi.dylib";
} else if (Platform.isWindows()) {
libImpl = "libyggdrasilffi.dll";
libName = "libyggdrasilffi.dll";
} else {
libName = "libyggdrasilffi.so";
}

String combinedPath = Paths.get(libraryPath, libImpl).toAbsolutePath().toString();
return Native.load(combinedPath, UnleashFFI.class);
try {
// Extract and load the native library from the JAR
Path tempLib = extractLibraryFromJar(libName);
System.load(tempLib.toAbsolutePath().toString());
return Native.load(libName, UnleashFFI.class);
} catch (IOException e) {
throw new RuntimeException("Failed to load native library", e);
}
}

YggdrasilFFI(String libraryPath) {
this(loadLibrary(libraryPath));
private static Path extractLibraryFromJar(String libName) throws IOException {
Path tempFile = Files.createTempFile("lib", libName);
try (InputStream in = UnleashFFI.class.getResourceAsStream("/" + libName);
OutputStream out = Files.newOutputStream(tempFile)) {
if (in == null) {
throw new FileNotFoundException("File " + libName + " was not found inside JAR.");
}

byte[] buffer = new byte[1024];
int readBytes;
while ((readBytes = in.read(buffer)) != -1) {
out.write(buffer, 0, readBytes);
}
}
return tempFile;
}

YggdrasilFFI() {
this(loadLibrary());
}

YggdrasilFFI(UnleashFFI ffi) {
Expand Down
Loading

0 comments on commit f735936

Please sign in to comment.