Maven Native Chronicles, Part 1: Figuring Out nar-maven-plugin
Fri Jul 24 15:48:59 EDT 2015
- Maven Native Chronicles, Part 1: Figuring Out nar-maven-plugin
- Maven Native Chronicles, Part 2: Setting Up a Windows Jenkins Node
- Maven Native Chronicles, Part 3: Improving Native Artifact Handling
- Maven Native Chronicles: Running Automated Notes-based Tests
As I mentioned the other day, my work lately involves a native shared library that is then included in an OSGi plugin. To get it working during a Maven compile, I just farmed out the actual build process to Visual Studio's command-line project builder. That works as far as it goes, but it's not particularly Maven-y and, more importantly, it's Windows-only.
In looking around, it seems like the most popular method of doing native compilation in Maven, especially with JNI components, is maven-nar-plugin - nar
means "Native ARchive", and it's meant to be a consistent way to package native artifacts (executables and libraries) across platforms. It does an admirable job wrangling the normally-loose nature of a C/C++ program to work with Maven-ish standards and attempts to paper over the differences between platforms and toolchains. I'm not entirely convinced that this will be the way I go long-term (in particular, its attitude towards multi-platform/arch builds seems to be "eh, sort of?"), but it's a good place to get started with non-Windows compilation.
The first step was to move the files around to mostly match a Maven-style layout. Starting out, the .cpp and .h files were in the src
folder directly, while dependency headers were in a dependencies
folder next to it. I left the Notes includes in there for now, but it seems that nar-maven-plugin
will cover the JNI stuff for me, so I could simplify that somewhat. The new project structure looks like:
- (project root)
- src
- main
- c++
- include
- main
- dependencies
- inc
- notes
- inc
- src
Next was to set up the project configuration. For now, I want to still use Visual Studio's CLI app to build the Windows version, and I'm going to have to specifically define supported platforms, so I define the project as a nar
, but then disable actual execution of the plugin by default:
<project> ... <packaging>nar</packaging> <build> <plugins> <plugin> <groupId>com.github.maven-nar</groupId> <artifactId>nar-maven-plugin</artifactId> <version>3.2.3</version> <extensions>true</extensions> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build> </project>
Then, much as I did for the Windows-specific builds, I added a profile to try to build on my Mac. Note that these build settings produce a library that fails all unit tests, so they're surely not correct, but hey, it compiles and links, so that's a start. To ensure that it only builds when it has an appropriate context, it is triggered by a combination of OS family and the presence of the notes-program
Maven property, which should point to the Notes executable directory.
<project> ... <profiles> ... <profile> <id>mac</id> <activation> <os> <family>mac</family> </os> <property> <name>notes-program</name> </property> </activation> <build> <plugins> <plugin> <groupId>com.github.maven-nar</groupId> <artifactId>nar-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <skip>false</skip> <cpp> <debug>true</debug> <includePaths> <includePath>${project.basedir}/src/main/include</includePath> <includePath>${project.basedir}/dependencies/inc/notes</includePath> </includePaths> <options> <option>-DMAC -DMAC_OSX -DMAC_CARBON -D__CF_USE_FRAMEWORK_INCLUDES__ -DLARGE64_FILES -DHANDLE_IS_32BITS -DTARGET_API_MAC_CARBON -DTARGET_API_MAC_OS8=0 -DPRODUCTION_VERSION -DOVERRIDEDEBUG</option> </options> </cpp> <linker> <options> <option>-L${notes-program}</option> </options> <libSet>notes</libSet> </linker> <libraries> <library> <type>shared</type> </library> </libraries> </configuration> </plugin> </plugins> </build> </profile> </profiles> </project>
Unstable though the result may be, the nar plugin does its job: it produces an archive containing the dylib
, suitable for distribution as a Maven artifact and extraction into the downstream project, which I'll go into later.
So this is a good step towards my final goal. As I mentioned, I may end up getting rid of nar-maven-plugin
specifically, but this is a good way to shape the code into something more portable (I also got rid of a few Windows-isms in the C++ while I was at it). My ultimate goal is to get a single build run that produces artifacts for all of the important platforms (Windows 32/64 and Linux 32/64 for production, Mac 32/64(?) for JUnit tests during development). I may be able to accomplish that using the nar plugin with a distributed Jenkins build, or I may be able to do it with Makefiles with GCC cross-compilers on OS X build host. If that works, it's the sort of thing that makes all this Maven stuff worthwhile.