diff --git a/Jenkinsfile b/Jenkinsfile index e39f7d87b0..bc9bd22167 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -366,11 +366,11 @@ def genAndStashSwigJNI() { stash includes: 'packages/jni-swig-stub/build/generated/sources/jni/realmc.cpp,packages/jni-swig-stub/build/generated/sources/jni/realmc.h', name: 'swig_jni' } def runBuild() { - def buildJvmAbiFlag = "-PcopyJvmABIs=false" + def buildJvmAbiFlag = "-Prealm.kotlin.copyNativeJvmLibs=false" if (shouldBuildJvmABIs()) { unstash name: 'linux_so_file' unstash name: 'win_dll' - buildJvmAbiFlag = "-PcopyJvmABIs=true" + buildJvmAbiFlag = "-Prealm.kotlin.copyNativeJvmLibs=true" } withCredentials([ @@ -385,7 +385,7 @@ def runBuild() { } sh """ cd packages - chmod +x gradlew && ./gradlew publishAllPublicationsToTestRepository ${buildJvmAbiFlag} ${signingFlags} --info --stacktrace --no-daemon + chmod +x gradlew && ./gradlew publishCIPackages ${buildJvmAbiFlag} ${signingFlags} --info --stacktrace --no-daemon """ } } @@ -724,19 +724,21 @@ def build_jvm_linux(String buildType) { unstash name: 'swig_jni' docker.build('jvm_linux', '-f packages/cinterop/src/jvmMain/generic.Dockerfile .').inside { sh """ - cd packages/cinterop/src/jvmMain/ - rm -rf linux-build-dir - mkdir linux-build-dir - cd linux-build-dir + cd packages/cinterop + mkdir build + cd build + rm -rf realmLinuxBuild + mkdir realmLinuxBuild + cd realmLinuxBuild cmake -DCMAKE_BUILD_TYPE=${buildType} \ -DREALM_ENABLE_SYNC=1 \ -DREALM_NO_TESTS=1 \ -DREALM_BUILD_LIB_ONLY=true \ - ../../jvm + ../../src/jvm make -j8 """ - stash includes:'packages/cinterop/src/jvmMain/linux-build-dir/librealmc.so', name: 'linux_so_file' + stash includes:'packages/cinterop/build/realmLinuxBuild/librealmc.so', name: 'linux_so_file' } } @@ -757,9 +759,9 @@ def build_jvm_windows(String buildType) { def cmakeDefinitions = cmakeOptions.collect { k,v -> "-D$k=$v" }.join(' ') dir('packages') { - bat "cd cinterop\\src\\jvmMain && rmdir /s /q windows-build-dir & mkdir windows-build-dir && cd windows-build-dir && \"${tool 'cmake'}\" ${cmakeDefinitions} ..\\..\\jvm && \"${tool 'cmake'}\" --build . --config Release" + bat "cd cinterop && rmdir /s /q build & mkdir build & cd build && rmdir /s /q realmWindowsBuild & mkdir realmWindowsBuild && cd realmWindowsBuild && \"${tool 'cmake'}\" ${cmakeDefinitions} ..\\..\\src\\jvm && \"${tool 'cmake'}\" --build . --config Release" } - stash includes: 'packages/cinterop/src/jvmMain/windows-build-dir/Release/realmc.dll', name: 'win_dll' + stash includes: 'packages/cinterop/build/realmWindowsBuild/Release/realmc.dll', name: 'win_dll' } def trackBuildMetrics(version) { diff --git a/packages/build.gradle.kts b/packages/build.gradle.kts index 296bf17b24..fcc0536eda 100644 --- a/packages/build.gradle.kts +++ b/packages/build.gradle.kts @@ -33,6 +33,136 @@ allprojects { } } +/** + * Task that will build and publish the defined packages to /packages/build/m2-buildrepo`. + * This is mostly suited for CI jobs that wants to build select publications on specific runners. + * + * + * See `gradle.properties` for specific configuration options available to this task. + * + * For local development, using: + * + * ``` + * > ./gradlew publishAllPublicationsToTestRepository + * ``` + * + * will build and publish all targets available to the builder platform. + */ +tasks.register("publishCIPackages") { + group = "Publishing" + description = "Publish packages that has been configured for this CI node. See `gradle.properties`." + + // Figure out which targets are configured. This will impact which sub modules will be published + val availableTargets = setOf( + "iosArm64", + "iosX64", + "jvm", + "macosX64", + "macosArm64", + "android", + "metadata", + "compilerPlugin", + "gradlePlugin" + ) + + val mainHostTarget: Set = setOf("metadata") // "kotlinMultiplatform" + + val isMainHost: Boolean = project.properties["realm.kotlin.mainHost"]?.let { it == "true" } ?: false + + // Find user configured platforms (if any) + val userTargets: Set? = (project.properties["realm.kotlin.targets"] as String?) + ?.split(",") + ?.map { it.trim() } + ?.filter { it.isNotEmpty() } + ?.toSet() + + userTargets?.forEach { + if (!availableTargets.contains(it)) { + project.logger.error("Unknown publication: $it") + throw IllegalArgumentException("Unknown publication: $it") + } + } + + // Configure which platforms publications we do want to publish + val publicationTargets = (userTargets ?: availableTargets).let { + when (isMainHost) { + true -> it + mainHostTarget + false -> it - mainHostTarget + } + } + + publicationTargets.forEach { target: String -> + when(target) { + "iosArm64" -> { + dependsOn( + ":cinterop:publishIosArm64PublicationToTestRepository", + ":cinterop:publishIosSimulatorArm64PublicationToTestRepository", + ":library-base:publishIosArm64PublicationToTestRepository", + ":library-base:publishIosSimulatorArm64PublicationToTestRepository", + ":library-sync:publishIosArm64PublicationToTestRepository", + ":library-sync:publishIosSimulatorArm64PublicationToTestRepository", + ) + } + "iosX64" -> { + dependsOn( + ":cinterop:publishIosX64PublicationToTestRepository", + ":library-base:publishIosX64PublicationToTestRepository", + ":library-sync:publishIosX64PublicationToTestRepository", + ) + } + "jvm" -> { + dependsOn( + ":jni-swig-stub:publishAllPublicationsToTestRepository", + ":cinterop:publishJvmPublicationToTestRepository", + ":library-base:publishJvmPublicationToTestRepository", + ":library-sync:publishJvmPublicationToTestRepository", + ) + } + "macosX64" -> { + dependsOn( + ":cinterop:publishMacosX64PublicationToTestRepository", + ":library-base:publishMacosX64PublicationToTestRepository", + ":library-sync:publishMacosX64PublicationToTestRepository", + ) + } + "macosArm64" -> { + dependsOn( + ":cinterop:publishMacosArm64PublicationToTestRepository", + ":library-base:publishMacosArm64PublicationToTestRepository", + ":library-sync:publishMacosArm64PublicationToTestRepository", + ) + } + "android" -> { + dependsOn( + ":jni-swig-stub:publishAllPublicationsToTestRepository", + ":cinterop:publishAndroidReleasePublicationToTestRepository", + ":library-base:publishAndroidReleasePublicationToTestRepository", + ":library-sync:publishAndroidReleasePublicationToTestRepository", + ) + } + "metadata" -> { + dependsOn( + ":cinterop:publishKotlinMultiplatformPublicationToTestRepository", + ":library-base:publishKotlinMultiplatformPublicationToTestRepository", + ":library-sync:publishKotlinMultiplatformPublicationToTestRepository", + ) + } + "compilerPlugin" -> { + dependsOn( + ":plugin-compiler:publishAllPublicationsToTestRepository", + ":plugin-compiler-shaded:publishAllPublicationsToTestRepository" + ) + } + "gradlePlugin" -> { + dependsOn(":gradle-plugin:publishAllPublicationsToTestRepository") + } + else -> { + throw IllegalArgumentException("Unsupported target: $target") + } + } + } +} + tasks.register("uploadDokka") { dependsOn("dokkaHtmlMultiModule") group = "Release" diff --git a/packages/cinterop/build.gradle.kts b/packages/cinterop/build.gradle.kts index 93cdb46e9e..429fcf8619 100644 --- a/packages/cinterop/build.gradle.kts +++ b/packages/cinterop/build.gradle.kts @@ -60,6 +60,17 @@ val buildType: BuildType = if ((System.getenv("CONFIGURATION") ?: "RELEASE").equ BuildType.DEBUG } + +fun checkIfBuildingNativeLibs(task: Task, action: Task.() -> Unit) { + // Whether or not to build the underlying native Realm Libs. Generally these are only + // needed at runtime and thus can be ignored when only building the layers on top + if (project.extra.properties["realm.kotlin.buildRealmCore"] == "true") { + action(task) + } else { + logger.warn("Ignore building native libs") + } +} + val corePath = "external/core" val absoluteCorePath = "$rootDir/$corePath" val jvmJniPath = "src/jvmMain/resources/jni" @@ -115,6 +126,8 @@ val nativeLibraryIncludesIosSimulatorArm64Release = kotlin { jvm() android("android") { + // Changing this will also requires an update to the publishCIPackages task + // in /packages/build.gradle.kts publishLibraryVariants("release") } // Cinterops seems sharable across architectures (x86_64/arm) with option of differentiation in @@ -275,25 +288,6 @@ kotlin { } } } - - // See https://kotlinlang.org/docs/reference/mpp-publish-lib.html#publish-a-multiplatform-library - // FIXME MPP-BUILD We need to revisit this when we enable building on multiple hosts. Right now it doesn't do the right thing. - /*** - * Uncommenting below will cause the aritifact to not be published for cinterop-jvm coordinate: - * > Task :cinterop:publishJvmPublicationToMavenLocal SKIPPED - Task :cinterop:publishJvmPublicationToMavenLocal in cinterop Starting - Skipping task ':cinterop:publishJvmPublicationToMavenLocal' as task onlyIf is false. - Task :cinterop:publishJvmPublicationToMavenLocal in cinterop Finished - :cinterop:publishJvmPublicationToMavenLocal (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs. - */ -// configure(listOf(targets["metadata"], jvm())) { -// mavenPublication { -// val targetPublication = this@mavenPublication -// tasks.withType() -// .matching { it.publication == targetPublication } -// .all { onlyIf { findProperty("isMainHost") == "true" } } -// } -// } } android { @@ -353,28 +347,51 @@ if (HOST_OS.isMacOs()) { val capiMacosUniversal by tasks.registering { build_C_API_Macos_Universal(buildVariant = buildType) } + // Building Simulator binaries for iosX64 (x86_64) and iosSimulatorArm64 (i.e Apple silicon arm64) - val capiSimulator by tasks.registering { + val capiSimulatorX64 by tasks.registering { build_C_API_Simulator("x86_64", buildType) + } + + // Building Simulator binaries for iosSimulatorArm64 (i.e Apple silicon arm64) + val capiSimulatorArm64 by tasks.registering { build_C_API_Simulator("arm64", buildType) } + // Building for ios device (arm64 only) val capiIosArm64 by tasks.registering { build_C_API_iOS_Arm64(buildType) } tasks.named("cinteropRealm_wrapperIosArm64") { - dependsOn(capiIosArm64) + checkIfBuildingNativeLibs(this) { + dependsOn(capiIosArm64) + } + } + + tasks.named("cinteropRealm_wrapperIosX64") { + checkIfBuildingNativeLibs(this) { + dependsOn(capiSimulatorX64) + } + } + tasks.named("cinteropRealm_wrapperIosSimulatorArm64") { - dependsOn(capiSimulator) + checkIfBuildingNativeLibs(this) { + dependsOn(capiSimulatorArm64) + } } tasks.named("cinteropRealm_wrapperMacosX64") { - dependsOn(capiMacosUniversal) + checkIfBuildingNativeLibs(this) { + dependsOn(capiMacosUniversal) + } } + tasks.named("cinteropRealm_wrapperMacosArm64") { - dependsOn(capiMacosUniversal) + checkIfBuildingNativeLibs(this) { + dependsOn(capiMacosUniversal) + } } } @@ -382,30 +399,40 @@ val buildJVMSharedLibs: TaskProvider by tasks.registering { if (HOST_OS.isMacOs()) { buildSharedLibrariesForJVMMacOs() } else if (HOST_OS.isWindows()) { - buildSharedLibrariesForJVMWindows() + buildSharedLibrariesForJVMWindows() } else { throw IllegalStateException("Building JVM libraries on this platform is not supported: $HOST_OS") } +} - // Only on CI for Snapshots and Releases which will run on MacOS. +/** + * Task responsible for copying native files for all architectures into the correct location, + * making the library ready for distribution. + * + * The task assumes that some other task already pre-built the binaries, making this task + * mostly useful on CI. + */ +val copyJVMSharedLibs: TaskProvider by tasks.registering { val copyJvmABIs = project.hasProperty("copyJvmABIs") && project.property("copyJvmABIs") == "true" if (copyJvmABIs) { - if (!HOST_OS.isMacOs()) { - throw IllegalStateException("Creating a full JVM bundle with all architectures is " + - "currently only supported on MacOs. This was: $HOST_OS") - } + // copy MacOS pre-built binaries + project.file("$buildDir/realmMacOsBuild/librealmc.dylib") + .copyTo(project.file("$jvmJniPath/macos/librealmc.dylib"), overwrite = true) + genHashFile(platform = "macos", prefix = "lib", suffix = ".dylib") // copy Linux pre-built binaries - project.file("src/jvmMain/linux-build-dir/librealmc.so") + project.file("$buildDir/realmLinuxBuild/librealmc.so") .copyTo(project.file("$jvmJniPath/linux/librealmc.so"), overwrite = true) genHashFile(platform = "linux", prefix = "lib", suffix = ".so") // copy Window pre-built binaries - project.file("src/jvmMain/windows-build-dir/Release/realmc.dll") + project.file("$buildDir/realmWindowsBuild/Release/realmc.dll") .copyTo(project.file("$jvmJniPath/windows/realmc.dll"), overwrite = true) genHashFile(platform = "windows", prefix = "", suffix = ".dll") // Register copied libraries as output + outputs.file(project.file("$jvmJniPath/macos/librealmc.dylib")) + outputs.file(project.file("$jvmJniPath/macos/dynamic_libraries.properties")) outputs.file(project.file("$jvmJniPath/linux/librealmc.so")) outputs.file(project.file("$jvmJniPath/linux/dynamic_libraries.properties")) outputs.file(project.file("$jvmJniPath/windows/realmc.dll")) @@ -433,7 +460,7 @@ fun getSharedCMakeFlags(buildType: BuildType, ccache: Boolean = true): Array + +# Whether or not to build Realm Core on the platform running the build. +# This can be used to prevent certain targets transentively starting a +# native build, which can easily take 30+ minutes. This is e.g. relevant +# when only building docs or compiler/gradle plugins. +realm.kotlin.buildRealmCore=true + +# Whether or not to copy pre-built JVM native files into place, making them +# ready to run or package the final JVM JARs. +# +# The prebuilt files must be placed in this location for this to work: +# - MacOS: /packages/cinterop/build/realmMacOsBuild/librealmc.dylib +# - Linux: /packages/cinterop/build/realmWindowsBuild/librealmc.so +# - Windows: /packages/cinterop/build/realmWindowsBuild/Release/realmc.dll +realm.kotlin.copyNativeJvmLibs=false + # See https://kotlinlang.org/docs/mpp-publish-lib.html#publish-an-android-library # Allow the default dependency name to match the client debug build type. Otherwise the client project has to # explicitly specify the debug variant (ex: implementation("io.realm.kotlin:library-base-debug:0.8.0")) diff --git a/packages/library-base/build.gradle.kts b/packages/library-base/build.gradle.kts index 1a75c1b893..c2edd01dff 100644 --- a/packages/library-base/build.gradle.kts +++ b/packages/library-base/build.gradle.kts @@ -36,7 +36,9 @@ project.extensions.configure(kotlinx.atomicfu.plugin.gradle.AtomicFUPluginExtens // Common Kotlin configuration kotlin { jvm() - android("android") { + androidTarget("android") { + // Changing this will also requires an update to the publishCIPackages task + // in /packages/build.gradle.kts publishLibraryVariants("release") } ios() @@ -225,6 +227,8 @@ val javadocJar by tasks.registering(Jar::class) { archiveClassifier.set("javadoc") } +// Make sure that docs are published for the Metadata publication as well. This is required +// by Maven Central publishing { // See https://dev.to/kotlin/how-to-build-and-publish-a-kotlin-multiplatform-library-going-public-4a8k publications.withType { diff --git a/packages/library-sync/build.gradle.kts b/packages/library-sync/build.gradle.kts index d2f3afe90e..3860ab158a 100644 --- a/packages/library-sync/build.gradle.kts +++ b/packages/library-sync/build.gradle.kts @@ -35,7 +35,9 @@ project.extensions.configure(kotlinx.atomicfu.plugin.gradle.AtomicFUPluginExtens // Common Kotlin configuration kotlin { jvm() - android("android") { + androidTarget("android") { + // Changing this will also requires an update to the publishCIPackages task + // in /packages/build.gradle.kts publishLibraryVariants("release") } iosX64() @@ -110,17 +112,6 @@ kotlin { // visibility modifier and will be stripped from Dokka, but will unfortunately still // leak into auto-complete in the IDE. explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Strict - - // See https://kotlinlang.org/docs/reference/mpp-publish-lib.html#publish-a-multiplatform-library - // FIXME MPP-BUILD We need to revisit this when we enable building on multiple hosts. Right now it doesn't do the right thing. -// configure(listOf(targets["metadata"], jvm())) { -// mavenPublication { -// val targetPublication = this@mavenPublication -// tasks.withType() -// .matching { it.publication == targetPublication } -// .all { onlyIf { findProperty("isMainHost") == "true" } } -// } -// } } // Using a custom name module for internal methods to avoid default name mangling in Kotlin compiler which uses the module @@ -205,6 +196,8 @@ val javadocJar by tasks.registering(Jar::class) { archiveClassifier.set("javadoc") } +// Make sure that docs are published for the Metadata publication as well. This is required +// by Maven Central publishing { // See https://dev.to/kotlin/how-to-build-and-publish-a-kotlin-multiplatform-library-going-public-4a8k publications.withType { @@ -214,7 +207,7 @@ publishing { // TODO: configure DOKKA so that it's only published for sync and not base val common = publications.getByName("kotlinMultiplatform") as MavenPublication -// // Configuration through examples/kmm-sample does not work if we do not resolve the tasks -// // completely, hence the .get() below. + // Configuration through examples/kmm-sample does not work if we do not resolve the tasks + // completely, hence the .get() below. common.artifact(tasks.named("dokkaJar").get()) }