--- Maven NAR Plugin --- --- Mark Donszelmann --- NAR Philosophy In the NAR plugin we follow Maven's main principles: * {{{#Convention over configuration}Convention over configuration}} * {{{#Reuse of build logic}Reuse of build logic}} * {{{#Declarative execution}Declarative execution}} * {{{#Coherent organization of dependencies}Coherent organization of dependencies}} [] 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: * {{{#Standard directory layout for Native projects}Standard directory layout for Native projects}} * {{{#A single Native project produces a single output}A single Native project produces a single output}} * {{{#Standard naming conventions}Standard naming conventions}} [] 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 attached artifact 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 <> 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 its normally named <<>>. The NAR Plugin follows this convention for its attached artifacts: [<<>>] contains the non architecture specific parts of the native library, such as include headers. [<<>>] contains the architecture specific parts of the native library, such as the library and object files. The is an Architecture-OS-Linker qualifier, for example: ppc-MacOSX-g++. The 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: +-- ... maven-nar-plugin false true ... +-- * {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). +-- 4.0.0 com.mycompany.app my-app nar 1.0-SNAPSHOT maven-nar-plugin nar-version-number true org.freehep nmath 4.5.1 nar +-- 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.html}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.