From db50e674c9d02e30c4d75d29eb9b68559391fd49 Mon Sep 17 00:00:00 2001 From: Georgi Georgiev Date: Mon, 10 Jun 2024 15:25:30 +0300 Subject: [PATCH] Binaries update (#8) * removed bintray mentions and added bonus configuration in the release yaml that requires testing Signed-off-by: Georgi Georgiev * changed action provider for prerelease Signed-off-by: Georgi Georgiev * removed creating pre-release Signed-off-by: Georgi Georgiev * update gradelev Signed-off-by: Georgi Georgiev * added echo command for arch Signed-off-by: Georgi Georgiev * added line Signed-off-by: Georgi Georgiev * fix Signed-off-by: Georgi Georgiev * trying Signed-off-by: Georgi Georgiev * added missing line Signed-off-by: Georgi Georgiev * switched sopaces for tabs Signed-off-by: Georgi Georgiev * small changes Signed-off-by: Georgi Georgiev * typo Signed-off-by: Georgi Georgiev * added a separate file for the mac release Signed-off-by: Georgi Georgiev * 2nd try with arch Signed-off-by: Georgi Georgiev * distribution Signed-off-by: Georgi Georgiev * added additional file for building the dylibs Signed-off-by: Georgi Georgiev * nerf eddition level Signed-off-by: Georgi Georgiev * another nerf Signed-off-by: Georgi Georgiev * aded file for dylib generation Signed-off-by: Georgi Georgiev * /testing Signed-off-by: Georgi Georgiev * changed generation command Signed-off-by: Georgi Georgiev * changed release action Signed-off-by: Georgi Georgiev * added rustup target Signed-off-by: Georgi Georgiev * typo Signed-off-by: Georgi Georgiev * typo Signed-off-by: Georgi Georgiev * testing Signed-off-by: Georgi Georgiev * testing Signed-off-by: Georgi Georgiev * testing Signed-off-by: Georgi Georgiev * testing Signed-off-by: Georgi Georgiev * testing Signed-off-by: Georgi Georgiev * changed all the yamls to activate on release to test everything together Signed-off-by: Georgi Georgiev * added some logs as well Signed-off-by: Georgi Georgiev * removed the 32 linux platform as github doesnt support such images and wasmer-vm causes crash when trying to cross-compile Signed-off-by: Georgi Georgiev * change to on push Signed-off-by: Georgi Georgiev * the aarch64v linux is incompatible too Signed-off-by: Georgi Georgiev * removed slashes Signed-off-by: Georgi Georgiev * deleted the outdated commandd in makefile Signed-off-by: Georgi Georgiev * undo deleting the makefile rust build commands as it is apperantly used by the test yaml as well Signed-off-by: Georgi Georgiev * Package all natives in a single jar. (#9) * Adapt dynamic_libs.yaml so that it uploads libs as artifacts. * Add condition when copying native libs to build dir. Test download artifacts in release.yaml. * Test fix pattern. * Test fix pattern. * Remove matrix. * Test remove pattern from artifact download. * Refactor dynamic_libs.yaml so that it's callable from release.yaml. * Add needs flag to release.yaml * Test list structure in native-libs directory. * Rework dynamic_libs.yaml so that it's reusable. Adapt release.yaml to use dynamic_libs_reusable.yaml. Executing release.yaml from a branch uploads the jar with all the possible native libs inside the workflow artifacts. Executing it from a tag uploads it to the tag's release. * Test param passs fix. * test * test * Remove github.event from the reusable workflow. * Use github.ref.type instead of github.ref * test * test * test * Remove test step from dynamic_libs_reusable.yaml. Use startsWith expression in release.yaml. * Add docs. Change name of JAR. * Test using randomizes artifacts dir. * Fix export syntax. * Add section in README.md for env variable. Rename GH_ARTIFACTS_DIR to EXT_ARTIFACTS_DIR * Change 'native-libs' to using env var. * test * test * Push EXT_ARTIFACTS_DIR to gh env. * Add mkdir. * Fix mkdir. * Fix mkdir. * Fix usage of env var in "with" section of step. * Test * Remove comments. * Remove leftovers. * fix: use "uname -m" when retrieving os arch in Native.java. Change win32 to windows. * fix: translate x86_64 to amd64 to keep uniform naming of artifacts. * Substitute random uuid for mktemp command. * Test fix env var assign * Test fix env var assign * Test remove cache steps. * Revert cache removal. --------- Signed-off-by: Georgi Georgiev Co-authored-by: Yordan Atanasov <99724706+Zurcusa@users.noreply.github.com> --- .github/workflows/build_publish.yaml | 134 +++++++++++++++++++ .github/workflows/dynamic_libs_reusable.yaml | 53 ++++++++ .github/workflows/release.yml | 130 ------------------ .github/workflows/{test.yml => test.yaml} | 4 +- Cargo.lock | 2 +- Cargo.toml | 5 +- Makefile | 4 - README.md | 69 +--------- build.gradle | 59 ++------ gradle/wrapper/gradle-wrapper.properties | 2 +- src/java/org/wasmer/Native.java | 27 ++-- 11 files changed, 232 insertions(+), 257 deletions(-) create mode 100644 .github/workflows/build_publish.yaml create mode 100644 .github/workflows/dynamic_libs_reusable.yaml delete mode 100644 .github/workflows/release.yml rename .github/workflows/{test.yml => test.yaml} (91%) diff --git a/.github/workflows/build_publish.yaml b/.github/workflows/build_publish.yaml new file mode 100644 index 0000000..db2f1d0 --- /dev/null +++ b/.github/workflows/build_publish.yaml @@ -0,0 +1,134 @@ +# This workflow compiles the native wasmer_jni lib into various OS and architectures using dynamic_libs_reusable.yaml. +# Then it builds and publishes the JAR based on the GITHUB_REF that calls the workflow. There are 2 options: +# 1. The workflow is executed from a branch. In that case the JAR is available in the GitHub action as an artifact. +# 2. The workflow is executed from a tag. In that case the JAR is published in the corresponding GitHub release. +name: Build and publish + +on: + push: + branches: + - '*' # Trigger on push to any branch + tags: + - '*' # Trigger on push to any tag + +jobs: + upload_dynamic_libs: + strategy: + matrix: + platform: + - os: 'macos-latest' + target: 'aarch64-apple-darwin' + artifact: 'darwin-arm64' + lib_name: 'libwasmer_jni.dylib' + + - os: 'macos-latest' + target: 'x86_64-apple-darwin' + artifact: 'darwin-amd64' + lib_name: 'libwasmer_jni.dylib' + + - os: 'ubuntu-latest' + target: 'x86_64-unknown-linux-gnu' + artifact: 'linux-amd64' + lib_name: 'libwasmer_jni.so' + + - os: 'windows-latest' + target: 'x86_64-pc-windows-msvc' + artifact: 'windows-amd64' + lib_name: 'wasmer_jni.dll' + uses: ./.github/workflows/dynamic_libs_reusable.yaml + with: + platform_os: ${{ matrix.platform.os }} + platform_target: ${{ matrix.platform.target }} + platform_artifact: ${{ matrix.platform.artifact }} + platform_lib_name: ${{ matrix.platform.lib_name }} + + publish_jar: + name: Publish the JARs + runs-on: ubuntu-latest + needs: [upload_dynamic_libs] + + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set up Java 21 + uses: actions/setup-java@v1 + with: + java-version: 21 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + override: true + + - name: Cache Cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: cargo-registry-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache Cargo bin + uses: actions/cache@v1 + with: + path: ~/.cargo/bin + key: cargo-bin-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache Cargo build + uses: actions/cache@v1 + with: + path: target + key: cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + + - name: Setup wasmer-jni artifacts dir + shell: bash + run: | + echo "EXT_ARTIFACTS_DIR=$(mktemp -d)" >> $GITHUB_ENV + + - name: Download wasmer_jni artifacts + uses: actions/download-artifact@v4 + with: + path: ${{ env.EXT_ARTIFACTS_DIR }} + + - name: Display structure of downloaded files + run: ls -R $EXT_ARTIFACTS_DIR + + - name: Run all the tests + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + make test + + - name: Create the JAR + id: create_jar + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + make package + + - name: Upload JAR as workflow artifact + if: startsWith(github.ref, 'refs/heads/') + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.create_jar.outputs.name }} + path: ${{ steps.create_jar.outputs.path }} + retention-days: 1 + + - name: Get release info + id: get_release_info + if: startsWith(github.ref, 'refs/tags/') + uses: bruceadams/get-release@v1.3.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload JAR as Github pre-release asset + if: startsWith(github.ref, 'refs/tags/') + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release_info.outputs.upload_url }} + asset_path: ${{ steps.create_jar.outputs.path }} + asset_name: ${{ steps.create_jar.outputs.name }} + asset_content_type: application/java-archive \ No newline at end of file diff --git a/.github/workflows/dynamic_libs_reusable.yaml b/.github/workflows/dynamic_libs_reusable.yaml new file mode 100644 index 0000000..fe5b6f9 --- /dev/null +++ b/.github/workflows/dynamic_libs_reusable.yaml @@ -0,0 +1,53 @@ +# This workflow cross compiles a rust library into a native based on a provided target. +# The compiled library is then uploaded as a GitHub artifact. +name: upload_dynamic_libs + +on: + workflow_call: + inputs: + platform_os: + description: Operating system that the runner will use. + type: string + required: true + platform_target: + description: The architecture that rust will compile the libs for. + type: string + required: true + platform_artifact: + description: The name of the artifact that will be uploaded. + type: string + required: true + platform_lib_name: + description: Name of the native library to be uploaded in the artifact. + type: string + required: true + + +jobs: + publish_dlibs: + name: Publish the dynamic libraries + runs-on: ${{ inputs.platform_os }} + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + override: true + + - name: Create the dynamic libs + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + rustup target add ${{ inputs.platform_target }} + cargo build --release --target=${{ inputs.platform_target }} + + - name: Upload the dynamic libs + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.platform_artifact }} + path: ./target/${{ inputs.platform_target }}/release/${{ inputs.platform_lib_name }} + retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f77f0a5..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,130 +0,0 @@ -name: Release - -on: - push: - tags: - - '**' - -jobs: - create_pre_release: - name: Create pre-release - - runs-on: ubuntu-latest - - steps: - # The pre-release must be created only once, hence the split - # into multiple jobs with different `strategy`. - - name: Create a Github pre-release - id: create_pre_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - draft: false - prerelease: true - - - name: Output `release_url` into a temporary file - run: echo "${{ steps.create_pre_release.outputs.upload_url }}" > release_url.txt - - - name: Save the `release_url` temporary file - uses: actions/upload-artifact@v1 - with: - name: release_url - path: release_url.txt - - publish_jar: - name: Publish the JARs - - needs: [create_pre_release] - - strategy: - matrix: - # The job runs on 3 different OS. - os: [ubuntu-latest, macos-latest, windows-latest] - # The job runs on different Java versions (LTS). - java: [8] - # As soon as one job fails in the matrix, all the other - # in-progress jobs are canceled. - fail-fast: true - - runs-on: ${{ matrix.os }} - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Set up Java ${{ matrix.version }} - uses: actions/setup-java@v1 - with: - java-version: ${{ matrix.java }} - - - name: Set up Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - default: true - override: true - - - name: Cache Cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache Cargo bin - uses: actions/cache@v1 - with: - path: ~/.cargo/bin - key: ${{ runner.os }}-cargo-bin-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache Cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - name: Run all the tests - shell: bash - run: | - export PATH="$HOME/.cargo/bin:$PATH" - make test - - - name: Create the JAR - id: create_jar - shell: bash - run: | - export PATH="$HOME/.cargo/bin:$PATH" - make package - - - name: Load the `release_url` from the temporary file - uses: actions/download-artifact@v1 - with: - name: release_url - - - name: Read the `release_url` temporary file - id: get_release_info - shell: bash - run: | - value=$(cat release_url/release_url.txt) - echo ::set-output name=upload_url::$value - - - name: Upload JAR as Github pre-release asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.get_release_info.outputs.upload_url }} - asset_path: ${{ steps.create_jar.outputs.path }} - asset_name: ${{ steps.create_jar.outputs.name }} - asset_content_type: application/java-archive - - - name: Upload JAR to Bintray (JCenter) - env: - BINTRAY_USER: ${{ secrets.BINTRAY_USER }} - BINTRAY_API_KEY: ${{ secrets.BINTRAY_API_KEY }} - shell: bash - run: | - export PATH="$HOME/.cargo/bin:$PATH" - make publish diff --git a/.github/workflows/test.yml b/.github/workflows/test.yaml similarity index 91% rename from .github/workflows/test.yml rename to .github/workflows/test.yaml index 87591e8..7b3095f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yaml @@ -10,9 +10,9 @@ jobs: strategy: matrix: # The job runs on 3 different OS. - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-13, windows-latest] # The job runs on different Java versions (LTS). - java: [8] + java: [21] # As soon as one job fails in the matrix, all the other # in-progress jobs are canceled. fail-fast: true diff --git a/Cargo.lock b/Cargo.lock index 0d27064..aca42b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "wasmer-jni" -version = "0.3.0" +version = "1.1.0" dependencies = [ "jni", "wasmer", diff --git a/Cargo.toml b/Cargo.toml index 8fdb281..07113d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,8 @@ [package] publish = false name = "wasmer-jni" -version = "0.3.0" -authors = ["Ivan Enderlin "] -edition = "2018" +version = "1.1.0" +edition = "2021" [lib] crate-type = ["cdylib"] diff --git a/Makefile b/Makefile index 5d9b059..8affd2a 100644 --- a/Makefile +++ b/Makefile @@ -112,10 +112,6 @@ javadoc: package: "./gradlew" --info jar -# Publish the package artifact to a public repository -publish: - "./gradlew" --info uploadToBintray - # Run a specific example, with `make run-example EXAMPLE=Simple` for instance. run-example: $(eval JAR := $(shell find ./build/libs/ -name "wasmer-jni-*.jar")) diff --git a/README.md b/README.md index 6928a9f..b543fd7 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,6 @@ License - - Go Package - API Documentation @@ -46,17 +43,13 @@ Features: # Install -The Wasmer package is published in [Bintray](https://bintray.com/) on [the -`wasmer/wasmer-jni` -repository](https://bintray.com/wasmer/wasmer-jni/wasmer-jni). - The JAR files are named as follows: -`wasmer-jni-$(architecture)-$(os)-$(version).jar`. Thus, to include +`wasmer-jni-$(version).jar`. Thus, to include Wasmer JNI as a dependency, write for instance: ```gradle dependencies { - implementation "org.wasmer:wasmer-jni-amd64-linux:0.3.0" + implementation "org.wasmer:wasmer-jni:1.1.0" } ``` @@ -309,61 +302,13 @@ To build the JAR package: $ make package ``` -This will generate the file `build/libs/wasmer-jni-$(architecture)-$(os)-0.3.0.jar`. - -#### Automatic dependencies per architecture and platform - -
-It is possible to infer the archive appendix automatically, see how. - -According the [Gradle Jar -API](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:appendix), -the `$(architecture)-$(os)` part is called the _archive prefix_. To -infer that appendix automatically to configure your dependencies, you -can use the following `inferWasmerJarAppendix` function: - -```gradle -String inferWasmerJarAppendix() { - def nativePlatform = new org.gradle.nativeplatform.platform.internal.DefaultNativePlatform("current") - def arch = nativePlatform.architecture - def os = nativePlatform.operatingSystem - - def arch_name - - switch (arch.getName()) { - case ["x86_64", "x64", "x86-64"]: - arch_name = "amd64" - break; - - default: - throw new RuntimeException("`wasmer-jni` has no pre-compiled archive for the architecture " + arch.getName()) - } - - def os_name - - if (os.isMacOsX()) { - os_name = "darwin" - } else if (os.isLinux()) { - os_name = "linux" - } else if (os.isWindows()) { - os_name = "windows" - } else { - throw new RuntimeException("`wasmer-jni` has no pre-compiled archive for the platform " + os.getName()) - } - - return arch_name + "-" + os_name -} -``` - -Finally, you can configure your dependencies such as: +This will generate the file `build/libs/wasmer-jni-1.1.0.jar`. -```gradle -dependencies { - implementation "org.wasmer:wasmer-jni-" + inferWasmerJarAppendix() + ":0.3.0" -} -``` +### Native `wasmer-jni` inclusion flow. -
+You can export the environment variable `EXT_ARTIFACTS_DIR` in the terminal that you are building the project from. +It should point to the directory where you've put your `wasmer-jni` native libs. If the environment variable is missing +gradle will copy the contents of the `artifacts` folder, which is generated by the `make build-rust` command. ### Testing diff --git a/build.gradle b/build.gradle index a701697..1f4f40e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,19 +2,18 @@ plugins { id "java" // As Wasmer is a package, we need tools to build the JARs and so id "java-library" - id "com.jfrog.bintray" version "1.8.5" } allprojects { group "org.wasmer" - version "0.3.0" + version "1.1.0" } // This is needed for the Java plugin to make sure // the generated class files are compatible with // old versions of Java. -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = 21 +targetCompatibility = 21 sourceSets { main { @@ -66,43 +65,6 @@ jar { attributes("Implementation-Title": project.name, "Implementation-Version": project.version) } - - // The JAR name is defined as - // `${baseName}-${appendix}-${version}-${classifier}.${extension}`. - archiveAppendix = inferWasmerJarAppendix() -} - -String inferWasmerJarAppendix() { - def nativePlatform = new org.gradle.nativeplatform.platform.internal.DefaultNativePlatform("current") - def arch = nativePlatform.architecture - def os = nativePlatform.operatingSystem - - def arch_name - - switch (arch.getName()) { - case ["x86_64", "x64", "x86-64"]: - arch_name = "amd64" - break; - case ["arm64", "aarch64"]: - arch_name = "arm64" - break; - default: - throw new RuntimeException("Architecture " + arch.getName() + " is not supported.") - } - - def os_name - - if (os.isMacOsX()) { - os_name = "darwin" - } else if (os.isLinux()) { - os_name = "linux" - } else if (os.isWindows()) { - os_name = "windows" - } else { - throw new RuntimeException("Platform " + os.getName() + " is not supported.") - } - - return arch_name + "-" + os_name } task generateJniHeaders(type: JavaCompile) { @@ -128,10 +90,17 @@ task buildRust(type: Exec) { task copyAllArtifacts(type: Copy) { dependsOn buildRust - description "Copy build artifacts to the `build/` directory." + description "Copy artifacts to the `build/` directory. If a non-empty directory exists at the value of " + + "'EXT_ARTIFACTS_DIR' env variable it copies its contents, otherwise it copies from the artifacts folder." + + def extArtifactsDir = System.getenv('EXT_ARTIFACTS_DIR') + def sourceDir = extArtifactsDir != null && !extArtifactsDir.isBlank() + ? extArtifactsDir + : 'artifacts' + + from(sourceDir) + include '**/*' - from "artifacts" - include "**/*" into "$buildDir/toArtifact/org/wasmer/native/" } @@ -150,7 +119,7 @@ tasks.withType(Test) { jar.doLast() { // Display specific “action outputs” for Github Actions. - def jar_name = project.archivesBaseName + "-" + inferWasmerJarAppendix() + "-" + project.version + ".jar" + def jar_name = project.archivesBaseName + "-" + project.version + ".jar" println(jar_name) println("::set-output name=path::./build/libs/" + jar_name) println("::set-output name=name::" + jar_name) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f42e62f..28f5fcf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/java/org/wasmer/Native.java b/src/java/org/wasmer/Native.java index 9969073..c4a9397 100644 --- a/src/java/org/wasmer/Native.java +++ b/src/java/org/wasmer/Native.java @@ -5,7 +5,13 @@ package org.wasmer; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.URL; import java.nio.file.Files; import java.util.logging.Level; @@ -19,7 +25,8 @@ public class Native { LOADED_EMBEDDED_LIBRARY = loadEmbeddedLibrary(); } - private Native() {} + private Native() { + } public static String getCurrentPlatformIdentifier() { String osName = System.getProperty("os.name").toLowerCase(); @@ -28,13 +35,15 @@ public static String getCurrentPlatformIdentifier() { osName = "windows"; } else if (osName.contains("mac os x")) { osName = "darwin"; - String[] args = new String[] {"/bin/bash", "-c", "uname", "-p"}; + String[] args = new String[]{"/bin/bash", "-c", "uname -m"}; try { Process proc = new ProcessBuilder(args).start(); - BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); - if (reader.readLine().equals("Darwin")) { - return osName + "-arm64"; - } + String arch = new BufferedReader(new InputStreamReader(proc.getInputStream())) + .readLine().equals("x86_64") + ? "amd64" + : "arm64"; + + return osName + "-" + arch; } catch (IOException e) { throw new RuntimeException(e); } @@ -64,14 +73,14 @@ private static boolean loadEmbeddedLibrary() { URL nativeLibraryUrl = null; // loop through extensions, stopping after finding first one - for (String lib: libs) { + for (String lib : libs) { nativeLibraryUrl = Module.class.getResource(url + lib); if (nativeLibraryUrl != null) { break; } } - + if (nativeLibraryUrl != null) { // native library found within JAR, extract and load try {