Fork me on GitHub

NAR Philosophy

In the NAR plugin we follow Maven's main principles:

below we explain how:

Convention over configuration

The NAR Plugin enables a cross-platform build technology for native artifacts using Maven. Though the user has full flexibility in the choice of compiler/linker and their options, sensible defaults are provided by the NAR Plugin. The three primary conventions for the NAR Plugin are:

and are detailed below:

Standard directory layout for Native projects

The NAR Plugin assumes to find its native sources and resources in a directory structure parallel to the java sources. Test sources and test resources are organized the same way.

/yourproject
            /src
                /main
                     /java
                     /resources
                     /include
                     /c++
                     /c
                     /fortran
               /test
                    /java
                    /resources
                    /include
                    /c++
                    /c
                    /fortran                        

Organizing the information this way means that it is easy to find, for both people and the plugins that need to work with them.

A single Native project produces a single output

Maven produces one primary output (jar, war, ...) per project. For the NAR Plugin the produced nar files are secondary outputs or artifacts attached to the primary output. There are several reasons why the native artifacts are not primary outputs:

  • When building a Native library, the object files and the include headers make a complete set. There is not much use of one without the other. Object files for one architecture combined in a nar file are no different than object files for another achitecture, their functionality is the same, its just for different machines. Neither the nar file with the include headers nor the nar files with the machine specific object files is a good primary output, so both attach to a jar file.
  • When one builds a JNI library one needs both the java code in a jar file and native code in a shared library. The expected dependency is that the jar file needs the JNI library. However, javah generates the necessary header file from java, which would make the JNI library dependent on the jar file. Since this is impossible it would be better to attach the JNI library (in its nar file) as a secondary artifact to the primary nar file.
  • When depending on a native library the user should just specify the artifactId, groupId, version and type, but not the architecture, os and linker it was build with. The latter is a derived value of the executable or library one is building at this time. Not specifying architecture, os and linker also means that the POM is generic. Since nar files are by default (except for the -noarch nar) architecture-os-linker specific they need to be secondary artifacts.

Standard naming conventions

When maven produces a jar file it's normally named artifactId-version.jar. The NAR Plugin follows this convention for its attached artifacts:

artifactId-version-noarch.nar
contains the non architecture specific parts of the native library, such as include headers.
artifactId-version-aol-type.nar
contains the architecture specific parts of the native library, such as the library and object files. The aol is an Architecture-OS-Linker qualifier, for example: ppc-MacOSX-g++. The type specifies what binding the library is, for example: shared, static or jni.

Since the java compiler has a unified interface there is no need for any naming conventions here. Native compilers and linkers on different platforms tend to have radically different interfaces (command line options) so these were unified, allowing the user to switch on exception handling and debugging with the same tag in the configuration no matter which platform the NAR Plugin is running on. An example is below:

...
<plugin>
  <groupId>com.github.maven-nar</groupId>
  <artifactId>nar-maven-plugin</artifactId>
  <configuration>
    <cpp>
      <exceptions>false</exceptions>
      <debug>true</debug>
    </cpp>
  </configuration>
</plugin>
...

Reuse of build logic

The NAR Plugin provides multiple goals organized in the "nar" lifecycle. The "nar" lifecycle integrates nicely with the default "jar" lifecycle to build java and native files into nar artifacts in parallel. Each of the NAR Plugin goals can of course also be configured separately if needed.

Most NAR goals are executed using the NarManager, which provides an API that can also be used by other plugins when they need to deal with NAR artifacts.

Declarative execution

All logic in Maven is setup in a declarative fashion using the Project Object Model (POM). The NAR Plugin can be configured inside this model. Moreover the "nar" lifecycle can be used to automatically integrate all the NAR Plugin goals with the goals of the default "jar" lifecycle.

NAR's project object model (POM)

The POM below is an example of how to compile, link and test a native shared library, which depends on a native math library (nmath).

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <packaging>nar</packaging>
  <version>1.0-SNAPSHOT</version>
  <build>
    <plugins>
      <plugin>
        <groupId>com.github.maven-nar</groupId>
        <artifactId>nar-maven-plugin</artifactId>
        <version>3.4.1-SNAPSHOT</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.freehep</groupId>
      <artifactId>nmath</artifactId>
      <version>4.5.1</version>
      <type>nar</type>
    </dependency>
  </dependencies>
</project>

This POM will allow you to compile, link and test the java and native code in the project. The crucial part of the POM is the inclusion of the NAR plugin as an extension and the declaration of the "nar" packaging. The two will call the NAR Plugin goals as specified in the lifecycle.

For this POM the NAR Plugin will download and unpack the noarch and aol parts of the org.freehep nmath library version 4.5.1, for the platform you are running on.

Maven's Super POM is used for most of the default behaviour and the NAR Plugin's AOL Properties are used for native defaults.

NAR's build lifecycle

Maven's default build lifecycle, "jar", allows one to execute plugin goals as part of it, but this necessitates declaration of separate goals in each POM. To simplify the building of native artifacts we chose to define a "nar" lifecycle which calls all the goals the "jar" lifecycle has, interleaved with the goals of the NAR Plugin.

Coherent organization of dependencies

Maven uses a local repository to resolve its dependencies. If not found one or more remote repositories are consulted to find a dependency. If found the dependency is downloaded to the local repository and used from there by any of the plugins.

The NAR Plugin uses the same organization of dependencies and repositories. When a user executes install or deploy on a NAR project the main nar file with its attached nar artifacts will be installed in the local repository or deployed in the remote repository.

A remote repository for the native math library would probably look like this (note the support for multiple platforms and multiple library bindings):

remoterepo/
           org/
               freehep/
                       nmath/
                             4.5.1/
                                   nmath-4.5.1.pom
                                   nmath-4.5.1.pom.sha1
                                   nmath-4.5.1.jar
                                   nmath-4.5.1.jar.sha1
                                   nmath-4.5.1-noarch.nar
                                   nmath-4.5.1-noarch.nar.sha1
                                   nmath-4.5.1-MacOSX-g++-static.nar
                                   nmath-4.5.1-MacOSX-g++-static.nar.sha1
                                   nmath-4.5.1-MacOSX-g++-shared.nar
                                   nmath-4.5.1-MacOSX-g++-shared.nar.sha1
                                   nmath-4.5.1-Linux-g++-shared.nar
                                   nmath-4.5.1-Linux-g++-shared.nar.sha1
                                   nmath-4.5.1-Windows-msvc-shared.nar
                                   nmath-4.5.1-Windows-msvc-shared.nar.sha1
                                   ...

When a NAR dependency is encountered the main nar file is already downloaded into the local repository by maven itself. The nar-download goal will download any attached NAR artifacts into the local repository and store them alongside the main nar file and the POM. Note that for these main nar and attached nar files there is just one POM, since it is just one artifact. If the nar files are already in the local repository, due to someone calling nar-install on them, no extra download occurs.

Now that the POM, main nar and attached nar files are all in the local repository, the nar-unpack goal unpacks the nar files in an area in the target directory of the project. This is necessary because none of the native compiler/linker tools understands nar files. Compilation and linking can thus search the local target area.