JUnit测试的本机代码未使用`nar-maven-plugin`编译

时间:2017-06-30 16:09:45

标签: java c++ maven java-native-interface maven-nar-plugin

我使用nar-maven-plugin配置了Maven的JNI项目。 Java和C ++代码都驻留在项目中。主代码显然可以正确编译(包括C ++和Java)。问题出在测试代码(JUnit)上。

测试代码定义了一个本身具有本机方法的Java类。相应的本机代码驻留在目录

<project root>
+- src
   +- test
      +- c++

构建消息中没有证据表明,当我从构建过程创建的DLL上的命令行运行nm时,编译此本机测试代码并且根本不显示相应的本机方法。另外,我故意在测试代码中加入语法错误并重新编译,看看是否会出现编译时错误。没有错误,符合我的信念,即代码永远不会被编译。

相应地,我在UnsatisfiedLinkError期间运行测试时得到mvn install。请注意,我可以从测试失败的角度判断主要(非测试)代码的本机方法是否已正确加载和链接。因此,我得出结论,存在一些与本机测试代码的构建和链接有关的问题。

我目前在Windows 10上使用Eclipse IDE和MinGW编译器获取本机代码。

我的POM的相关部分如下(根据Avoiding machine-dependent POM with MinGW compiler and nar-maven-plugin与早期配置问题相关的回答略有更新):

<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>

有没有一种已知的方法来处理这个问题? (也许是其他配置标签?)

1 个答案:

答案 0 :(得分:1)

我让这个工作,但它不漂亮。我将在最后给出POM部分,但概括地说,这些是步骤:

  • 按照编译主代码的问题设置项目
  • 使用<tests>上的nar-maven-plugin部分编译本机测试代码,注意这实际上已设置为本机测试创建可执行文件而不创建DLL / SO支持Java测试
  • 为链接器指定<testOptions>以将其破解为制作DLL / SO而不是可执行文件
  • 进一步指定<testOptions>以使测试代码与主项目代码相关联,因为当您选择jni(而不是sharedstatic时,系统不会自动支持此代码)库类型
  • 将测试库移动到测试前的路径中,并在
  • 之后删除它

更详细地扩展最后一个项目:即使你得到了要编译的测试代码,你也无法从Java加载它,因为当测试时测试目录不在java.library.path上跑!我能找到的最佳解决方案是将测试库临时移动到路径上的目录中。由于可以轻松获得配置选项,这似乎比更改路径更容易。如果您在路径上获得了库,那么您可以像往常一样在JUnit测试执行期间使用System.loadLibrary

现在,这里是扩展的POM细分,可以实现上述目标。这是基于我在问题中的内容,但是新的部分需要在答案开始时完成子弹。请注意,支持测试的JNI代码位于TestWrapper.cpp中的文件/src/test/c++中。 (我知道这不是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>