
Embeds resource directories into native executables via a Gradle plugin and archive format; supports incremental updates, LZ4 compression, memory-mapped reads, and a CLI.
konaResource provides embedded resources for Linux/x64 Kotlin/Native executables.
plugin is a Gradle plugin that updates embedded resources.common contains the KonaArchive format and the library for reading embedded resources.sample1 is a sample project that uses published artifacts.sample2 is a sample project that uses sibling modules.cli is a CLI tool for the KonaArchive format.@InputFiles is used so routine builds do no more work than necessary.DT_NEEDED entries as an empty Kotlin/Native binary.mallinfo, backtrace, backtrace_symbols_fd) that are unavailable in musl libc, making it impossible to compile or run Kotlin/Native programs on Alpine. See KT-38891 for upstream status.Example:
sample1/build.gradle.ktsplugins {
alias(libs.plugins.kotlinMultiplatform)
// Add the konaResource plugin
id("jp.juggler.konaResource") version "..."
}
kotlin {
sourceSets {
linuxX64Main.dependencies {
// Add the konaResource common module
implementation("jp.juggler.konaResource:common:...")
}
}
}
// Specify compression settings and resource directories
konaResource{
// LZ4 compression parameters. All parameters have default values and are optional.
// LZ4F compression level. 0 is the default fast compression, positive values use LZ4HC, and negative values use fast acceleration.
lz4CompressionLevel = 0
lz4BlockSizeID = 1MB
lz4BlockMode = "LZ4F_blockIndependent"
lz4ContentSizeFlag = true
lz4ContentChecksumFlag = true
lz4blockChecksumFlag = true
lz4AutoFlush = false
lz4FavorDecSpeed = false
// Skip embedding for selected Kotlin/Native targets.
// The target name is e.g. "linuxX64" or "macosArm64".
skipEmbedIf { targetName -> targetName == "macosArm64" }
// Used for the .o file name and symbol name
val name1 = "resources"
// Input directory for the resource archive
val inDir1 = "src/resources"
modules.add( name1 to inDir1 )
// Multiple name and input-directory pairs can be registered
modules.add( "resourcesB" to "src/resourcesB" )
}Example:
sample1/src/linuxX64Main/kotlin/jp/juggler/konaResource/sample/Main.kt// Open the archive in the embedded resources.
val root = embedKonaArchive("sample").root
// Read a file.
val bytes = root.pathToFile(path)?.bytes()
val string = root.pathToFile(path)?.string()
val buffer = root.pathToFile(path)?.buffer()
// Read a directory.
for (entry in root.pathToDir(path)!!) {
println("name=${entry.name}")
}The konaResource plugin embeds each configured resource directory into every
Kotlin/Native executable that belongs to the target project:
generateKonaResource<Target> task packs the directory into a KonaArchive .bin file..incbin, with exported start and end symbols.embedKonaArchive(name) converts the name to the same safe symbol name and resolves
konaResource_<name>_start and konaResource_<name>_end with kona_dlsym.EmbedRandomAccess reads that address range directly from the executable, and the
common decoder reads the KonaArchive metadata from it.The embedded range is read-only and is not copied wholesale into another buffer.
The common module provides the KonaArchive reader and writer for Linux/x64
Kotlin/Native code. Use the plugin module when embedding resource archives
into an executable.
# Build
./gradlew build
# detekt, kotest for some modules
./gradlew check
# Run sample1, sample2
# Build and run the sample1 that uses published artifacts
./gradlew sample1:runDebugExecutableLinuxX64
./gradlew sample1:runReleaseExecutableLinuxX64
# Build and run the sample2 that uses sibling modules
./gradlew sample2:runDebugExecutableLinuxX64
./gradlew sample2:runReleaseExecutableLinuxX64reason to cross-platform, this app build separate binary to unit test.
./gradlew test:deploy
java -jar bin/konaCommonTest.jar test
./bin/konaCommonTest-linuxX64 test
# (or some binalies for each build-available arch)
# Execute benchmark on the JVM
./gradlew :benchmark:runJvm
# Execute benchmark on the host's Native target
./gradlew :benchmark:runRelease
# Build and deploy standalone benchmark artifacts
./gradlew :benchmark:deployThe project is built and tested on JDK 21 with the C toolchain. The GitHub Actions
workflows run on ubuntu-24.04 / ubuntu-24.04-arm (with gcc, g++, make
preinstalled) and install Java 21 via actions/setup-java. To reproduce the same
environment locally on Ubuntu:
# JDK 21 (matching the CI)
sudo apt install openjdk-21-jdk
# C toolchain (gcc, g++, make)
sudo apt install build-essentialthis project uses 2 kind of Native code.
plugin module, that need to build embed resource.common module and embed to user application.JNI and resource object builds use the Kotlin/Native compiler distribution's run_konan wrapper.
The build script automatically detects which targets are available from kotlinc-native -list-targets.
./gradlew :commonJni:listAvailableJniBuildTargetsThe default JNI compiler options can be overridden for one host/target pair with Gradle properties.
The host and target names are the enum names in KonaBuildHost and JniBuildTarget.
./gradlew \
-PLinuxX64_MingwX64_compileOpt=-Wall,-Wextra,-O3,-D_JNI_IMPLEMENTATION_ \
-PLinuxX64_MingwX64_linkOpt=-shared \
:common:jvmJar{host}_{target}_compileOpt replaces the default C compiler options. Options are comma-separated.{host}_{target}_linkOpt replaces the default linker options. Options are comma-separated.${project}/jdk/${ArchName}/ .${project}/jdk/${ArchName}/include/jni.h.jni.h are available.common module's native targets (linuxX64 / linuxArm64 / mingwX64) are cross-compiled
by Kotlin/Native itself.# Deploy the CLI fat JAR and launcher
./gradlew cli:deploy
# Convert a directory to an archive
java -jar bin/konaArchive.jar pack sample1Res.kona sample1/src/res
# List the contents of an archive
java -jar bin/konaArchive.jar list sample1Res.kona
# Extract an archive
java -jar bin/konaArchive.jar extract sample1Res.kona /tmp/sample1ReskonaResource provides embedded resources for Linux/x64 Kotlin/Native executables.
plugin is a Gradle plugin that updates embedded resources.common contains the KonaArchive format and the library for reading embedded resources.sample1 is a sample project that uses published artifacts.sample2 is a sample project that uses sibling modules.cli is a CLI tool for the KonaArchive format.@InputFiles is used so routine builds do no more work than necessary.DT_NEEDED entries as an empty Kotlin/Native binary.mallinfo, backtrace, backtrace_symbols_fd) that are unavailable in musl libc, making it impossible to compile or run Kotlin/Native programs on Alpine. See KT-38891 for upstream status.Example:
sample1/build.gradle.ktsplugins {
alias(libs.plugins.kotlinMultiplatform)
// Add the konaResource plugin
id("jp.juggler.konaResource") version "..."
}
kotlin {
sourceSets {
linuxX64Main.dependencies {
// Add the konaResource common module
implementation("jp.juggler.konaResource:common:...")
}
}
}
// Specify compression settings and resource directories
konaResource{
// LZ4 compression parameters. All parameters have default values and are optional.
// LZ4F compression level. 0 is the default fast compression, positive values use LZ4HC, and negative values use fast acceleration.
lz4CompressionLevel = 0
lz4BlockSizeID = 1MB
lz4BlockMode = "LZ4F_blockIndependent"
lz4ContentSizeFlag = true
lz4ContentChecksumFlag = true
lz4blockChecksumFlag = true
lz4AutoFlush = false
lz4FavorDecSpeed = false
// Skip embedding for selected Kotlin/Native targets.
// The target name is e.g. "linuxX64" or "macosArm64".
skipEmbedIf { targetName -> targetName == "macosArm64" }
// Used for the .o file name and symbol name
val name1 = "resources"
// Input directory for the resource archive
val inDir1 = "src/resources"
modules.add( name1 to inDir1 )
// Multiple name and input-directory pairs can be registered
modules.add( "resourcesB" to "src/resourcesB" )
}Example:
sample1/src/linuxX64Main/kotlin/jp/juggler/konaResource/sample/Main.kt// Open the archive in the embedded resources.
val root = embedKonaArchive("sample").root
// Read a file.
val bytes = root.pathToFile(path)?.bytes()
val string = root.pathToFile(path)?.string()
val buffer = root.pathToFile(path)?.buffer()
// Read a directory.
for (entry in root.pathToDir(path)!!) {
println("name=${entry.name}")
}The konaResource plugin embeds each configured resource directory into every
Kotlin/Native executable that belongs to the target project:
generateKonaResource<Target> task packs the directory into a KonaArchive .bin file..incbin, with exported start and end symbols.embedKonaArchive(name) converts the name to the same safe symbol name and resolves
konaResource_<name>_start and konaResource_<name>_end with kona_dlsym.EmbedRandomAccess reads that address range directly from the executable, and the
common decoder reads the KonaArchive metadata from it.The embedded range is read-only and is not copied wholesale into another buffer.
The common module provides the KonaArchive reader and writer for Linux/x64
Kotlin/Native code. Use the plugin module when embedding resource archives
into an executable.
# Build
./gradlew build
# detekt, kotest for some modules
./gradlew check
# Run sample1, sample2
# Build and run the sample1 that uses published artifacts
./gradlew sample1:runDebugExecutableLinuxX64
./gradlew sample1:runReleaseExecutableLinuxX64
# Build and run the sample2 that uses sibling modules
./gradlew sample2:runDebugExecutableLinuxX64
./gradlew sample2:runReleaseExecutableLinuxX64reason to cross-platform, this app build separate binary to unit test.
./gradlew test:deploy
java -jar bin/konaCommonTest.jar test
./bin/konaCommonTest-linuxX64 test
# (or some binalies for each build-available arch)
# Execute benchmark on the JVM
./gradlew :benchmark:runJvm
# Execute benchmark on the host's Native target
./gradlew :benchmark:runRelease
# Build and deploy standalone benchmark artifacts
./gradlew :benchmark:deployThe project is built and tested on JDK 21 with the C toolchain. The GitHub Actions
workflows run on ubuntu-24.04 / ubuntu-24.04-arm (with gcc, g++, make
preinstalled) and install Java 21 via actions/setup-java. To reproduce the same
environment locally on Ubuntu:
# JDK 21 (matching the CI)
sudo apt install openjdk-21-jdk
# C toolchain (gcc, g++, make)
sudo apt install build-essentialthis project uses 2 kind of Native code.
plugin module, that need to build embed resource.common module and embed to user application.JNI and resource object builds use the Kotlin/Native compiler distribution's run_konan wrapper.
The build script automatically detects which targets are available from kotlinc-native -list-targets.
./gradlew :commonJni:listAvailableJniBuildTargetsThe default JNI compiler options can be overridden for one host/target pair with Gradle properties.
The host and target names are the enum names in KonaBuildHost and JniBuildTarget.
./gradlew \
-PLinuxX64_MingwX64_compileOpt=-Wall,-Wextra,-O3,-D_JNI_IMPLEMENTATION_ \
-PLinuxX64_MingwX64_linkOpt=-shared \
:common:jvmJar{host}_{target}_compileOpt replaces the default C compiler options. Options are comma-separated.{host}_{target}_linkOpt replaces the default linker options. Options are comma-separated.${project}/jdk/${ArchName}/ .${project}/jdk/${ArchName}/include/jni.h.jni.h are available.common module's native targets (linuxX64 / linuxArm64 / mingwX64) are cross-compiled
by Kotlin/Native itself.# Deploy the CLI fat JAR and launcher
./gradlew cli:deploy
# Convert a directory to an archive
java -jar bin/konaArchive.jar pack sample1Res.kona sample1/src/res
# List the contents of an archive
java -jar bin/konaArchive.jar list sample1Res.kona
# Extract an archive
java -jar bin/konaArchive.jar extract sample1Res.kona /tmp/sample1Res