0%

编译各平台的 JNI 链接库

DSRC 是一款 C++ 编写的 DNA 序列压缩工具。

准备

JNI(Java Native Interface)编译过程:

  1. 编写带有 natvie 方法的 java 类,如 DSRCImpl.java
  2. javac 编译 class 文件,DSRCImpl.class
  3. 使用 javah 生成头文件, package_DSRCImpl.h
  4. 编写 c/c++ 代码实现DSRCImpl.h 中的方法。
  5. 编译各个平台的动态链接库。
  6. 在 Java 项目中加载动态链接库。

其中最关键的是第4、5步,实现头文件方法,及编译各平台动态链接库。

通过 native 方法生成 .h 头文件

DSRCImpl.java 代码如下:

1
2
3
4
5
6
7
8
package com.genedock.sdk.internal.compress;

public class DSRCImpl {

public native int decompress(String inputFile, String outputFile, int t);

public native int compress(String inputFile, String outputFile, int t, int m);
}

编译出头文件:

生成的 com_genedock_sdk_internal_compress_DSRCImpl.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 */
#include <jni.h>
/* Header for class com_genedock_sdk_internal_compress_DSRCImpl */

#ifndef _Included_com_genedock_sdk_internal_compress_DSRCImpl
#define _Included_com_genedock_sdk_internal_compress_DSRCImpl
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_genedock_sdk_internal_compress_DSRCImpl
* Method: decompress
* Signature: (Ljava/lang/String;Ljava/lang/String;I)I
*/
JNIEXPORT jint JNICALL Java_com_genedock_sdk_internal_compress_DSRCImpl_decompress
(JNIEnv *, jobject, jstring, jstring, jint);

