diff --git a/README.md b/README.md index ed1b18b..c12affe 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,38 @@ You can also specify the temporary directory where the library will be extracted ## Usage Please have a look at unit tests, more samples will be added soon. + +## Building + +-**secp256k1-kmp** is a [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html) wrapper for Bitcoin Core's [secp256k1 library](https://github.com/bitcoin-core/secp256k1). +- +To build the library you need the following: +- Window 64 bits, Linux 64 bits, or MacOs 64 Bits +- OpenJDK11 (we recommend using packages provided by https://adoptopenjdk.net/ but there are other options) +- (optional) Android SDK + +It may work with other Operating Systems and JDKs, but then you're on your own (in particular we don't plan to support 32 bits Operating Systems). +To build the library and publish compiled artefacts locally (so they can be used by other projects): + +```sh +./gradlew :build +./gradlew :publishToMavenLocal +``` + +To run all tests on all platforms: + +```sh +./gradlew allTests +``` + +To run tests on a single platform, for example the JVM: + +```sh +./gradlew jvmTest +``` + +If you want to skip building Android artefacts create a `local.properties` file in the project's root directory and add the following line: + +``` +skip.android=true +``` \ No newline at end of file diff --git a/native/build.gradle.kts b/native/build.gradle.kts index d7880b6..cba74e3 100644 --- a/native/build.gradle.kts +++ b/native/build.gradle.kts @@ -1,6 +1,10 @@ import org.gradle.internal.os.OperatingSystem -evaluationDependsOn(":jni:android") +val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true + +if (includeAndroid) { + evaluationDependsOn(":jni:android") +} val currentOs = OperatingSystem.current() val bash = if (currentOs.isWindows) "bash.exe" else "bash" @@ -39,34 +43,39 @@ val buildSecp256k1Ios by tasks.creating(Exec::class) { commandLine(bash, "build-ios.sh") } -val buildSecp256k1Android by tasks.creating { - group = "build" - buildSecp256k1.dependsOn(this) -} -fun creatingBuildSecp256k1Android(arch: String) = tasks.creating(Exec::class) { - group = "build" - buildSecp256k1Android.dependsOn(this) +if (includeAndroid) { - inputs.files(projectDir.resolve("build-android.sh")) - outputs.dir(projectDir.resolve("build/android/$arch")) - - workingDir = projectDir - - val toolchain = when { - currentOs.isLinux -> "linux-x86_64" - currentOs.isMacOsX -> "darwin-x86_64" - currentOs.isWindows -> "windows-x86_64" - else -> error("No Android toolchain defined for this OS: $currentOs") + val buildSecp256k1Android by tasks.creating { + group = "build" + buildSecp256k1.dependsOn(this) } - environment("TOOLCHAIN", toolchain) - environment("ARCH", arch) - environment("ANDROID_NDK", (project(":jni:android").extensions["android"] as com.android.build.gradle.LibraryExtension).ndkDirectory) - commandLine(bash, "build-android.sh") + + fun creatingBuildSecp256k1Android(arch: String) = tasks.creating(Exec::class) { + group = "build" + buildSecp256k1Android.dependsOn(this) + + inputs.files(projectDir.resolve("build-android.sh")) + outputs.dir(projectDir.resolve("build/android/$arch")) + + workingDir = projectDir + + val toolchain = when { + currentOs.isLinux -> "linux-x86_64" + currentOs.isMacOsX -> "darwin-x86_64" + currentOs.isWindows -> "windows-x86_64" + else -> error("No Android toolchain defined for this OS: $currentOs") + } + environment("TOOLCHAIN", toolchain) + environment("ARCH", arch) + environment("ANDROID_NDK", (project(":jni:android").extensions["android"] as com.android.build.gradle.LibraryExtension).ndkDirectory) + commandLine(bash, "build-android.sh") + } + + val buildSecp256k1AndroidX86_64 by creatingBuildSecp256k1Android("x86_64") + val buildSecp256k1AndroidX86 by creatingBuildSecp256k1Android("x86") + val buildSecp256k1AndroidArm64v8a by creatingBuildSecp256k1Android("arm64-v8a") + val buildSecp256k1AndroidArmeabiv7a by creatingBuildSecp256k1Android("armeabi-v7a") } -val buildSecp256k1AndroidX86_64 by creatingBuildSecp256k1Android("x86_64") -val buildSecp256k1AndroidX86 by creatingBuildSecp256k1Android("x86") -val buildSecp256k1AndroidArm64v8a by creatingBuildSecp256k1Android("arm64-v8a") -val buildSecp256k1AndroidArmeabiv7a by creatingBuildSecp256k1Android("armeabi-v7a") val clean by tasks.creating { group = "build" diff --git a/settings.gradle.kts b/settings.gradle.kts index b71d09c..a375d68 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,10 +7,19 @@ pluginManagement { } rootProject.name = "secp256k1-kmp" +// We use a property defined in `local.properties` to know whether we should build the android application or not. +// For example, iOS developers may want to skip that most of the time. +val skipAndroid = File("$rootDir/local.properties").takeIf { it.exists() } + ?.inputStream()?.use { java.util.Properties().apply { load(it) } } + ?.run { getProperty("skip.android", "false")?.toBoolean() } + ?: false + +// Use system properties to inject the property in other gradle build files. +System.setProperty("includeAndroid", (!skipAndroid).toString()) + include( ":native", ":jni", - ":jni:android", ":jni:jvm", ":jni:jvm:darwin", ":jni:jvm:linux", @@ -18,3 +27,10 @@ include( ":jni:jvm:all", ":tests" ) + +if (!skipAndroid) { + print("building android library") + include(":jni:android") +} else { + print("skipping android build") +} diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 67f9184..9f4b948 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -1,11 +1,16 @@ + plugins { kotlin("multiplatform") - id("com.android.library") + if (System.getProperty("includeAndroid")?.toBoolean() == true) { + id("com.android.library") + } } kotlin { explicitApi() + val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true + val commonMain by sourceSets.getting { dependencies { implementation(rootProject) @@ -29,17 +34,19 @@ kotlin { } } - android { - compilations.all { - kotlinOptions.jvmTarget = "1.8" - } - sourceSets["androidMain"].dependencies { - implementation(project(":jni:android")) - } - sourceSets["androidTest"].dependencies { - implementation(kotlin("test-junit")) - implementation("androidx.test.ext:junit:1.1.2") - implementation("androidx.test.espresso:espresso-core:3.3.0") + if (includeAndroid) { + android { + compilations.all { + kotlinOptions.jvmTarget = "1.8" + } + sourceSets["androidMain"].dependencies { + implementation(project(":jni:android")) + } + sourceSets["androidTest"].dependencies { + implementation(kotlin("test-junit")) + implementation("androidx.test.ext:junit:1.1.2") + implementation("androidx.test.espresso:espresso-core:3.3.0") + } } } @@ -48,23 +55,26 @@ kotlin { ios() } -android { - defaultConfig { - compileSdkVersion(30) - minSdkVersion(21) - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } +val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true +if (includeAndroid) { + extensions.configure("android") { + defaultConfig { + compileSdkVersion(30) + minSdkVersion(21) + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } - sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") - afterEvaluate { - tasks.withType().all { - enabled = false + afterEvaluate { + tasks.withType().all { + enabled = false + } } } -} +} \ No newline at end of file