This is an example of how to create a Modern CMake C++/Java Project.
This project aim to explain how you build a Java 1.8 native (for win32-x86-64,
linux-x86-64 and darwin-x86-64) maven multiple package using mvn
and few POM.xml.
e.g. You have a cross platform C++ library and a JNI wrapper on it thanks to SWIG.
Then you want to provide a cross-platform Maven package to consume it in a
Maven project...
This project should run on:
- MacOS (darwin-aarch64, darwin-x86-64)
- GNU/Linux (linux-aarch64, linux-x86-64)
- Windows (win32-x86-64)
You'll need:
- "CMake >= 3.18".
- "Java SDK >= 1.8" and "Maven >= 3.6".
Please verify you also have the JAVA_HOME
environment variable set otherwise CMake
and Maven won't be able to find your Java SDK.
The project layout is as follow:
-
CMakeLists.txt Top-level for CMake based build.
-
cmake Subsidiary CMake files.
- java.cmake All internall Java CMake stuff.
-
ci Root directory for continuous integration.
-
Foo Root directory for
Foo
library.- CMakeLists.txt for
Foo
. - include public folder.
- src private folder.
- java
- CMakeLists.txt for
Foo
Java. - foo.i SWIG Java wrapper.
- CMakeLists.txt for
- CMakeLists.txt for
-
Bar Root directory for
Bar
library.- CMakeLists.txt for
Bar
. - include public folder.
- src private folder.
- java
- CMakeLists.txt for
Bar
Java. - bar.i SWIG Java wrapper.
- CMakeLists.txt for
- CMakeLists.txt for
-
FooBar Root directory for
FooBar
library.- CMakeLists.txt for
FooBar
. - include public folder.
- src private folder.
- java
- CMakeLists.txt for
FooBar
Java. - foobar.i SWIG Java wrapper.
- CMakeLists.txt for
- CMakeLists.txt for
-
java Root directory for Java template files
- base.i Generic SWIG stuff (e.g. fixing int64 java typemaps).
- pom-native.xml.in POM template to build the native project.
- Loader.java Unpack and load the correct native libraries.
- pom-local.xml.in POM template to build the "pure" Java project.
- Test.java Test source code to verify the Java wrapper is working.
- pom-test.xml.in POM template to build the test project.
To complexify a little, the CMake project is composed of three libraries (Foo, Bar and FooBar) with the following dependencies:
Foo:
Bar:
FooBar: PUBLIC Foo PRIVATE Bar
To Create a native dependent package we will split it in two parts:
- A bunch of
org.mizux.javanative:javanative-{platform}
maven packages for each supported platform targeted and containing the native libraries. - A generic maven package
org.mizux.javanative:javanative-java
depending on each native packages and containing the Java code.
platform
names come from the JNA project (Java Native Access) which will be use to find at runtime on which platform the code is currently running.
The pipeline for linux-x86-64
should be as follow:
note: The pipeline will be similar for other architecture,
don't hesitate to look at the CI log!
disclaimer: In this git repository, we use CMake
and SWIG
.
Thus we have the C++ shared library libFoo.so
and the SWIG generated Java wrapper Foo.java
.
note: For a C++ CMake cross-platform project sample, take a look at Mizux/cmake-cpp.
note: For a C++/Swig CMake cross-platform project sample, take a look at Mizux/cmake-swig.
So first let's create the local org.mizux.javanative:javanative-{platform}.jar
maven package.
Here some dev-note concerning this POM.xml
.
- This package is a native package only containing native libraries.
Then you can generate the package and install it locally using:
mvn package
mvn install
note: this will automatically trigger the mvn compile
phase.
If everything good the package (located in
<buildir>/java/org.mizux.javanative-<platform>/target/
) should have this layout:
{...}/target/javanative-<platform>-1.0.jar:
\- <platform>
\-libFoo.so.1.0
\-libjnijavanative.so
...
note: <platform>
could be linux-x86-64
, darwin-x86-64
or win32-x86-64
.
tips: since maven package are just zip archive you can use unzip -l <package>.jar
to study their layout.
So now, let's create the local org.mizux.javanative:javanative-java.jar
maven
package which will depend on our previous native package.
Here some dev-note concerning this POM.xml
.
- Add runtime dependency on each native package(s) availabe:
<dependency> <groupId>org.mizux.javanative</groupId> <artifactId>javanative-linux-x86-64</artifactId> <version>[1.0,)</version> <type>jar</type> <scope>runtime</scope> </dependency>
- Add dependency to jna so we can find at runtime the current
<platform>
:
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> <version>5.13.0</version> </dependency>
- Add dependency to jna so we can find at runtime the current
Then you can generate the package using:
mvn package
mvn install
If everything good the package (located in
<buildir>/java/org.mizux.javanative/target/
) should have this layout:
{...}/target/javanative-java-1.0.jar:
\- org/
\- mizux/
\- javanative/
\- Loader$PathConsumer.class
\- Loader$1.class
\- Loader.class
\- foo/
\- GlobalsJNI.class
\- StringJaggedArray.class
\- IntPair.class
\- StringVector.class
\- Foo.class
\- PairVector.class
\- PairJaggedArray.class
\- Globals.class
...
We can test everything is working by using the org.mizux.javanative.test:javanative-test
project.
First you can build it using:
cmake --build build
note: javanative-test
depends on javanative-java
which is locally installed in the local maven cache
(~/.m2/repository/org/mizux/javanative/...
).
Then you can run it using:
cmake --build build --target test
or manually using:
cd <builddir>/java/org.mizux.javanative.test
mvn exec:java -Dexec.mainClass="org.mizux.javanative.Test"
Few links on the subject...
Project layout:
- The Pitchfork Layout Revision 1 (cxx-pflR1)
CMake:
- https://llvm.org/docs/CMakePrimer.html
- https://cliutils.gitlab.io/modern-cmake/
- https://cgold.readthedocs.io/en/latest/
Java:
- POM.xml reference
- Maven Central POM requirement
- Javadoc Plugin
- Java Source Plugin
- Java Native Access Project
Image has been generated using plantuml:
plantuml -Tsvg docs/{file}.dot
So you can find the dot source files in docs.
Apache 2. See the LICENSE file for details.
This is not an official Google product, it is just code that happens to be owned by Google.