Native jni build (#5)

* Each native library is in its own jar. Got rid of cross compilation. The project is `secp256k1` and not `secp256k1-kmp`.

* Updated CI to use all 3 OS VMs

Co-authored-by: Salomon BRYS <salomon@kodein.net>
This commit is contained in:
Salomon BRYS
2020-07-09 15:54:34 +03:00
committed by GitHub
parent c5dbc42496
commit 6c850eb2c4
20 changed files with 393 additions and 694 deletions

View File

@@ -3,8 +3,6 @@ plugins {
`maven-publish`
}
val currentOs = org.gradle.internal.os.OperatingSystem.current()
kotlin {
explicitApi()
}
@@ -14,7 +12,7 @@ dependencies {
implementation(kotlin("stdlib-jdk8"))
}
val generateJniHeaders by tasks.creating(JavaCompile::class) {
val generateHeaders by tasks.creating(JavaCompile::class) {
group = "build"
classpath = sourceSets["main"].compileClasspath
destinationDir = file("${buildDir}/generated/jni")
@@ -29,52 +27,6 @@ val generateJniHeaders by tasks.creating(JavaCompile::class) {
}
}
sealed class Cross {
abstract fun cmd(target: String, project: Project): List<String>
class DockCross(val cross: String) : Cross() {
override fun cmd(target: String, project: Project): List<String> = listOf("${project.rootDir}/cross-scripts/dockcross-$cross", "bash", "-c", "CROSS=1 TARGET=$target jni/build.sh")
}
class MultiArch(val crossTriple: String) : Cross() {
override fun cmd(target: String, project: Project): List<String> {
val uid = Runtime.getRuntime().exec("id -u").inputStream.use { it.reader().readText() }.trim().toInt()
return listOf(
"docker", "run", "--rm", "-v", "${project.rootDir.absolutePath}:/workdir",
"-e", "CROSS_TRIPLE=$crossTriple", "-e", "TARGET=$target", "-e", "TO_UID=$uid", "-e", "CROSS=1",
"multiarch/crossbuild", "jni/build.sh"
)
}
}
}
val buildNativeJni by tasks.creating {
group = "build"
}
val noCrossCompile: String? by project
fun creatingBuildNativeJni(target: String, cross: Cross?) = tasks.creating(Exec::class) {
group = "build"
dependsOn(generateJniHeaders)
dependsOn(":native:buildSecp256k1${target.capitalize()}")
buildNativeJni.dependsOn(this)
if (noCrossCompile == "true") onlyIf { cross == null }
inputs.files(projectDir.resolve("build.sh"))
outputs.dir(buildDir.resolve("build/cmake/$target"))
workingDir = rootDir
environment("TARGET", target)
commandLine((cross?.cmd(target, project) ?: emptyList()) + "jni/build.sh")
}
val buildNativeJniDarwin by creatingBuildNativeJni("darwin", if (currentOs.isMacOsX) null else Cross.MultiArch("x86_64-apple-darwin"))
val buildNativeJniLinux by creatingBuildNativeJni("linux", if (currentOs.isLinux) null else Cross.DockCross("linux-x64"))
val buildNativeJniMingw by creatingBuildNativeJni("mingw", if (currentOs.isWindows) null else Cross.DockCross("windows-x64"))
afterEvaluate {
tasks["clean"].doLast {
delete(buildDir.resolve("build/cmake"))
}
}
publishing {
publications {
create<MavenPublication>("jvm") {

View File

@@ -12,6 +12,8 @@
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall
#include <stdint.h>
typedef long jint;
typedef int64_t jlong;
typedef signed char jbyte;

View File

@@ -0,0 +1,20 @@
plugins {
`java-library`
// `maven-publish`
id("ru.vyarus.pom") version "2.1.0"
}
dependencies {
api(project(":jni:jvm:darwin"))
api(project(":jni:jvm:linux"))
api(project(":jni:jvm:mingw"))
}
publishing {
publications {
create<MavenPublication>("jvm") {
artifactId = "secp256k1-jni-jvm"
from(components["java"])
}
}
}

View File

@@ -3,8 +3,27 @@ plugins {
`maven-publish`
}
kotlin {
explicitApi()
val currentOs = org.gradle.internal.os.OperatingSystem.current()
val bash = if (currentOs.isWindows) "bash.exe" else "bash"
val buildNativeHost by tasks.creating(Exec::class) {
group = "build"
dependsOn(":jni:generateHeaders")
dependsOn(":native:buildSecp256k1Host")
val target = when {
currentOs.isLinux -> "linux"
currentOs.isMacOsX -> "darwin"
currentOs.isWindows -> "mingw"
else -> error("Unsupported OS $currentOs")
}
inputs.files(projectDir.resolve("build.sh"))
outputs.dir(buildDir.resolve(target))
workingDir = projectDir
environment("TARGET", target)
commandLine(bash, "build.sh")
}
dependencies {
@@ -12,26 +31,17 @@ dependencies {
implementation(kotlin("stdlib-jdk8"))
}
val copyJni by tasks.creating(Sync::class) {
dependsOn(":jni:buildNativeJni")
from(rootDir.resolve("jni/build/jni/linux/libsecp256k1-jni.so")) { rename { "libsecp256k1-jni-linux-x86_64.so" } }
from(rootDir.resolve("jni/build/jni/darwin/libsecp256k1-jni.dylib")) { rename { "libsecp256k1-jni-darwin-x86_64.dylib" } }
from(rootDir.resolve("jni/build/jni/mingw/secp256k1-jni.dll")) { rename { "secp256k1-jni-mingw-x86_64.dll" } }
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native"))
}
(tasks["processResources"] as ProcessResources).apply {
dependsOn("copyJni")
from(buildDir.resolve("jniResources"))
}
java.withSourcesJar()
publishing {
publications {
create<MavenPublication>("jvm") {
artifactId = "secp256k1-jni-jvm"
artifactId = "secp256k1-jni-jvm-extract"
from(components["java"])
}
}
}
afterEvaluate {
tasks["clean"].doLast {
delete("$buildDir/build/cmake")
}
}

View File

@@ -14,7 +14,6 @@ JNI_HEADERS=$TARGET
if [ "$TARGET" == "linux" ]; then
OUTFILE=libsecp256k1-jni.so
[ "$CROSS" == "1" ] && sudo apt -y install libgmp-dev
ADD_LIB=-lgmp
CC_OPTS="-fPIC"
elif [ "$TARGET" == "darwin" ]; then
@@ -22,14 +21,12 @@ elif [ "$TARGET" == "darwin" ]; then
ADD_LIB=-lgmp
elif [ "$TARGET" == "mingw" ]; then
OUTFILE=secp256k1-jni.dll
CC=/usr/src/mxe/usr/bin/x86_64-w64-mingw32.static-gcc
JNI_HEADERS=linux
CC_OPTS="-fPIC"
CC=x86_64-w64-mingw32-gcc
fi
mkdir -p build/jni/$TARGET
$CC -shared $CC_OPTS -o build/jni/$TARGET/$OUTFILE c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c -Ic/headers/ -Ic/headers/java -Ic/headers/$JNI_HEADERS/ -I../native/secp256k1/ -lsecp256k1 -L../native/build/$TARGET/ $ADD_LIB
$CC -shared $CC_OPTS -o build/$TARGET/$OUTFILE ../c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c -I../c/headers/ -I../c/headers/java -I../c/headers/$JNI_HEADERS/ -I../../native/secp256k1/ -lsecp256k1 -L../../native/build/$TARGET/ $ADD_LIB
[[ ! -z "$TO_UID" ]] && chown -R $TO_UID:$TO_UID .

View File

@@ -0,0 +1,33 @@
plugins {
kotlin("jvm")
`maven-publish`
}
dependencies {
implementation(project(":jni:jvm"))
}
val copyJni by tasks.creating(Sync::class) {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
dependsOn(":jni:jvm:buildNativeHost")
from(rootDir.resolve("jni/jvm/build/darwin/libsecp256k1-jni.dylib"))
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin-x86_64"))
}
(tasks["processResources"] as ProcessResources).apply {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
dependsOn(copyJni)
from(buildDir.resolve("jniResources"))
}
publishing {
publications {
val pub = create<MavenPublication>("jvm") {
artifactId = "secp256k1-jni-jvm-darwin"
from(components["java"])
}
if (!org.gradle.internal.os.OperatingSystem.current().isMacOsX) {
tasks.withType<AbstractPublishToMaven>().all { onlyIf { publication != pub } }
}
}
}

View File

@@ -0,0 +1,33 @@
plugins {
kotlin("jvm")
`maven-publish`
}
dependencies {
implementation(project(":jni:jvm"))
}
val copyJni by tasks.creating(Sync::class) {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isLinux }
dependsOn(":jni:jvm:buildNativeHost")
from(rootDir.resolve("jni/jvm/build/linux/libsecp256k1-jni.so"))
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/linux-x86_64"))
}
(tasks["processResources"] as ProcessResources).apply {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isLinux }
dependsOn(copyJni)
from(buildDir.resolve("jniResources"))
}
publishing {
publications {
val pub = create<MavenPublication>("jvm") {
artifactId = "secp256k1-jni-jvm-linux"
from(components["java"])
}
if (!org.gradle.internal.os.OperatingSystem.current().isLinux) {
tasks.withType<AbstractPublishToMaven>().all { onlyIf { publication != pub } }
}
}
}

View File

@@ -0,0 +1,33 @@
plugins {
kotlin("jvm")
`maven-publish`
}
dependencies {
implementation(project(":jni:jvm"))
}
val copyJni by tasks.creating(Sync::class) {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isWindows }
dependsOn(":jni:jvm:buildNativeHost")
from(rootDir.resolve("jni/jvm/build/mingw/secp256k1-jni.dll"))
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/mingw-x86_64"))
}
(tasks["processResources"] as ProcessResources).apply {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isWindows }
dependsOn(copyJni)
from(buildDir.resolve("jniResources"))
}
publishing {
publications {
val pub = create<MavenPublication>("jvm") {
artifactId = "secp256k1-jni-jvm-mingw"
from(components["java"])
}
if (!org.gradle.internal.os.OperatingSystem.current().isWindows) {
tasks.withType<AbstractPublishToMaven>().all { onlyIf { publication != pub } }
}
}
}

View File

@@ -187,7 +187,7 @@ public object NativeSecp256k1Loader {
// Try loading library from fr.acinq.secp256k1.lib.path library path */
val libraryPath = System.getProperty("fr.acinq.secp256k1.lib.path")
val libraryName = System.getProperty("fr.acinq.secp256k1.lib.name") ?: System.mapLibraryName("secp256k1-jni-${OSInfo.nativeSuffix}")
val libraryName = System.getProperty("fr.acinq.secp256k1.lib.name") ?: System.mapLibraryName("secp256k1-jni")
if (libraryPath != null) {
if (loadNativeLibrary(libraryPath, libraryName)) {
extracted = true
@@ -197,7 +197,7 @@ public object NativeSecp256k1Loader {
// Load the os-dependent library from the jar file
val packagePath = NativeSecp256k1Loader::class.java.getPackage().name.replace("\\.".toRegex(), "/")
val embeddedLibraryPath = "/$packagePath/native"
val embeddedLibraryPath = "/$packagePath/native/${OSInfo.nativeSuffix}"
val hasNativeLib = NativeSecp256k1Loader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null
if (!hasNativeLib) {
error("No native library found: at $embeddedLibraryPath/$libraryName")