Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build multi-platform executable for a SWT application using maven

My question is: How can I generate multiple executable Jar files (incl. dependencies) for a SWT application according to the target os/architecture at once using maven?

I have created a SWT application, which have to run on different operating systems and architectures. The project is a Maven multi-module project including a parent POM.

project-pom.xml (packaging = pom, specify SWT dependencies)
`- application-module (inherit SWT dependencies)
   `- pom.xml (packaging = jar)
`- domain-specific-module
   `- pom.xml (packaging = jar)
`- utils-module (inherit SWT dependencies)
   `- pom.xml (packaging = jar)

In the parent POM (project-pom.xml) I've included the SWT dependencies according to my os and architecture using maven profiles which works fine:

<dependencies>
    <dependency>
        <groupId>org.eclipse.swt</groupId>
        <artifactId>${swt.artifactId}</artifactId>
        <version>4.3</version>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>EclipseSwtRepository</id>
        <url>https://swt-repo.googlecode.com/svn/repo/</url>
    </repository>
</repositories>

<profiles>
    <profile>
        <id>unix-amd64</id>
        <activation>
            <os>
                <family>unix</family>
                <arch>amd64</arch>
            </os>
        </activation>
        <properties>
            <swt.artifactId>org.eclipse.swt.gtk.linux.x86_64</swt.artifactId>
        </properties>
    </profile>
    <profile>
        <id>windows-x86</id>
        <activation>
            <os>
                <family>windows</family>
                <arch>x86</arch>
            </os>
        </activation>
        <properties>
            <swt.artifactId>org.eclipse.swt.win32.win32.x86</swt.artifactId>
        </properties>
    </profile>
    <profile>
        <id>windows-x86_64</id>
        <activation>
            <os>
                <family>windows</family>
                <arch>x86_64</arch>
            </os>
        </activation>
        <properties>
            <swt.artifactId>org.eclipse.swt.win32.win32.x86_64</swt.artifactId>
        </properties>
    </profile>
</profiles>

Now maven selects the right SWT dependency, e.g. for compiling the application. I've checked the active profile with the maven command mvn help:active-profiles.

Now I want to generate the executable Jars for the target platforms linux_x86_64/amd64, windows_x86 and windows_x86_64. In the first step I used the maven-jar-plugin to generate a Jar and the manifest file (application-module/pom.xml):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>libs/</classpathPrefix>
                 <mainClass>qualified.path.to.MyApplication</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

This works fine. The Jar is generated and the manifest is within. As you can see, my intention is to place all the dependencies in the directory called lib/. But the Jar file is not executable (the libraries are missing).

The next step (in theory) is to find the dependencies and copy these in die lib/ directory during the package phase. For this step I (was trying to) use the maven-dependency-plugin (application-module/pom.xml):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.8</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/libs</outputDirectory>
                <overWriteReleases>false</overWriteReleases>
                <overWriteSnapshots>false</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
    </executions>
</plugin>

If I run the command mvn package nothing happens (no directory is created in target/ or elsewhere). To check if the plugin works properly I've used the command mvn dependency:copy-dependencies which created a directory called dependency (I assume the default for this plugin) and placed all dependent Jar files there.

This is the first problem I have. The next problem is, how can I create multiple (not only a single) executable Jar files according to the target os/architecture of my SWT application all at once (with a single command like mvn package)? The resulting structure of the build directory should look similar to this:

target/
`- linux_x86_64/
   `- application-version_linux_x86_64.jar
   `- libs/
      `- org.eclipse.swt.gtk.linux.x86_64-4.3.jar
      `- ...
`- windows_x86/
   `- application-version_windows_x86.jar
   `- libs/
      `- org.eclipse.swt.win32.win32.x86-4.3.jar
      `- ...
`- windows_x86_64/
   `- application-version_x86_64.jar
   `- libs/
      `- org.eclipse.swt.win32.win32.x86_64-4.3.jar
      `- ...

Another solution is to place the dependencies in the Jar file directly but currently I am not aware of how I can do this.

I read about some other plugins in this context (maven-assembly-plugin, maven-shade-plugin), but currently these are too much information to me and to check whether it will work or not. Can someone tell me which plugin is the best to use for my purposes?

After this is done eventually, I want to use the launch4j-maven-plugin to generate native windows executables (the end user doesn't like the feeling of non-native executables) using the previous generate executable Jar files for windows.

I hope these are enough information.

like image 307
Marcel Avatar asked Jan 22 '14 14:01

Marcel


1 Answers

I've done this using Maven by studying JackRabbit's setup. JackRabbit is delivered in all kinds of forms (standalone, OSGi bundle, webapp, etc.) and it is all done from one aggregator pom.

The first step is to separate your parent pom with profiles and common dependencies from an aggregator pom which defines the (order of the) sub-modules of your project. The first sub-module in the aggregator pom is usually the parent-pom. The advantage of the aggregator pom is that you can run Maven with the aggregator pom to build everything for your project in one go.

Next I created a "common assembly resources" project which produces a jar with all resources (e.g. readme, manual, pictures, etc.) which are used in all assemblies for a platform. Each platform assembly project extracts this jar and then copies the extracted files to a proper location. JackRabbit does not do this as far as I can see, so you can probably skip this step.

Finally, for each platform a separate platform-assembly sub-module is created. In the platform-assembly you can set which dependencies you want and do not want. The sub-module can contain files specific for the platform (e.g. batch-files for Windows, .sh-files for Linux). The assembly form can be made specific for the platform (e.g. .zip for Windows and .tar.gz for Linux). For Windows I also included NSIS to create a setup.exe (which you can also build on Linux with the NSIS-package installed).

I've used the maven-dependency-plugin a lot in this setup with different phases. One I often use is phase process-resources, you can try that instead of phase package.

like image 184
vanOekel Avatar answered Nov 03 '22 02:11

vanOekel