Skip to content

Commit

Permalink
Shim vips library loading to fix Windows lookup, and allow multiple a…
Browse files Browse the repository at this point in the history
…ttempts (#57)
  • Loading branch information
lopcode authored Sep 20, 2024
1 parent 221bdb8 commit 2812ba6
Show file tree
Hide file tree
Showing 21 changed files with 155 additions and 36 deletions.
33 changes: 31 additions & 2 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,41 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 8.9
gradle-version: 8.10.1

- name: Run checks
run: ./gradlew build check shadowJar

- name: Run samples
run: |
vips --version || (sudo apt update && sudo apt install libvips-dev)
./run_samples.sh
./run_samples.sh
windows-sense-check:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: 22

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 8.10.1

- name: Run samples
shell: pwsh
run: |
Invoke-WebRequest "https://github.com/libvips/build-win64-mxe/releases/download/v8.15.3/vips-dev-w64-web-8.15.3-static-ffi.zip" -OutFile "vips_w64.zip"
(Get-FileHash .\vips_w64.zip).Hash -eq "60500d990c6063a1e95f1ff29a540c5943537ccbed4dd55711947b68229774ee"
Expand-Archive vips_w64.zip
$env:Path = "$pwd\vips_w64\vips-dev-8.15;$pwd\vips_w64\vips-dev-8.15\bin;" + $env:Path
Write-Output $env:Path
Get-ChildItem .\vips_w64\vips-dev-8.15
.\gradlew.bat sample:clean sample:shadowJar
java -jar sample/build/libs/sample-all.jar
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 8.9
gradle-version: 8.10.1

- name: Run checks
run: ./gradlew build check shadowJar
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ build

jextract*
logo*
native_libs/lib*
includes.txt
includes_filtered.txt
hs_err*
Expand Down
2 changes: 1 addition & 1 deletion .run/Discover Vips operations.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Discover Vips operations" type="JetRunConfigurationType">
<envs>
<env name="DYLD_LIBRARY_PATH" value="native_libs" />
<env name="DYLD_LIBRARY_PATH" value="/opt/homebrew/lib" />
</envs>
<option name="MAIN_CLASS_NAME" value="vipsffm.DiscoverVipsOperationsKt" />
<module name="vips-ffm.generator.main" />
Expand Down
2 changes: 1 addition & 1 deletion .run/Generate V classes.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Generate V classes" type="JetRunConfigurationType">
<envs>
<env name="DYLD_LIBRARY_PATH" value="native_libs" />
<env name="DYLD_LIBRARY_PATH" value="/opt/homebrew/lib" />
</envs>
<option name="MAIN_CLASS_NAME" value="vipsffm.GenerateVClasses" />
<module name="vips-ffm.generator.main" />
Expand Down
2 changes: 1 addition & 1 deletion .run/Generate VipsHelper class.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Generate VipsHelper class" type="JetRunConfigurationType">
<envs>
<env name="DYLD_LIBRARY_PATH" value="native_libs" />
<env name="DYLD_LIBRARY_PATH" value="/opt/homebrew/lib" />
</envs>
<option name="MAIN_CLASS_NAME" value="vipsffm.GenerateVipsHelperClass" />
<module name="vips-ffm.generator.main" />
Expand Down
2 changes: 1 addition & 1 deletion .run/Run samples.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run samples" type="JetRunConfigurationType">
<envs>
<env name="DYLD_LIBRARY_PATH" value="native_libs" />
<env name="DYLD_LIBRARY_PATH" value="/opt/homebrew/lib" />
</envs>
<option name="MAIN_CLASS_NAME" value="vipsffm.SampleRunner" />
<module name="vips-ffm.sample.main" />
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repositories {
}

dependencies {
implementation("app.photofox.vips-ffm:vips-ffm-core:0.4.0")
implementation("app.photofox.vips-ffm:vips-ffm-core:0.5.0")
}
```

Expand Down
2 changes: 0 additions & 2 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ tasks.withType<Test> {
exceptionFormat = TestExceptionFormat.FULL
}
jvmArgs("--enable-native-access=ALL-UNNAMED")
environment(mapOf("DYLD_LIBRARY_PATH" to "native_libs"))
outputs.upToDateWhen { false }
}

Expand All @@ -61,7 +60,6 @@ tasks.named("check") {

tasks.withType<JavaExec>().configureEach {
jvmArgs("--enable-native-access=ALL-UNNAMED")
environment(mapOf("DYLD_LIBRARY_PATH" to "native_libs"))
javaLauncher.set(project.javaToolchains.launcherFor(java.toolchain))
}

Expand Down
87 changes: 87 additions & 0 deletions core/src/main/java/app/photofox/vipsffm/VipsLibLookup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package app.photofox.vipsffm;

import java.lang.foreign.Arena;
import java.lang.foreign.SymbolLookup;
import java.util.List;
import java.util.Optional;

public class VipsLibLookup {

public static SymbolLookup buildSymbolLoader(Arena arena) {
var vipsLoader = findVipsLoader(arena);
if (vipsLoader == null) {
throw new UnsatisfiedLinkError("could not make loader for libvips");
}
var glibLoader = findGlibLoader(arena);
if (glibLoader == null) {
throw new UnsatisfiedLinkError("could not make loader for glib");
}
var gobjectLoader = findGObjectLoader(arena);
if (gobjectLoader == null) {
throw new UnsatisfiedLinkError("could not make loader for gobject");
}

return vipsLoader.or(glibLoader).or(gobjectLoader);
}

private static SymbolLookup findVipsLoader(Arena arena) {
var abiNumber = Optional.ofNullable(System.getProperty("vipsffm.abinumber.vips.override"))
.orElse("42");
var names = List.of(
"vips", // default unix-like
"vips." + abiNumber, // some linux systems don't symlink and need abi number
"libvips-" + abiNumber // windows needs everything
);
for (var name : names) {
var attempt = attemptLibraryLookup(name, arena);
if (attempt.isPresent()) {
return attempt.get();
}
}
return null;
}

private static SymbolLookup findGlibLoader(Arena arena) {
var abiNumber = Optional.ofNullable(System.getProperty("vipsffm.abinumber.glib.override"))
.orElse("0");
var names = List.of(
"glib-2.0", // default unix-like
"glib-2.0." + abiNumber, // some linux systems don't symlink and need abi number
"libglib-2.0-" + abiNumber // windows needs everything
);
for (var name : names) {
var attempt = attemptLibraryLookup(name, arena);
if (attempt.isPresent()) {
return attempt.get();
}
}
return null;
}

private static SymbolLookup findGObjectLoader(Arena arena) {
var abiNumber = Optional.ofNullable(System.getProperty("vipsffm.abinumber.gobject.override"))
.orElse("0");
var names = List.of(
"gobject-2.0", // default unix-like
"gobject-2.0." + abiNumber, // some linux systems don't symlink and need abi number
"libgobject-2.0-" + abiNumber // windows needs everything
);
for (var name : names) {
var attempt = attemptLibraryLookup(name, arena);
if (attempt.isPresent()) {
return attempt.get();
}
}
return null;
}

static Optional<SymbolLookup> attemptLibraryLookup(String name, Arena arena) {
try {
return Optional.of(
SymbolLookup.libraryLookup(System.mapLibraryName(name), arena)
);
} catch (IllegalArgumentException _) {
return Optional.empty();
}
}
}
6 changes: 2 additions & 4 deletions core/src/main/java/app/photofox/vipsffm/jextract/VipsRaw.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
// Generated by jextract

package app.photofox.vipsffm.jextract;
import app.photofox.vipsffm.VipsLibLookup;

import java.lang.invoke.*;
import java.lang.foreign.*;
import java.nio.ByteOrder;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

import static java.lang.foreign.ValueLayout.*;
import static java.lang.foreign.MemoryLayout.PathElement.*;

public class VipsRaw {

Expand Down Expand Up @@ -55,7 +53,7 @@ static MemoryLayout align(MemoryLayout layout, long align) {
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("vips"), LIBRARY_ARENA)
static final SymbolLookup SYMBOL_LOOKUP = VipsLibLookup.buildSymbolLoader(LIBRARY_ARENA)
.or(SymbolLookup.loaderLookup())
.or(Linker.nativeLinker().defaultLookup());

Expand Down
2 changes: 0 additions & 2 deletions generate_ffm_bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,4 @@ set -x
@includes_filtered.txt \
"$LIBVIPS_ENTRY_PATH"

export DYLD_LIBRARY_PATH="native_libs"

./gradlew clean generator:run
5 changes: 4 additions & 1 deletion generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ tasks.withType<Test> {

application {
mainClass = "vipsffm.MainKt"
applicationDefaultJvmArgs = listOf("--enable-preview", "--enable-native-access=ALL-UNNAMED", "-Xlog:library", "-Djava.library.path=${rootProject.projectDir}/native_libs")
applicationDefaultJvmArgs = listOf(
"--enable-preview",
"--enable-native-access=ALL-UNNAMED"
)
tasks.run.get().workingDir = rootProject.projectDir
}
2 changes: 0 additions & 2 deletions generator/src/main/kotlin/vipsffm/GenerateVClasses.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ object GenerateVClasses {
private val vNamedEnumType = ClassName.get("app.photofox.vipsffm", "VNamedEnum")

@JvmStatic fun main(args: Array<String>) {
System.loadLibrary("vips")

val operations = Arena.ofConfined().use {
DiscoverVipsOperations.run(it)
}
Expand Down
1 change: 1 addition & 0 deletions generator/src/main/kotlin/vipsffm/Main.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package vipsffm

fun main(args: Array<String>) {
ReplaceVipsLoader.main(args)
GenerateVipsHelperClass.main(args)
GenerateVClasses.main(args)
}
22 changes: 22 additions & 0 deletions generator/src/main/kotlin/vipsffm/ReplaceVipsLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package vipsffm

import java.nio.file.Files
import java.nio.file.Path

object ReplaceVipsLoader {

@JvmStatic fun main(args: Array<String>) {
val vipsRawPath = Path.of("core/src/main/java/app/photofox/vipsffm/jextract/VipsRaw.java")

val content = String(Files.readAllBytes(vipsRawPath), Charsets.UTF_8)
.replace(
"SymbolLookup.libraryLookup(System.mapLibraryName(\"vips\"), LIBRARY_ARENA)",
"VipsLibLookup.buildSymbolLoader(LIBRARY_ARENA)"
)
.replace(
"package app.photofox.vipsffm.jextract;\n\n",
"package app.photofox.vipsffm.jextract;\nimport app.photofox.vipsffm.VipsLibLookup;\n\n",
)
Files.write(vipsRawPath, content.toByteArray(Charsets.UTF_8))
}
}
2 changes: 1 addition & 1 deletion 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.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Empty file removed native_libs/.gitkeep
Empty file.
2 changes: 1 addition & 1 deletion run_samples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ echo "building samples..."
./gradlew sample:clean sample:shadowJar

if [[ "$OSTYPE" == "darwin"* ]]; then
export DYLD_LIBRARY_PATH=native_libs
export DYLD_LIBRARY_PATH=/opt/homebrew/lib
fi

echo "running samples..."
Expand Down
3 changes: 0 additions & 3 deletions sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,10 @@ tasks.withType<Test> {
events = TestLogEvent.values().toSet() - TestLogEvent.STARTED
exceptionFormat = TestExceptionFormat.FULL
}
environment(mapOf("DYLD_LIBRARY_PATH" to "native_libs"))
outputs.upToDateWhen { false }
}

tasks.withType<JavaExec>().configureEach {
environment(mapOf("DYLD_LIBRARY_PATH" to "native_libs"))
javaLauncher.set(project.javaToolchains.launcherFor(java.toolchain))
}

Expand All @@ -58,5 +56,4 @@ tasks.withType<Jar>().configureEach {
application {
mainClass = "vipsffm.SampleRunner"
applicationDefaultJvmArgs = listOf("--enable-native-access=ALL-UNNAMED")
// todo: figure out how to set DYLD_LIBRARY_PATH here
}
11 changes: 0 additions & 11 deletions setup_lib_symlinks.sh

This file was deleted.

0 comments on commit 2812ba6

Please sign in to comment.