/*
* Class: com_genedock_sdk_internal_compress_DSRCImpl
* Method: compress
* Signature: (Ljava/lang/String;Ljava/lang/String;II)I
*/
JNIEXPORT jint JNICALL Java_com_genedock_sdk_internal_compress_DSRCImpl_compress
(JNIEnv *, jobject, jstring, jstring, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

实现头文件中的方法

我们需要调用 DSRC 代码来压缩和解压文件。先获取 DSRC 项目。

1
git clone https://github.com/refresh-bio/DSRC.git

将上面生成的 .h 文件拷贝到项目中。我将它放在 /example/cpplib/ 目录下。新建 dsrcimpl.cpp 实现头文件中的方法。如下(jstring 类型需要做转换):

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include "com_genedock_sdk_internal_compress_DSRCImpl.h"
#include <Dsrc.h>

JNIEXPORT jint JNICALL Java_com_genedock_sdk_internal_compress_DSRCImpl_decompress
(JNIEnv *env, jobject obj, jstring inputFile, jstring outputFile, jint t) {
using namespace dsrc::lib;

const char *inFilename_ = env->GetStringUTFChars(inputFile, 0);
const char *outFilename_ = env->GetStringUTFChars(outputFile, 0);

try {
DsrcModule dsrc;
dsrc.Decompress(inFilename_, outFilename_);
}
catch (const DsrcException &e) {
return -1;
}
env->ReleaseStringUTFChars(inputFile, 0);
env->ReleaseStringUTFChars(outputFile, 0);
return 0;
}

JNIEXPORT jint JNICALL Java_com_genedock_sdk_internal_compress_DSRCImpl_compress
(JNIEnv *env, jobject obj, jstring inputFile, jstring outputFile, jint t, jint m) {
using namespace dsrc::lib;


const char *inFilename_ = env->GetStringUTFChars(inputFile, 0);
const char *outFilename_ = env->GetStringUTFChars(outputFile, 0);

// Configure DSRC compressor
try {
DsrcModule dsrc;
dsrc.SetThreadsNumber(t);

switch (m) {
//case 3:
// pars.tagPreserveFlags = BIT(1) | BIT(2);
case 2:
dsrc.SetDnaCompressionLevel(3);
dsrc.SetQualityCompressionLevel(2);
dsrc.SetFastqBufferSizeMB(256);
break;
case 1:
dsrc.SetDnaCompressionLevel(2);
dsrc.SetQualityCompressionLevel(2);
dsrc.SetFastqBufferSizeMB(64);
break;
case 0:
dsrc.SetDnaCompressionLevel(0);
dsrc.SetQualityCompressionLevel(0);
dsrc.SetFastqBufferSizeMB(8);
break;
}

dsrc.Compress(inFilename_, outFilename_);
}
catch (const DsrcException &e) {
return -1;
}

env->ReleaseStringUTFChars(inputFile, 0);
env->ReleaseStringUTFChars(outputFile, 0);
return 0;
}

编写好cpp文件,修改example/cpplib/目录下的Makefile文件,如下:

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
all: example1 example2 dsrcimpl

INC_PATH = ../../include/dsrc
LIB_PATH = ../../lib/
LIB_JNI_PATH = /System/Library/Frameworks/JavaVM.framework/Headers
DSRC_LIB = -ldsrc

.cpp.o:
$(CXX) $(CXXFLAGS) -c $< -o $@ -I$(INC_PATH) -I$(LIB_JNI_PATH)

example1: example1.o
$(CXX) $(CXXFLAGS) -o $@ $? -L$(LIB_PATH) $(DSRC_LIB) $(DEP_LIBS)
strip $@

example2: example2.o
$(CXX) $(CXXFLAGS) -o $@ $? -L$(LIB_PATH) $(DSRC_LIB) $(DEP_LIBS)
strip $@

dsrcimpl: dsrcimpl.o
c++ -dynamiclib -o libdsrcjava.jnilib dsrcimpl.o -L../../lib/ -ldsrc

clean:
-rm *.o
-rm example1
-rm example2
-rm libdsrcjava.jnilib

编译各平台动态链接库

MacOS

在 DSRC 根目录执行如下命令:

1
make -f Makefile.osx lib

结束后在 lib/ 目录下的到 libdsrc.a 文件。进入 example/cpplib/ 目录使用 make 命令编译指定目标:

1
make dsrcimpl

如果顺利,此时在当前目录下已经生成了 libdsrcjava.jnilib 文件。

Ubuntu

修改 src/Makefile 文件 34 行,如下:

1
$(CXX) $(CXXFLAGS) -c $< -o $@ -fPIC

在 DSRC 根目录执行编译命令

1
make -f Makefile.c++11 lib

再进入 examples/cpplib/ 目录执行如下命令,即可生成 dsrcjava.so 动态链接库文件。

1
2
g++  -c dsrcimpl.cpp -o dsrcjava.o -I../../include/dsrc -I/usr/lib/jvm/java-8-oracle/include -I/usr/lib/jvm/java-8-oracle/include/linux
g++ -O2 -m64 -DNDEBUG -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -Wall -std=c++11 -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -I../../include/dsrc -shared -o dsrcjava.so dsrcimpl.cpp -L../../lib/ -ldsrc -lpthread -fPIC

Windows

windows 需要编译 x86 和 x64 两个版本链接库。DSRC 默认不支持编译 32 位的类库。

DSRC 项目使用 Visual Studio 编译,所以在此我们也使用 Visual Studio 编译。安装好 Visual Studio 后双击打开 DSRC 根目录下 dsrc20-vs2k12.sln 文件即可导入项目。

在解决方案上右键,点击生成即可编译。在 1 处选择 Release Lib 即可编译生成 .lib 静态类库。

X64 编译

在解决方案上右键 -> 添加 -> 新建项目。选择 windows 桌面 -> windows 桌面向导,输入名称后确定。应用类型选择 动态链接库,取消预编译头。删除生成的头文件和源文件。将我们的头文件和 cpp 文件添加进来,如下:

添加引用,选择 dsrc20-vs2k12,设置 dsrcjava 属性。修改 VC++ 目录 下 包含目录引用目录,分别添加 jdk 安装目录下的 include/、include/win32/ 和 lib/ 目录,如下:

C/C++ 中添加 附加包含目录,指定 DSRC 中 include/dsrc 目录。在代码生成中,修改运行库多线程(/MT)

此时再去编译即可。

X86 编译

首先需要为 DSRC 添加 win32 解决方案。进入配置管理器,在 dsrc20-vs2k12 平台中新建如下:

打开 src/QualityModelerProxy.h 文件,将 168-176 行修改如下:

1
2
3
4
5
6
7
8
9
case 1:	return new TQualityLossyOrderPositionalModeler<7, 1>();
case 2: return new TQualityLossyOrderPositionalModeler<7, 2>();
case 3: return new TQualityLossyOrderPositionalModeler<7, 3>();
case 4: return new TQualityLossyOrderPositionalModeler<7, 4>();
case 5: return new TQualityLossyOrderPositionalModeler<7, 5>();
case 6: return new TQualityLossyOrderPositionalModeler<7, 6>();
case 7: return new TQualityLossyOrderPositionalModeler<7, 7>();
case 8: return new TQualityLossyOrderPositionalModeler<7, 8>();
case 9: return new TQualityLossyOrderPositionalModeler<7, 9>();

如 64 位同样,添加 JDK 的 include 和 include/win32 及 DSRC/include/dsrc 包含目录。将 C/C++ -> 代码生成中,修改运行库多线程调试(/MTd)
此时编译即可生成 32 位平台链接库。

参考

  1. 极客学院 wiki
  2. Visual Studio下包含多项目的解决方案及项目间引用
  3. Visual Studio中开发Jni dll库
  4. 使用JNI进行Java与C/C++语言混合编程(1)–在Java中调用C/C++本地库
  5. Ubuntu 使用Jni开发实例详解
  6. Windows、Linux、Mac OSX编译jni动态库
  7. Mac OS上编译JNI的动态库