将本地系统库与SBT集成

31

如何很好地将各种SBT任务与本地库(例如JOGLLWGLJCuda)集成?具体而言,

  1. 有没有一种推荐的方式来在run任务中包含本地库? SBT邮件列表上的讨论提出了以下几种可能性:

    最后一种方法的优点是run不需要分叉,但缺点是配置必须在SBT之外进行。

  2. 我可以自动在sbteclipse插件生成的Eclipse项目中包含本地库吗?可以在后处理步骤中重写.project文件。有没有示例代码?是否有更好的方法?

  3. 本地库能否包含在由插件生成的可运行Jar中,例如sbt-assemblysbt-onejarsbt-proguard

我认为SBT没有直接的本地库设置。如果有这样的设置,那么上述任务是否可以透明地处理本地库?

3个回答

30

根据我之前所做的研究,只有两种方法可以加载本地库:修改java.library.path并使用System.loadLibrary(我觉得大多数人都是这么做的),或者使用带有绝对路径的System.load(感觉不太常用)。

正如你所指出的那样,操作java.library.path在配置SBT和Eclipse方面可能会很麻烦,并且我认为无法自动地为可执行的JAR文件进行配置。

因此,只能使用System.load。如果要编写自己的本地库,可以进行以下操作:

  • 创建一个SBT任务,编译您的本地源代码(使用javahgcc),获取生成的.so文件以及它所依赖的任何.so文件,将它们放入目标目录下的一个jar文件中(作为资源),并将该jar文件的路径添加到Compile 中的unmanagedJars
  • 创建一个Scala方法来加载库。它将使用Class.getResourceAsStream来读取库,File.createTempFile在文件系统上的某个地方编写它,然后使用System.load将其加载到JVM中。
  • 现在,不要像以前那样调用System.loadLibrary,而是调用MyClasspathJniLoader.loadLibrary

这将适用于SBT运行、Eclipse和可执行的JAR文件,无需进行任何额外的配置(尽管我不知道proguard如何知道包含哪些资源)。

至于已经编写好的第三方本地库,其中一些,例如jblas,已经采用了这种“fat jar”方法。如果它们希望您设置java.library.path,然后他们有需要时调用System.loadLibrary,则需要进行一些魔法才能使其正常工作。

我没有尝试过这个解决方案,但这个解决方案可能有效:

  • 使用类似的SBT任务将所有本地路径上的库打成资源jar包,并将该jar包添加到类路径中。
  • 创建一个Scala方法,接受一个函数和一个库名列表作为参数,创建一个临时目录,在该jar包的资源中读取这些库并将它们写入临时目录中的文件,将临时目录添加到java.library.path中,调用传入的函数,最后将java.library.path恢复到之前的状态。
  • 第一次调用本地库时(通常会静态初始化并进行System.loadLibrary调用),在你的方法中使用要加载的库列表包装该特定调用。这样,当它调用System.loadLibrary时,所有的库都在java.library.path上,可以成功加载。
  • 显然,这很麻烦,因为你必须手动初始化第三方库才能使用它,但与让所有工具正确设置java.library.path相比,包装所有初始化点(主要函数和测试初始化)似乎更可行。如果你已经有自己的抽象层位于第三方库之上,则更容易,所以只需要包装一个初始化点。

    如果这看起来是一个现实的解决方案,我可以提供关于SBT任务或Scala包装方法的更多细节,以便您更好地理解。


请问您能否详细描述一下“修改java.library.path并使用System.loadLibrary(我觉得大多数人都这样做)”?我想在sbt中使用Java本地库,并最终制作一个可用的jar文件,那么我应该怎么做才能包含java.library.path呢?我需要更改build.sbt吗?还是需要按照某种特定的顺序执行操作?谢谢。 - linpingta

0

在Osx下,如果您在/lib/*.jnilib中加载本地库时遇到问题,请尝试以下方法来解决sbt test的问题。

[error] java.lang.UnsatisfiedLinkError: Fatal execution error, caused by no jniortools in java.library.path

您可以使用以下代码代替System.loadLibrary("jniortools")

new File("lib").listFiles().map(_.getAbsolutePath).filter(_.endsWith("jniortools.jnilib")).foreach(System.load)

0

有一个简单的方法。

假设本地库存储在 lib_extra 目录中

  1. 将 jna 添加到 libraryDependencies 中:

    libraryDependencies ++= Seq("net.java.dev.jna" % "jna-platform" % "4.1.0")

  2. 将以下代码添加到 build.sbt 中:

    unmanagedResourceDirectories in Compile += baseDirectory.value / "lib_extra"

    includeFilter in (Compile, unmanagedResourceDirectories) := ".dll,.so"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接