使用Maven绑定可运行的.jar中的本地依赖项 [英] Bundle native dependencies in runnable .jar with Maven

查看:222
本文介绍了使用Maven绑定可运行的.jar中的本地依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个在Maven中管理的项目,其中有一些本机依赖关系 LWJGL

I have a project managed in Maven that has some native dependencies (LWJGL).

开发中的一切工作正常,但现在我要设置Maven,以便构建一个可重新分发的可运行的.jar文件。特别是,我希望用户可以轻松地运行应用程序,而不必乱写图书馆路径或打开本机库等。

Everything works fine in development, but now I want to set up Maven so that it will build a runnable .jar file that I can redistribute. In particular, I want it to be very easy for users to run the app without having to mess around with library paths or unpacking native libraries etc.

目前我可以构建一个包含所有依赖项的.jar文件,但是如果我运行它(不出意料之间),我得到一个不满意的链接错误:

Currently I am able to build a .jar file that includes all the dependencies, but if I run it then (unsurprisingly) I get an unsatisfied link error:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no lwjgl in java.libr
ary.path
        at java.lang.ClassLoader.loadLibrary(Unknown Source)
        at java.lang.Runtime.loadLibrary0(Unknown Source)
        at java.lang.System.loadLibrary(Unknown Source)
        at org.lwjgl.Sys$1.run(Sys.java:73)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.lwjgl.Sys.doLoadLibrary(Sys.java:66)
        at org.lwjgl.Sys.loadLibrary(Sys.java:95)
        at org.lwjgl.Sys.<clinit>(Sys.java:112)
        at org.lwjgl.opengl.Display.<clinit>(Display.java:132)
        at glaze.TestApp.start(TestApp.java:10)
        at glaze.TestApp.main(TestApp.java:31)

显然,我可以通过手动安装本机库并运行jar,使用 java -Djava.library.path = / path / to / libs 但这不是我可以期望我的用户做的。

Obviously I can make it work by manually installing the native libraries and running the jar with java -Djava.library.path=/path/to/libs but that isn't something I can expect my users to do.

这是pom.xml,以防它是相关的: https://github.com/mikera/glaze/blob/master/pom.xml

Here's the pom.xml in case it is relevant: https://github.com/mikera/glaze/blob/master/pom.xml

可以设置Maven,以便它将创建一个可运行的.jar,它包含本机依赖项,​​并在双击时运行成功?

It is possible to set up Maven so that it will create a runnable .jar that includes the native dependencies and will run successfully when double-clicked?

推荐答案

这是我以前用来加载 dll 的代码在jar中捆绑的库。

This is some code I used to use to load dll or so libraries that are bundled in the jar.

库必须作为资源添加。我们使用maven并将它们放在这个层次结构中:

The libraries must be added as resources. We used maven and put them in this hierarchy:

src/main/resources/lib/win-x86/<dlls for 32-bit windows>
src/main/resources/lib/linux-x86/<so for 32-bit linux>
src/main/resources/lib/linux-x86_64/<so for 64-bit linux>
src/main/resources/lib/linux-ia64/<so for 64-bit linux on itanium>

共享库将被解压缩到平台的tmp目录,并且还有一个临时名称解压。这是为了让几个进程加载dll,所以没有共享实际提取的dll /所以,因为解包可以覆盖现有的,如果具有相同的名称(在文件被替换时,在某些平台上有非常奇怪的行为)。

The shared libraries will be unpacked to the tmp-directory for the platform and also have a temporary name when unpacked. This is to let several processes load the dll/so without sharing the actual extracted dll/so since the unpacking could overwrite existing ones if having the same name (with very strange behavior on some platforms when the file was replaced).

该文件也设置为 deleteOnExit 设置,但在Windows AFAIK上不起作用。

The file is also set to have deleteOnExit set but that does not work on windows AFAIK.

NativeLoader.java

public class NativeLoader {

    public static final Logger LOG = Logger.getLogger(NativeLoader.class);

    public NativeLoader() {
    }

    public void loadLibrary(String library) {
        try {
            System.load(saveLibrary(library));
        } catch (IOException e) {
            LOG.warn("Could not find library " + library +
                    " as resource, trying fallback lookup through System.loadLibrary");
            System.loadLibrary(library);
        }
    }


    private String getOSSpecificLibraryName(String library, boolean includePath) {
        String osArch = System.getProperty("os.arch");
        String osName = System.getProperty("os.name").toLowerCase();
        String name;
        String path;

        if (osName.startsWith("win")) {
            if (osArch.equalsIgnoreCase("x86")) {
                name = library + ".dll";
                path = "win-x86/";
            } else {
                throw new UnsupportedOperationException("Platform " + osName + ":" + osArch + " not supported");
            }
        } else if (osName.startsWith("linux")) {
            if (osArch.equalsIgnoreCase("amd64")) {
                name = "lib" + library + ".so";
                path = "linux-x86_64/";
            } else if (osArch.equalsIgnoreCase("ia64")) {
                name = "lib" + library + ".so";
                path = "linux-ia64/";
            } else if (osArch.equalsIgnoreCase("i386")) {
                name = "lib" + library + ".so";
                path = "linux-x86/";
            } else {
                throw new UnsupportedOperationException("Platform " + osName + ":" + osArch + " not supported");
            }
        } else {
            throw new UnsupportedOperationException("Platform " + osName + ":" + osArch + " not supported");
        }

        return includePath ? path + name : name;
    }

    private String saveLibrary(String library) throws IOException {
        InputStream in = null;
        OutputStream out = null;

        try {
            String libraryName = getOSSpecificLibraryName(library, true);
            in = this.getClass().getClassLoader().getResourceAsStream("lib/" + libraryName);
            String tmpDirName = System.getProperty("java.io.tmpdir");
            File tmpDir = new File(tmpDirName);
            if (!tmpDir.exists()) {
                tmpDir.mkdir();
            }
            File file = File.createTempFile(library + "-", ".tmp", tmpDir);
            // Clean up the file when exiting
            file.deleteOnExit();
            out = new FileOutputStream(file);

            int cnt;
            byte buf[] = new byte[16 * 1024];
            // copy until done.
            while ((cnt = in.read(buf)) >= 1) {
                out.write(buf, 0, cnt);
            }
            LOG.info("Saved libfile: " + file.getAbsoluteFile());
            return file.getAbsolutePath();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ignore) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ignore) {
                }
            }
        }
    }
}

通过创建一个 NativeLoader 的实例,然后调用 loadLibrary (thelibrary)没有os特定的前缀和扩展名。

The libraries are loaded by creating an instance of the NativeLoader and then by calling loadLibrary("thelibrary") without the os-specific prefixes and extensions.

这对我们来说很好,但是你必须添加共享库手动到不同的资源目录,然后构建jar。

This worked well for us but you will have to add the shared libraries manually to the different resource directories and then build the jar.

我意识到这个类中的一些代码可能是奇怪的或过时的,但是请记住,这是我写的代码几年前,它一直工作得很好。

I realize that some code in this class may be strange or obsolete but bare in mind that this is code I wrote some years ago and it has been working really well.

这篇关于使用Maven绑定可运行的.jar中的本地依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