Tag Archives: Maven - Page 2

Building c/c++ applications with maven

Apache Maven is, quoting from their website:

a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.

and is usually used to build java applications. It has a plugin architecture which allows extending it’s base functionality and did not take too long until plugins appeared for building native applications with it. One of such plugins is Maven NAR Plugin, wich allows building native c++, c and fortran code on a number of architectures – Linux, Windows, MacOS, Solaris .. – with a various number of compilers/linkers, like g++, Microsoft Visual C++, CC, etc.

The nice thing about this plugin is that it wraps the build output in a so called native archive (nar) in machine-dependent and machine independent forms and these archives can be installed in local maven repository and deployed to a standard maven web server using the standard maven plugins. They also can be declared as dependencies for other projects using them using the standard maven dependency declaration and they’re downloaded, unpacked, installed in the local repository just like the java archives. For more information about the nar archives and the Maven NAR Plugin, please follow this link.

For this article, we will have a sample native C project which depends on a native shared library and we’ll try to set up the build system with maven and Maven NAR Plugin. So let’s begin.

Directory structure and project layout

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. Therefore a project to be built with maven nar plugin, will have to have this structure:

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

(Please note that this structure can be altered and freely configured, however we’ll not cover this aspect in this article, and we’ll rely on the standard setup here).

Since we are going to have two projects, a native application and a native shared library, we’re going to use a project with two submodules, one being the shared library and the other being the application itself. So our directory structure will be something like this:

├── pom.xml (1)
├── test-app
│   ├── bin
│   ├── pom.xml (2)
│   └── src
│       └── main
│           └── c++
│               └── main.cpp
└── test-library
    ├── pom.xml (3)
    └── src
        └── main
            ├── c++
            │   ├── dummy.cpp
            └── include
                └── test.h

Project setup an pom.xml file

So let’s take it step by step. First the project pom file – the one marked with (1). This should include reference to the submodules test-app and test-library and we’ll also declare here some dependencies and the repositories where maven finds the maven-nar plugin, and we’ll end up with something like this:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.awakening</groupId>
    <artifactId>mvn-test</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>

    <modules>
        <module>test-library</module>
        <module>test-app</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>net.sf.antcontrib</groupId>
            <artifactId>cpptasks-parallel</artifactId>
            <version>1.0-beta-5-parallel-1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <pluginRepositories>
	<pluginRepository>
		<id>Mark.Donszelmann@gmail.com</id>
		<name>Duns' Maven Snapshot Repository</name>
		<url>http://duns.github.com/maven-snapshots/</url>
		<releases>
			<enabled>false</enabled>
			<updatePolicy>never</updatePolicy>
		</releases>
		<snapshots>
			<enabled>true</enabled>
			<updatePolicy>daily</updatePolicy>
		</snapshots>
	</pluginRepository>

	<pluginRepository>
		<id>Mark.Donszelmann@gmail.com-custom-build</id>
		<name>Duns' Maven Snapshot Repository (custom build)</name>
		<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
		<releases>
			<enabled>false</enabled>
			<updatePolicy>never</updatePolicy>
		</releases>
		<snapshots>
			<enabled>true</enabled>
			<updatePolicy>daily</updatePolicy>
		</snapshots>
	</pluginRepository>
    </pluginRepositories>

</project>

Setting up the shared library project

Next, we create a subfolder for the shared library like we specifiede earlier and create the include file and the source file in specified locations, with some test function, like this:

test.h:

#ifndef __TEST_H__
#define __TEST_H__
extern int function(int a);
#endif

dummy.cpp:

#include <test.h>
int function(int a)
{
 return a+1;
}

and then the pom.xml for building this shared library:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.awakening</groupId>
        <artifactId>mvn-test</artifactId>
        <version>1.0</version>
    </parent>

    <groupId>com.awakening.mvn-test</groupId>
    <artifactId>test-library</artifactId>
    <packaging>nar</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <skipTests>true</skipTests>
    </properties>

    <build>
        <defaultGoal>install</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-nar-plugin</artifactId>
                <version>2.1-SNAPSHOT</version>
                <extensions>true</extensions>
                <configuration>
                    <layout>NarLayout20</layout>
                    <libraries>
                        <library>	
                            <type>shared</type>
                        </library>
                    </libraries>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

As you can see, nothing very special here. We define the parent project were this module belongs to and then we add maven-nar-plugin to build, and we specify in it’s configuration that we’re going to build a shared library.

Setting up the application project

First, let’s define a source file which uses the function we’ve declared in the shared library:

main.cpp:

#include <stdio.h>
#include <test.h>

extern int function(int p);

int main(int argc, char** argv)
{
  printf("hello world %d\n", function(10));
}

And finally we need the pom.xml file to build this project:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.awakening</groupId>
        <artifactId>mvn-test</artifactId>
        <version>1.0</version>
    </parent>

    <groupId>com.awakening.mvn-test</groupId>
    <artifactId>test-app</artifactId>
    <packaging>nar</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <skipTests>true</skipTests>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.awakening.mvn-test</groupId>
            <artifactId>test-library</artifactId>
            <version>1.0-SNAPSHOT</version>
            <type>nar</type>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>integration-test</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-nar-plugin</artifactId>
                <version>2.1-SNAPSHOT</version>
                <extensions>true</extensions>

                <configuration>

                    <libraries>
                        <library>
                            <type>executable</type>
                            <run>true</run>
                        </library>
                    </libraries>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <configuration>
                            <tasks>
                                <copy todir="bin" flatten="true">
                                    <fileset dir="target">
                                        <include name="**/*so"/>
                                        <include name="**/*test-app"/>
                                    </fileset>

                                </copy>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project>

There is a small trick here. During the packaging phase, we use maven-antrun-plugin to run ant and extract the executable artifacts (the application itself and the dependent shared libraries) in a ./bin foder which would allow us running the application way easier. You can also notice that this time for maven-nar-plugin we’ve specified in config the type as executable.

Also, please notice in case of native archive dependencies, is required to specify the dependency type to ‘nar’!

These being said, by running mvn package or mvn install, the build starts up and after everything completes, you will end up with the application and the shared library in the test-app/bin folder.
In order to run the application, since we specified <run>true</run>, we can run it using maven like this:

	mvn test

and will display something like this:

	[INFO] hello world 11

Note that this will also run the tests if there are any.
Since we have the application and the shared library in the bin folder tank to the antrun plugin we ran earlier, we can alternatively run it like this:

	~/work/mvn-test$ chmod +x test-app/bin/test-app 
	~/work/mvn-test$ env LD_LIBRARY_PATH=test-app/bin/ test-app/bin/test-app 
	hello world 11

Source of this project can be viewed/downloaded from this repository.