This is a support library for loading and writing TurnKey bundles. A TurnKey bundle is a Java library that depends on a native library which is included in the JAR archive and unpacked at runtime. This requires distributing the library and its dependencies for all supported platforms inside the JAR.
However, loading libraries in the JVM is only possible if these libraries are present in the file system. Therefore, all required libraries are unpacked to a temporary directory by the support library. Additionally, loading of dependencies can not be done in a fully platform-agnostic way: for some platforms, libraries can be (re-) linked to search for their dependencies in the same directory, while for others, the dependencies must be explicitly loaded by the JVM beforehand. A TurnKey bundle therefore includes a metadata file that a) lists all required libraries and b) lists the required library load commands for the target platform in order.
A TurnKey bundle is identified by a prefix, which should correspond to the package name used by the library's Java code (e.g., a JNI binding) to avoid namespace conflicts.
The support library is published as a Maven artifact on Maven Central and can be included in every Maven-supporting build tool.
The library defines a single entry point for loading bundles, TurnKey.load
. This method accepts
the bundle prefix and a function that can be used to load libraries, usually the loading class'
getResourceAsStream
method. The latter is necessary to handle Java modularity, sind the libraries
can only be accessed from code in the same module, not the support library.
A sensible place for calling this method is the static initializer of, e.g., a JNI wrapper class:
package com.acme.example;
import tools.aqua.turnkey.support.TurnKey;
class Example {
static {
TurnKey.load("com/acme/example", Example.class::getResourceAsStream);
}
}
A TurnKey bundle can be constructed by placing all required files in a specific structure and adding
a metadata file. Supported platforms are defined by operating system and CPU architecture. The
JAR file should then contain all required files at $prefix/$os/$arch
.
For each supported platform, the file turnkey.xml
must be present. It can be authored via the
TurnKeyMetadata
class.
The turnkey.xml
file defines all metadata for the required support files. It is a Java Properties
XML file that stores collections of items as follows: for the list key k
, the contents of k
are
stored as k.0
, k.1
, etc. Each metadata file contains:
- the set
bundled-libraries
, containing all files that must be unpacked from the support file directory for the native code to work, - the set
system-libraries
, containing all system libraries that are not bundled but must be present for the library to work (at the moment, this information is not used by the support library), and - the list
load-command
, listing the required libraries in load order. If the platform support linking to libraries in the same directory, this will usually only contain a “root” library, if not, it will contain the dependency graph in inverse topological order.
For the library libexample.so
by ACME, Inc., a TurnKey bundle might contain:
com/acme/example/Example.class # JNI binding
com/acme/example/windows/x86/turnkey.xml # Windows x86 metadata
com/acme/example/windows/x86/example.dll # Windows x86 library file
com/acme/example/linux/amd64/turnkey.xml # Linux AMD64 metadata
com/acme/example/linux/amd64/example.so # Linux AMD64 library file
The library requires Java 8. It can be used as a Java module on Java 9+ via the multi-release JAR
mechanism as the tools.aqua.turnkey.support
module. It uses JSpecify
annotations to declare nullability metadata.
The support library's runtime code is released under the ISC License. Tests and other non-runtime code are licensed under the Apache Licence, Version 2.0. Standalone documentation is licensed under the Creative Commons Attribution 4.0 International License. The library uses JSpecify annotations, which are licensed under the Apache Licence, Version 2.0.
The turnkey-gradle-plugin provides facilities for rewriting non-TurnKey library files as well as modifying Java source code (to insert calls to this library) via Gradle.
At the moment, two libraries are using this infrastructure: Z3-TurnKey and cvc5-TurnKey.