前些天有个项目有scala-c++交叉编译的需求,于是简单做了个demo把构建流程打通,并且尽量自动化。
现在项目好像是黄了,还是整理一下发出来吧。
项目地址: https://github.com/pzque/scala_native_demo
Dependencies
- jdk
- sbt / idea内置sbt
- scala / idea内置scala
- cmake / clion内置cmake
Supported Platforms
MacOS, Linux(Ubuntu16.04), Windows10测试通过。
Structure
目录 | 详情 |
---|---|
demo_scala | 使用sbt构建的scala项目,可以直接使用idea打开并开发 |
demo_cpp | 使用cmake构建的c++项目,可以直接使用clion打开并开发 |
lib | demo_cpp生成的动态链接库 |
Workflow
1. 前置条件
idea+clion
idea需要装Scala语言插件,clion不需要额外配置。
本向导默认用户能够熟练使用idea开发sbt项目。
命令行用户
有sbt和cmake即可。
2. 创建含有native方法的scala类
在/src/main/scala/文件夹下创建一个含有native方法的scala类:
例如本项目给出的示例:
1 | class NativeDemo { |
这时候已经可以在scala内使用这个类并且可以通过编译,因为编译器只关心方法的类型签名。例如本项目的main方法:
1 | object Main { |
使用了System.loadLibrary("NativeDemo")
来加载动态库。
但是这时候的动态库还没有实现,jvm并不能找到这个库,运行到这一行代码时会报错。
所以我们接下来使用c++来实现我们的native方法,并编译成动态库。
3. 编译demo_scala项目
在build.sbt内添加native类的名字,例如本项目给出的示例:1
2
3lazy val nativeClassNames = List(
"NativeDemo"
)
然后构建项目。
对于idea用户
依次点击:
菜单栏->Build->Build Project
对于命令行用户
直接:
1 | sbt compile` |
说明
build.sbt在编译时会做三件事:
1.调用javah
命令native类相应应的.h文件到“demo_cpp”目录内,比如这里的”demo_cpp/NativeDemo.h”1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/* DO NOT EDIT THIS FILE - it is machine generated */
/* Header for class NativeDemo */
extern "C" {
/*
* Class: NativeDemo
* Method: add
* Signature: (DD)D
*/
JNIEXPORT jdouble JNICALL Java_NativeDemo_add
(JNIEnv *, jobject, jdouble, jdouble);
/*
* Class: NativeDemo
* Method: distance
* Signature: ([D[D)D
*/
JNIEXPORT jdouble JNICALL Java_NativeDemo_distance
(JNIEnv *, jobject, jdoubleArray, jdoubleArray);
}
2.拷贝”demo_cpp/NativeDemo.h”内的函数声明到”demo_cpp/NativeDemo.cpp”内(如果cpp内已经有的话则跳过),刚生成的cpp文件是这样的:
1 |
|
你需要把它补充完整,提供具体的实现
3.在”demo_cpp/CMakeList.txt”内添加一个动态库的target(如果已经有的话则跳过),这里就是添加这两行:
1 | set(NativeDemo_SOURCE_FILES NativeDemo.h NativeDemo.cpp) |
4. 实现native方法并编译c++动态库
补充完整native方法对应的c++方法,例如”demo_cpp/NativeDemo.cpp”补充完整后的内容:
1 |
|
这里是使用JDK提供的JNI接口进行编程,具体的JNI编程规范可以参考JNI官网或者其中文翻译。
写完之后编译即可:
clion用户
依次点击:菜单栏的Run->Build选项进行cpp工程的编译。
编译完成后,动态库会生成到lib
目录内。
命令行用户
依次执行:1
2cmake .
make
编译完成后,动态库会生成到”lib”目录内。
5.运行
动态库编译完成后,查看”lib”目录,里面将包含动态库文件:1
libNativeDemo.dylib(MacOS)/libNativeDemo.so(Linux)/libNativeDemo.dll(Windows)
我们只需要让jvm找到这个文件即可,方法是给jvm加启动参数-Djava.library.path=../lib
(如果这里的”..lib”不管用的话那么改成绝对路径)。
怎么加呢?
idea用户
别忘了idea打开的是”demo_scala”目录。
1.点击运行按钮左边的下拉条,选择”Edit Configurations”
2.然后在VM options一栏里加入-Djava.library.path=../lib
即可
命令行用户
给jvm启动参数直接加-Djava.library.path=../lib
即可。
例如在”demo_scala”下使用sbt run:1
sbt -Djava.library.path=../lib run
将运行程序的main函数。