我有一个使用 nar-maven-plugin
配置了 Maven 的 JNI 项目。Java 和C++代码都驻留在项目中。 主代码显然可以正确编译(C++和Java(。 问题出在测试代码 (JUnit( 上。
测试代码定义了一个本身具有本机方法的 Java 类。相应的本机代码驻留在目录中
<project root>
+- src
+- test
+- c++
生成消息中没有证据表明此本机测试代码曾经编译过,并且当我在构建过程创建的 DLL 上从命令行运行nm
时,根本不显示相应的本机方法。 此外,我故意在测试代码中放入语法错误并重新编译,以查看是否会收到编译时错误。 没有错误,与我的观点一致,即代码永远不会被编译。
相应地,当测试在mvn install
期间运行时,我会得到一个UnsatisfiedLinkError
。 请注意,我可以从测试失败的点看出主(非测试(代码的本机方法已正确加载和链接。 因此,我得出的结论是,存在一些与本机测试代码的构建和链接相关的问题。
我目前在Windows 10上使用Eclipse IDE和MinGW编译器进行本机代码。
我的 POM 的相关部分如下(根据我关于使用 MinGW 编译器和 nar-maven-plugin 避免与早期配置问题相关的机器依赖 POM 的回答略有更新(:
<profiles>
<profile>
<id>Windows-MinGW</id>
<activation>
<os>
<family>Windows</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.github.maven-nar</groupId>
<artifactId>nar-maven-plugin</artifactId>
<version>3.5.1</version>
<extensions>true</extensions>
<configuration>
<cpp>
<options>
<option>-std=c++1y</option>
</options>
</cpp>
<linker>
<name>g++</name>
<options>
<option>-Wl,--kill-at</option>
</options>
</linker>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<defaultGoal>integration-test</defaultGoal>
<plugins>
<plugin>
<groupId>com.github.maven-nar</groupId>
<artifactId>nar-maven-plugin</artifactId>
<version>3.5.1</version>
<extensions>true</extensions>
<configuration>
<cpp>
<defines>
<define>EXPORT_DLL</define>
</defines>
</cpp>
<libraries>
<library>
<type>jni</type>
<narSystemPackage>com.mycompany.sandbox</narSystemPackage>
</library>
</libraries>
</configuration>
</plugin>
</plugins>
</build>
有没有已知的方法来处理这个问题?(也许是其他配置标记?
我让它工作,但它并不漂亮。我将在最后给出POM部分,但是,在大纲中,这些是步骤:
- 按照问题中设置项目以编译主代码
- 使用
nar-maven-plugin
上的<tests>
部分来编译本机测试代码,请注意,这实际上是为本机测试创建可执行文件,而不是创建支持 Java 测试的 DLL/SO - 指定链接器
<testOptions>
以将其破解为DLL/SO而不是可执行文件 - 指定进一步的
<testOptions>
以使测试代码链接到主项目代码,因为当您选择jni
(而不是shared
或static
(库类型时,不会自动支持此功能 - 将测试库移动到测试前的路径中,并在测试之后将其删除
更详细地扩展最后一个项目符号:即使你得到了要编译的测试代码,你也无法从 Java 加载它,因为在运行测试时测试目录不在java.library.path
上!我能找到的最佳解决方案是将测试库临时移动到路径上的目录中。由于配置选项很容易获得,这似乎比更改路径更容易。假设您在路径上获取库,则可以在 JUnit 测试执行期间照常使用System.loadLibrary
。
现在,这里是完成上述任务的扩展POM细分市场。这是基于我在问题中的内容,但对于新部分,需要在答案开始时完成项目符号。 请注意,支持测试的 JNI 代码位于 /src/test/c++
中TestWrapper.cpp
的文件。(我知道这不是 JNI 源文件的标准命名约定。
注意:在这一点上,我只制定了用于在我的 Windows 10 计算机上进行测试的链接器标志,这在profiles
部分中表示。同样,复制/删除明确具有.dll
扩展名,需要进行调整。(但是,进一步注意,即使您获得一个.dll
文件,当插件制作它时,它也会有一个.exe
扩展名! 在这一点上,我的POM对于其他机器/架构来说无疑是坏的,但是从这里开始有一条明确的前进道路,所以似乎值得按原样发布答案。
<profiles>
<profile>
<id>Windows-MinGW</id>
<activation>
<os>
<family>Windows</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.github.maven-nar</groupId>
<artifactId>nar-maven-plugin</artifactId>
<version>3.5.1</version>
<extensions>true</extensions>
<configuration>
<cpp>
<options>
<option>-std=c++1y</option>
</options>
</cpp>
<linker>
<name>g++</name>
<options>
<option>-Wl,--kill-at</option>
</options>
<testOptions>
<!-- Put the -shared flag onto the linker - That will force a DLL instead of an EXE -->
<testOption>-shared</testOption>
<!-- We cannot easily link to the *library* that was created for the main project but we can get the compiled object files with the following option -->
<testOption>${project.build.directory}/nar/obj/${nar.aol}/*.o</testOption>
</testOptions>
</linker>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<defaultGoal>integration-test</defaultGoal>
<plugins>
<plugin>
<groupId>com.github.maven-nar</groupId>
<artifactId>nar-maven-plugin</artifactId>
<version>3.5.1</version>
<extensions>true</extensions>
<configuration>
<cpp>
<defines>
<define>EXPORT_DLL</define>
</defines>
</cpp>
<libraries>
<library>
<type>jni</type>
<narSystemPackage>com.mycompany.mypackage</narSystemPackage>
</library>
</libraries>
<tests>
<test>
<name>TestWrapper</name>
<run>false</run>
</test>
</tests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>copy-test-lib-to-path</id>
<phase>pre-integration-test</phase>
<configuration>
<target>
<copy file="${project.build.directory}/test-nar/bin/${nar.aol}/TestWrapper.exe" tofile="${project.build.directory}/nar/${project.artifactId}-${project.version}-${nar.aol}-jni/lib/${nar.aol}/jni/TestWrapper.dll"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>delete-test-lib-from-deployment</id>
<phase>post-integration-test</phase>
<configuration>
<target>
<delete file="${project.build.directory}/nar/${project.artifactId}-${project.version}-${nar.aol}-jni/lib/${nar.aol}/jni/TestWrapper.dll"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>