我有一个在 Maven 中管理的项目,它具有一些 native 依赖项 ( LWJGL )。
在开发中一切正常,但现在我想设置 Maven,以便它构建一个我可以重新分发的可运行 .jar 文件。特别是,我希望用户可以非常轻松地运行该应用程序,而不必弄乱库路径或解压缩 native 库等。
目前我能够构建一个包含所有依赖项的 .jar 文件,但是如果我运行它然后(不出所料)我会收到一个未满足的链接错误:
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)
显然,我可以通过手动安装 native 库并使用 java -Djava.library.path=/path/to/libs
运行 jar 来使其工作,但这不是我可以期待的我的用户要做。
这是相关的 pom.xml:https://github.com/mikera/glaze/blob/master/pom.xml
是否可以设置 Maven,使其创建一个包含 native 依赖项的可运行 .jar,并在双击时成功运行?
最佳答案
这是我用来加载捆绑在 jar 中的 dll
或 so
库的一些代码。
库必须作为资源添加。我们使用了 maven 并将它们放在这个层次结构中:
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/so 的情况下加载 dll/so,因为如果具有相同的名称,解压可能会覆盖现有的 dll/so(当文件被替换时,在某些平台上会出现非常奇怪的行为)。
该文件还设置了 deleteOnExit
设置,但这在 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")
而不使用操作系统特定的前缀和扩展名。
这对我们来说效果很好,但您必须手动将共享库添加到不同的资源目录,然后构建 jar。
我意识到此类中的某些代码可能很奇怪或已过时,但请记住,这是我几年前编写的代码,并且一直运行良好。
关于java - 使用 Maven 将 native 依赖项捆绑在可运行的 .jar 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12036607/