The JNI Library Plugin provides the tasks, configurations and conventions for building libraries using the Java Native Interface runtime. It provides both an entry point (library) and runtime (JNI). An JVM (i.e. the Java Plugin) and native implementation (i.e. the C++ Language Plugin or C Language Plugin) language plugins are required to be applied to produce anything meaningful.

Usage

Example 1. Applying the JNI Library Plugin
build.gradle
plugins {
   id 'dev.nokee.jni-library'
}
build.gradle.kts
plugins {
   id("dev.nokee.jni-library")
}
This plugin produces a JNI library as JARs using the JNI runtime. It doesn’t provide any implementation language capabilities and thus needs to be composed by applying other implementation language plugins, such as C++ Language Plugin, C Language Plugin, Objective-C Language Plugin, Objective-C++ Language Plugin and Java Plugin.
This plugin is incompatible with the Java Library Plugin and Java Application Plugin. Those two plugins provide an opinionated entry point (e.g. library and application, respectively). They cannot be applied within the same project. Instead, use the Java Language Plugin. The plugin can consume JVM libraries produced by the Java Library Plugin without any issue.

Tasks

The following diagram shows the relationships between tasks added by this plugin.

jni library task graph
Figure 1. JNI Library Plugin default task graph
jarJar

Depends on: All the task which contribute to the JAR library (e.g. classes)

Assembles the production JAR file, based on the classes and resources attached to the main source set.

Variant-dependent Tasks

The JNI Library Plugin creates tasks based on variants of the library component. Read the introduction to build variants for more information.

linkVariant (e.g. linkWindows and linkLinux) - Task (lifecycle)

Depends on: All tasks which contribute to the link libraries, including linkVariant and createVariant tasks from projects that are resolved via project dependencies

Links shared library from compiled object files using the selected linker.

sharedLibraryVariant (e.g. sharedLibraryWindows and sharedLibraryLinux) - Task (lifecycle)

Depends on: linkVariant This is an aggregate task that depends on tasks for creating the Shared Library binary.

jarVariant (e.g. jarWindows and jarLinux) - Jar

Depends on: sharedLibraryVariant

Assembles the production JAR file, based on the classes and resources attached to the main source set. The task will contain the shared library for the JNI bridge as well as all the native runtime dependencies when a single target machine is configured. When multiple target machines are configured, a separate JAR task is created for each variant containing the respective shared library and native runtime dependencies.

Lifecycle Tasks

The JNI Library Plugin attaches some of its tasks to the standard lifecycle tasks documented in the Base Plugin chapter — which the <<jni-library-plugin.adoc#,JNI Library Plugin] applies automatically:

assemble - Task (lifecycle)

Aggregates task that assembles the debug variant of the shared library (if available) for the current host (if present) in the project as well as the JAR. This task is added by the Base Plugin.

check - Task (lifecycle)

Aggregates task that performs verification tasks, such as running the tests. Some plugins add their verification task to check. This task is added by the Base Plugin.

build - Task (lifecycle)

Depends on: check, assemble

Aggregate tasks that perform a full build of the project. This task is added by the Base Plugin.

clean - Delete

Deletes the build directory and everything in it, i.e. the path specified by the Project.getBuildDir() project property. This task is added by the Base Plugin.

Dependency management

The following graph describes the configurations added by the JNI Library Plugin:

jni library configurations
Figure 2. JNI Library Plugin configurations
  • The configurations in white are the ones a user should use to declare dependencies

  • The configurations in pink, also known as consumable denoted by (C), are the ones used when a component runs against the library

  • The configurations in blue, also known as resolvable denoted by (R), are internal to the component, for its own use

To declare dependencies, it is preferred to use the library extension:

Example 2. Adding JVM and native dependencies
build.gradle
library {
   dependencies {
      api 'org.apache.httpcomponents:httpclient:4.5.7'
      jvmImplementation 'org.apache.commons:commons-lang3:3.5'
      nativeImplementation 'io.qt:network:5.1'
   }
}
build.gradle.kts
library {
   dependencies {
      api("org.apache.httpcomponents:httpclient:4.5.7")
      jvmImplementation("org.apache.commons:commons-lang3:3.5")
      nativeImplementation("io.qt:network:5.1")
   }
}

The following configurations can be used to declare dependencies:

api

Used for declaring API dependencies for JVM consumers (see API vs implementation section of the Java Library Plugin chapter). This is where you should declare dependencies that are transitively exported to consumers for compile. Note that only JVM dependencies are transitively exported to consumers.

jvmImplementation extends api

Used for declaring implementation dependencies for the JVM component (see API vs implementation section of the Java Library Plugin chapter). This is where you should declare dependencies which are purely internal and not meant to be exposed to consumers.

nativeImplementation

Used for declaring implementation dependencies for the native shared library component (see API vs implementation section of the C++ Library Plugin chapter). This is where you should declare dependencies which are purely internal and not meant to be exposed to consumers.

The following configurations are used by consumers:

apiElements extends jvmImplementation

Used for compiling the library. This configuration is meant to be used by consumers to retrieve all the elements necessary to compile against the library.

runtimeElements extends jvmImplementation

Used for executing the library. This configuration is meant to be used by consumers to retrieve all the elements necessary to run against the library.

The following configuration is used by the library itself:

nativeRuntimeVariant (e.g. nativeRuntimeDebug) extends nativeImplementation

Used for executing the library. This configuration contains the native runtime libraries of the shared library.

Library Extension

The plugin expose a Gradle extension to use for customizing the library. The extension name is library and is of type JavaNativeInterfaceLibrary.

Properties

targetMachines

Configure the target machines of the library. Each target machines is a TargetMachine. It can be created via TargetMachineFactory.

variants

A view of all the variants available for the library. Each variant is a JniLibrary.

Methods

dependencies(action)

Configure the dependencies of the JNI library component.