将本机系统库与 SBT 集成 [英] Integrating native system libraries with SBT
问题描述
将各种 SBT 任务与本机库(例如,来自 JOGL、LWGL 或 JCuda? 具体来说,
What is a good way to integrate various SBT tasks with native libraries (for example, those from JOGL, LWGL, or JCuda? Specifically,
是否有推荐的方法在
run
任务中包含本机库?SBT 邮件列表上的讨论表明了这些可能性:
Is there a recommended way to include a native library in the
run
task? A discussion on the SBT mailing list suggests these possibilities:
- 修改
JavaOptions
包含-Djava.library.path=<本地库的路径>
,然后修改run
任务来fork JVM.(参见这个插件一个例子.) - 使用 SBT
initialize
设置 运行调用System.setProperty(...)
的代码来配置java.library.path
.同样,run
必须 fork. - 将本机库放在类路径上之前启动 SBT.
- Modify
JavaOptions
to include-Djava.library.path=<path to native libraries>
, and then modify therun
task to fork the JVM. (See this plugin for an example.) - Use the SBT
initialize
setting to run code that callsSystem.setProperty(...)
to configurejava.library.path
. Again,run
must fork. - Put the native libraries on the classpath before launching SBT.
最后一个优点是run
不需要fork,缺点是配置必须在SBT之外.
The last one has the advantage that run
need not fork, but the disadvantage that the configuration must be done outside SBT.
我能否在由 sbteclipse 插件生成的 Eclipse 项目中自动包含本机库?可以重写.project
文件在后处理步骤中. 有示例代码吗?有没有更好的方法?
Can I automatically include native libraries in the Eclipse project generated by the sbteclipse plugin? It's possible to rewrite the .project
file in a post-processing step. Is there example code? Is there a better way?
本地库是否可以包含在由插件生成的可运行 Jar 中,例如 sbt-程序集、sbt-onejar 或sbt-proguard?
Can native libraries be included in the runnable Jar that is generated by a plugin such as sbt-assembly, sbt-onejar or sbt-proguard?
我假设本机库没有直接的 SBT 设置.如果存在这样的事情,上述任务是否可以透明地处理本机库?
I assume there is no direct SBT setting for native libraries. If something like that existed, could the above tasks handle native libraries transparently?
推荐答案
从我过去的研究来看,只有两种方法可以加载原生库:修改 java.library.path
code> 并使用 System.loadLibrary
(我觉得大多数人都这样做),或者使用带有绝对路径的 System.load
.
From the research I've done in the past, there are only two ways to get native libraries loaded: modifying java.library.path
and using System.loadLibrary
(I feel like most people do this), or using System.load
with an absolute path.
正如您所提到的,在配置 SBT 和 Eclipse 方面,使用 java.library.path
可能会很烦人,而且我认为不可能为可执行 jar 自动执行此操作.
As you've alluded to, messing with java.library.path
can be annoying in terms of configuring SBT and Eclipse, and I do not think it's possible to do automatically for an executable jar.
这样就剩下System.load
.在编写自己的原生库方面,您可以做的是:
So that leaves System.load
. In terms of writing your own native libraries, what you can do is:
- 创建一个 SBT 任务来编译您的本机源代码(使用
javah
和gcc
),获取生成的 .so 文件和它依赖的任何 .so 文件,将它们放入在目标目录的 jar(作为资源)中,并将 jar 的路径添加到unmanagedJars in Compile
. - 创建一个 Scala 方法来加载库.它不会调用
System.loadLibrary
,而是使用Class.getResourceAsStream
读取库,File.createTempFile
将其写入文件系统的某个位置,和System.load
将其加载到 JVM 中. - 现在不再像以前那样调用
System.loadLibrary
,而是调用MyClasspathJniLoader.loadLibrary
.
- Create an SBT task that compiles your native sources (with
javah
andgcc
), takes the resulting .so files and any .so files it depends on, puts them in a jar (as resources) in the target directory, and adds the path to the jar tounmanagedJars in Compile
. - Create a Scala method to load a library. Instead of calling
System.loadLibrary
, it will useClass.getResourceAsStream
to read the library,File.createTempFile
to write it somewhere on the filesystem, andSystem.load
to load it into the JVM. - Now instead of calling
System.loadLibrary
as you would before, callMyClasspathJniLoader.loadLibrary
.
这将适用于 SBT 运行、Eclipse 和可执行 jar,无需任何额外配置(虽然我不知道 proguard 如何知道要包含哪些资源).
That will work with SBT run, Eclipse, and executable jars without any extra configuration (although I don't know how proguard knows which resources to include).
现在对于已经编写好的第三方本机库,其中一些像 jblas 已经使用了这种fat jar"方法.如果他们希望您设置 java.library.path
,然后在他们愿意时调用 System.loadLibrary
,那么您需要做一些魔术来做到这一点工作.
Now as to third party native libraries that have already been written, some of them like jblas already use this "fat jar" approach. If they expect you to set up java.library.path
and then they call System.loadLibrary
when they feel like it, you'll need to do some magic to make that work.
我没有试过这个,但这个解决方案可能有效:
I haven't tried this, but this solution might work:
- 使用类似的 SBT 任务将其本机路径上的所有库作为资源放入 jar 中,并将该 jar 放入 clsaspath.
- 创建一个 Scala 方法,它接受一个函数和一个库名列表,创建一个临时目录,将这些库从 jar 的资源中读取到临时目录中的文件中,将临时目录添加到
java.library.path
,调用传入的函数,最后将java.library.path
恢复到原来的样子. - 第一次调用本机库时(当它可能会静态初始化并进行
System.loadLibrary
调用时),将该特定调用包装在您的方法中,并包含它将加载的库列表.这样,当它调用System.loadLibrary
时,它的所有库都将在java.library.path
上并成功加载.
- Use a similar SBT task to put all of the libraries on their native path into a jar as resources, and put that jar on the clsaspath.
- Create a Scala method that takes a function and a list of library names, creates a temp directory, reads those libraries from the jar's resources into files in the temp directory, adds the temp directory to
java.library.path
, calls the passed in function, and finally reverts thejava.library.path
back to what it was before. - The first time you call into the native library (when it presumably will statically initialize and make the
System.loadLibrary
call), wrap that particular call in your method with the list of libraries it will load. That way, when it callsSystem.loadLibrary
, all of its libraries will be onjava.library.path
and will be loaded successfully.
显然这很烦人,因为您必须在使用之前手动初始化第三方库,但包装所有初始化点(您的主要功能和测试初始化)似乎比获取所有工具更可行正确设置 java.library.path
.如果您已经在第三方库之上拥有自己的抽象层,那么这可能会更容易,因此您实际上只需要包装一个初始化点.
Obviously this is annoying since you have to manually initialize the third party library before using it, but it seems more feasible to wrap all of the initialization points (your main functions and test initializations) than it does to get all of your tools to set java.library.path
correctly. And it may be even easier than that if you already have your own layer of abstraction above the third party library, so there's really only one initialization point you have to wrap.
如果这看起来是一个现实的解决方案,如果您感到困惑,我可以添加有关 SBT 任务或 Scala 包装方法的更多详细信息.
If that seems like a realistic solution, I can add more details about the SBT task or Scala wrapper methods if you're confused.
这篇关于将本机系统库与 SBT 集成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!