Skip to content

fix: Update LibNames to load candidates in order#96

Merged
chriswk merged 9 commits intomainfrom
graal-musl-static-load
Mar 12, 2026
Merged

fix: Update LibNames to load candidates in order#96
chriswk merged 9 commits intomainfrom
graal-musl-static-load

Conversation

@chriswk
Copy link
Copy Markdown
Member

@chriswk chriswk commented Mar 11, 2026

this makes it possible to build static graal musl images, which failed in #95

fixes: #95

Copilot AI review requested due to automatic review settings March 11, 2026 11:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates native library selection on Linux to better detect musl-based environments (notably statically linked GraalVM + Alpine), preventing the wrong .so from being loaded (fixes #95).

Changes:

  • Refactors LibNames to support libc overrides (system property/env), filesystem musl markers, and /proc/self/maps as a final fallback.
  • Adds focused unit tests for libc override and marker/fallback behavior.
  • Bumps yggdrasilCoreVersion and the Java engine version in gradle.properties.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
java-engine/src/main/java/io/getunleash/engine/LibNames.java Implements ordered musl detection via overrides → marker paths → /proc/self/maps fallback.
java-engine/src/test/java/io/getunleash/engine/LibNamesTest.java Adds unit tests validating selection for musl/glibc and marker/fallback behavior.
java-engine/gradle.properties Updates core/native version and published Java engine version.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread java-engine/src/main/java/io/getunleash/engine/LibNames.java Outdated
Comment thread java-engine/src/main/java/io/getunleash/engine/LibNames.java
Comment thread java-engine/src/main/java/io/getunleash/engine/LibNames.java Outdated
@chriswk chriswk requested a review from sighphyre March 11, 2026 11:17
@chriswk chriswk self-assigned this Mar 11, 2026
Copilot AI review requested due to automatic review settings March 11, 2026 11:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +112 to +116
if (value == null) {
return null;
}

String normalized = value.trim().toLowerCase(Locale.ROOT);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseLibcOverride writes directly to stderr for unsupported values. This library already uses SLF4J elsewhere, and writing to stderr can be noisy/uncontrollable for consumers (especially in serverless/Graal native images). Consider using the project logger (e.g., LOGGER.warn(...)) or silently ignoring unsupported values (or guarding logging behind a debug flag).

Copilot uses AI. Check for mistakes.
Comment on lines +134 to +136
}

