决定用 Java 构建 Linux 的 GNU 或 MUSL

Gau*_*ani 5 java native glibc jna musl

我有一个 Java 桌面应用程序,应该可以在 GNU Linux 发行版(Debian 和 Ubuntu)和 MUSL Linux 发行版(Alpine)中运行。我的应用程序也使用本机库,并且两种类型的 Linux 发行版的本机库构建都不同。

我会将两者与我的申请放在不同的文件夹中。因此,在运行时,Java 程序需要根据 Linux(GNU 或 MUSL)选择正确的本地库发行版。

我没有找到任何机制来了解 Java 程序中运行的是哪个 Linux 发行版 JVM。

我正在考虑从 Linux 的 /etc/ 文件夹中读取操作系统文件的一种方法。但我认为这不是一个好的解决方案(因为某些自定义构建可能会更改此细节),有人可以为这个问题建议一些更好的解决方案吗?或者如何做到这一点?

Dan*_*dis 7

使用Java/JNA,您可以映射gnu_get_libc_version()函数并尝试在加载后执行它libc。如果它有效,那么您就使用了 glibc (GNU)。如果您发现UnsatisfiedLinkError该函数未找到,则说明您使用的是其他 libc。

映射函数:

public interface Libc extends Library {
    Libc INSTANCE = Native.load("c", Libc.class);

    String gnu_get_libc_version();
}
Run Code Online (Sandbox Code Playgroud)

叫它:

public class GnuOrMusl {
    public static void main(String[] args) {
        try {
            System.out.println("On GNU libc version " + Libc.INSTANCE.gnu_get_libc_version());
        } catch (UnsatisfiedLinkError e) {
            System.out.println("Not on glibc!");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果您处于具有 glibc 兼容性的容器化环境中,此检测可能会指示 glibc,但这会引发一个问题:哪种版本的库适合该环境。

可能有类似的方法具有独特的功能来区分其他 libc 变体和 MUSL,但据我所知,MUSL 试图如此符合标准,以至于它实际上不允许识别自己。


查找 GNU 发行版的另一个选项是uname -o可以使用 ProcessBuilder 执行的命令。

在非 GNU (Alpine) 上它只是“Linux”,而在 Ubuntu、Debian 和 OpenSUSE 上它是“GNU/Linux”。


/lib*您还可以通过迭代目录查找 libc 变体来成功确定 GNU 与 MUSL 。这与编译 JDK 时所采用的方法类似,后者执行ldd命令并从输出中解析库。

例如,迭代/libAlpine linux 中的目录会给出以下链接:libc.musl-x86_64.so.1 -> ld-musl-x86_64.so.1

在 Debian 中/lib32libc.so.6 -> libc-2.28.so,在 OpenSUSE 中/lib64我看到类似的东西:libc.so.6 -> libc-2.26.so,而 Ubuntu 中/lib/aarch64-linux-gnulibc-2.27.so.

如果您停留在 Java 领域,确定/lib搜索路径可能需要一些尝试和错误。解析命令行的输出ldd `which ls`可能会得到一个包含gnuor的字符串musl


至于确定使用哪个 Linux 发行版,从/etc文件夹中读取是一个很好的直觉。我管理基于 Java 的操作系统和硬件信息 (OSHI) 项目,并浏览了几乎所有选项来确定您正在运行哪个发行版。您可以在本课程中看到所有劳动成果。

我将引用该文件中的评论:

系列/版本信息有两个相互竞争的选项。较新的系统正在采用标准 /etc/os-release 文件: https://www.freedesktop.org/software/systemd/man/os-release.html

某些系统仍在使用 lsb 标准,该标准解析各种 /etc/*-release 文件,并且最容易通过命令行 lsb_release -a 访问,请参见此处: https: //linux.die.net/man/1/lsb_release在这种情况下,/etc/lsb-release 文件(如果存在)可以选择覆盖 /etc/distrib-release 文件中的信息,其中显示:“Distributor release xx(代号)”

代码的逻辑是:

  1. 试图/etc/system-release
  2. 试图/etc/os-release
  3. 运行lsb_release命令
  4. /etc/lsb-release
  5. 查找并读取任何/etc/*-release文件。

这些文件包含类似的密钥NAME,可以帮助您解决问题。

请随意复制和使用该文件或变体,或者仅使用该项目作为依赖项。