private static boolean fileExists(String path) {
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapsContainMusl logs to stderr on any IOException reading /proc/self/maps. This can generate unexpected console output in restricted/containerized environments; prefer using the project logger at an appropriate level, or suppressing the warning (since falling back to false is already handled).

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +21
@Test
void propertyOverrideWinsOverMuslMarkers() {
String libName = LibNames.pickFor("Linux", "amd64", "glibc", null, path -> true, () -> true);

assertEquals("libyggdrasilffi_x86_64.so", libName);
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LibNames.pickFor now supports UNLEASH_ENGINE_LIBC (env override) and a defined precedence (system property first, then env). There isn’t a test covering the env override path (including that env is ignored when the property is set). Adding a couple of assertions here would help prevent regressions.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings March 11, 2026 13:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

java-engine/src/main/java/io/getunleash/engine/LibNames.java:156

  • mapsContainMusl() only catches IOException, but opening/reading /proc/self/maps can also throw SecurityException in restricted environments. That would propagate and fail native library selection/loading. Consider catching SecurityException (or RuntimeException) here as well and treating it the same as the IOException path.
  private static boolean mapsContainMusl() {
    try (BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"))) {
      String line;
      while ((line = reader.readLine()) != null) {
        if (line.contains("musl")) {
          return true;
        }
      }
    } catch (IOException e) {
      System.err.println(
          "Warning: Failed to read /proc/self/maps, assuming this is not a musl system: "
              + e.getMessage());
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread java-engine/src/main/java/io/getunleash/engine/LibNames.java
Copilot AI review requested due to automatic review settings March 11, 2026 13:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +142 to +146
System.err.println(
"Warning: Security manager blocked access to '"
+ path
+ "' while detecting libc. Assuming the file does not exist.");
return false;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileExists prints a warning to System.err on SecurityException. Elsewhere in this module, warnings go through SLF4J (LOGGER.warn, e.g. FlatInterface.java:29). Please use the shared logging approach (or avoid printing from a utility method) to prevent unexpected stderr output in consumer applications.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +21
* <p>Supported values:
*
* <ul>
* <li>{@code "musl"} – force musl libc selection
* <li>{@code "glibc"} – force glibc selection
* </ul>
*
* Any other value (including {@code null}) will fall back to automatic detection.
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc for the libc override says only "musl" and "glibc" are supported, but parseLibcOverride also treats "gnu" and "gnu libc" as aliases for glibc. Please either document these aliases here (and in the env var Javadoc) or remove the alias handling so the docs and behavior match.

Copilot uses AI. Check for mistakes.
"/usr/lib/ld-musl.so.1"
};

private static Boolean parseLibcOverride(String value) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify and return a boolean? Instead of null, we can return false?

Although this is coupled with isMusl... so this makes the whole thing a bit uneasy to read or understand...

I'd design parseLibcOverride to return null or a valid libc version (as an enum) or something else, cause now we're using a ternary result true, false, or null, which in the context of parseLibcOverride is not very clear what each response means without spending some time reading the code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can have a look here. this is the least interesting thing, it's the extra bit AI added because it insisted it was better to have and not need it. Since we're running a library it isn't really needed, I might be able to just delete it.

@gastonfournier gastonfournier moved this from New to In Progress in Issues and PRs Mar 12, 2026
@chriswk chriswk force-pushed the graal-musl-static-load branch from d826c13 to a380b24 Compare March 12, 2026 08:37
Copilot AI review requested due to automatic review settings March 12, 2026 08:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread java-engine/src/main/java/io/getunleash/engine/LibNames.java
Comment on lines +126 to +141

private static Optional<LibcVersion> parseLibcOverride(String value) {
if (value == null) {
return Optional.empty();
}

String normalized = value.trim().toLowerCase(Locale.ROOT);
switch (normalized) {
case "":
case "glibc":
case "gnu":
case "gnu libc":
return Optional.of(LibcVersion.Glibc);
case "musl":
return Optional.of(LibcVersion.Musl);
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseLibcOverride treats an empty/whitespace-only override value ("") as an explicit glibc override, but the Javadoc above states that any other value (including null) falls back to auto-detection. This can also cause surprising behavior if the property/env is set but empty. Consider treating empty/blank as "no override" (return Optional.empty()), or update the Javadoc to match the intended behavior.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@gastonfournier gastonfournier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine, maybe https://github.com/Unleash/yggdrasil-bindings/pull/96/changes#r2923105429 is intentional, and https://github.com/Unleash/yggdrasil-bindings/pull/96/changes#r2917705839 is cosmetic, but I remember @sighphyre wanted to do some testing, let's make sure we're aligned.

@github-project-automation github-project-automation Bot moved this from In Progress to Approved PRs in Issues and PRs Mar 12, 2026
Copilot AI review requested due to automatic review settings March 12, 2026 13:47
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

/**
* Environment variable used to override libc detection.
*
* <p>Supported values are the same as for {@link #LIBC_PROPERTY}: {@code "musl"}, {@code "glibc"}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the LIBC_ENV Javadoc, the supported-values list is missing punctuation between "glibc" and "gnu", which makes the sentence read oddly. Consider adding a comma/"and" so the list is unambiguous.

Suggested change
* <p>Supported values are the same as for {@link #LIBC_PROPERTY}: {@code "musl"}, {@code "glibc"}
* <p>Supported values are the same as for {@link #LIBC_PROPERTY}: {@code "musl"}, {@code "glibc"},

Copilot uses AI. Check for mistakes.
chriswk and others added 2 commits March 12, 2026 15:08
this makes it possible to build static graal musl images, which failed in #95

fixes: #95
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@chriswk chriswk force-pushed the graal-musl-static-load branch from 996adbb to 991f13a Compare March 12, 2026 14:08
Copilot AI review requested due to automatic review settings March 12, 2026 14:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +90 to +94
static LibcVersion libcVersion(
String libcProperty,
String libcEnv,
Predicate<String> fileExists,
BooleanSupplier mapsContainMusl) {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libcVersion(...) is declared with package-private visibility but returns the private nested type LibcVersion. This makes the method effectively unusable outside LibNames and is confusing API surface; consider making libcVersion private (or widening LibcVersion visibility if it’s intentionally part of the package API).

Copilot uses AI. Check for mistakes.
Comment on lines 66 to +72
} else if (os.contains("win")) {
if (arch.contains("arm64")) return "yggdrasilffi_arm64.dll";
if (arch.contains("64")) return "yggdrasilffi_x86_64.dll";
if (arch.contains("arm64")) {
return "yggdrasilffi_arm64.dll";
}
if (arch.contains("64")) {
return "yggdrasilffi_x86_64.dll";
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows ARM64 detection only checks for "arm64", but os.arch is commonly "aarch64" on Windows ARM. In that case this will fall through to the arch.contains("64") branch and incorrectly pick the x86_64 DLL. Consider treating aarch64 as ARM64 here (consistent with the macOS/Linux branches).

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +38
* <p>Supported values are the same as for {@link #LIBC_PROPERTY}: {@code "musl"}, {@code "glibc"}
* {@code "gnu"}, {@code "gnu libc"}. Any other value (including unset) will fall back to
* automatic detection.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc list of supported values for the env override is hard to read because the inline {@code ...} values are split across lines without clear separators. Consider converting it to a bullet list (like the property Javadoc above) or adding clear punctuation so it renders unambiguously.

Suggested change
* <p>Supported values are the same as for {@link #LIBC_PROPERTY}: {@code "musl"}, {@code "glibc"}
* {@code "gnu"}, {@code "gnu libc"}. Any other value (including unset) will fall back to
* automatic detection.
* <p>Supported values are the same as for {@link #LIBC_PROPERTY}:
*
* <ul>
* <li>{@code "musl"} – force musl libc selection
* <li>{@code "glibc"} – force glibc selection
* <li>{@code "gnu"} - alias for glibc
* <li>{@code "gnu libc"} - alias for glibc
* </ul>
*
* Any other value (including unset) will fall back to automatic detection.

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +43
void propertyOverrideWorksWithAliases() {
String libcProperty = LibNames.pickFor("Linux", "amd64", "gnu", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv = LibNames.pickFor("Linux", "amd64", null, "gnu", path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcEnv);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file isn’t formatted according to the repo’s Spotless/Google Java Format settings (several long statements remain on a single line). Running ./gradlew :java-engine:spotlessApply (or reformatting) should wrap these lines and avoid spotlessCheck failures in CI.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +51
String libcProperty = LibNames.pickFor("Linux", "amd64", "gnu", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv = LibNames.pickFor("Linux", "amd64", null, "gnu", path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcEnv);
}

@Test
void gnuLibcIsAlsoAValidAlias() {
String libcProperty =
LibNames.pickFor("Linux", "amd64", "gnu libc", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv = LibNames.pickFor("Linux", "amd64", null, "gnu libc", path -> true, () -> true);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional long lines here are also likely to violate the project’s Google Java Format enforcement (Spotless). Reformatting the file should wrap these calls consistently with the rest of the codebase.

Suggested change
String libcProperty = LibNames.pickFor("Linux", "amd64", "gnu", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv = LibNames.pickFor("Linux", "amd64", null, "gnu", path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcEnv);
}
@Test
void gnuLibcIsAlsoAValidAlias() {
String libcProperty =
LibNames.pickFor("Linux", "amd64", "gnu libc", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv = LibNames.pickFor("Linux", "amd64", null, "gnu libc", path -> true, () -> true);
String libcProperty =
LibNames.pickFor(
"Linux", "amd64", "gnu", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv =
LibNames.pickFor(
"Linux", "amd64", null, "gnu", path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcEnv);
}
@Test
void gnuLibcIsAlsoAValidAlias() {
String libcProperty =
LibNames.pickFor(
"Linux", "amd64", "gnu libc", null, path -> true, () -> true);
assertEquals("libyggdrasilffi_x86_64.so", libcProperty);
String libcEnv =
LibNames.pickFor(
"Linux", "amd64", null, "gnu libc", path -> true, () -> true);

Copilot uses AI. Check for mistakes.
@chriswk chriswk merged commit 0a326e7 into main Mar 12, 2026
7 checks passed
@chriswk chriswk deleted the graal-musl-static-load branch March 12, 2026 14:29
@github-project-automation github-project-automation Bot moved this from Approved PRs to Done in Issues and PRs Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

isMusl does not detect musl when running on alpine with GraalVM

4 participants