Skip to content

Commit

Permalink
Break up build script into convention plugins
Browse files Browse the repository at this point in the history
The library will be split up into modules, so this makes parts of the
build configuration easy to reuse.
  • Loading branch information
BenWoodworth committed Sep 25, 2024
1 parent 8c3c570 commit a0a7a0a
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 151 deletions.
156 changes: 5 additions & 151 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,96 +14,22 @@
* limitations under the License.
*/

import kotlinx.validation.ExperimentalBCVApi
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
import java.net.URL

plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.dokka)
alias(libs.plugins.binary.compatibility.validator)
id("parameterize.library-conventions")
id("kotlin-multiplatform-conventions")
id("dokka-conventions")
id("binary-compatibility-validator-conventions")
id("publishing-conventions")
id("ci-conventions")
}

repositories {
mavenCentral()
}

apiValidation {
@OptIn(ExperimentalBCVApi::class)
klib {
enabled = true
}
}

@OptIn(ExperimentalWasmDsl::class)
kotlin {
explicitApi()

jvm {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget = JvmTarget.JVM_1_8
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
js {
browser()
nodejs()
}

linuxX64()
linuxArm64()
androidNativeArm32()
androidNativeArm64()
androidNativeX86()
androidNativeX64()
macosX64()
macosArm64()
iosSimulatorArm64()
iosX64()
watchosSimulatorArm64()
watchosX64()
watchosArm32()
watchosArm64()
tvosSimulatorArm64()
tvosX64()
tvosArm64()
iosArm64()
watchosDeviceArm64()
mingwX64()

wasmJs {
browser()
nodejs()
}
wasmWasi {
nodejs()
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
}

sourceSets {
all {
languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
}

val commonMain by getting
val commonTest by getting {
dependencies {
implementation(libs.kotlin.test)
}
}
val jvmMain by getting {
dependencies {
implementation(libs.opentest4j)
Expand All @@ -112,79 +38,7 @@ kotlin {
}
}

val ciHostTargets = run {
val hostTargetPrefixes = mapOf(
"linux" to listOf("metadata", "jvm", "js", "linux", "android", "wasm"),
"macos" to listOf("macos", "ios", "watchos", "tvos"),
"windows" to listOf("mingw")
)

val hostTargets = hostTargetPrefixes.mapValues { (_, prefixes) ->
kotlin.targets.filter { target ->
prefixes.any { target.name.startsWith(it) }
}
}

val envCiHost = System.getenv("CI_HOST")
val osName = System.getProperty("os.name")
val host = when {
envCiHost != null -> envCiHost.also {
require(envCiHost in hostTargets) { "Invalid CI_HOST: $envCiHost. Must be one of: ${hostTargets.keys}" }
}

osName == "Linux" -> "linux"
osName == "Mac OS X" -> "macos"
osName.startsWith("Windows") -> "windows"
else -> error("Unable to determine CI Host for OS: $osName. CI_HOST env can be set instead.")
}

// Check for non-existent, unaccounted for, or double-counted targets
val allTargets = kotlin.targets.map { it.name }.sorted()
val groupedTargets = hostTargets.values.flatten().map { it.name }.sorted()
check(groupedTargets == allTargets) {
"Bad host target grouping.\n\tExpected: $allTargets\n\tActual: $groupedTargets"
}

hostTargets[host]!!.asSequence()
}

tasks.create("ciTest") {
ciHostTargets
.filterIsInstance<KotlinTargetWithTests<*, *>>()
.map { target -> "${target.name}Test" }
.forEach { targetTest ->
dependsOn(targetTest)
}
}

tasks.create("ciPublish") {
ciHostTargets
.map { it.name.uppercaseFirstChar() }
.map { if (it == "Metadata") "KotlinMultiplatform" else it }
.map { target -> "publish${target}PublicationToMavenRepository" }
.forEach { publishTarget ->
dependsOn(publishTarget)
}
}

tasks.withType<DokkaTask>().configureEach {
dokkaSourceSets.configureEach {
reportUndocumented = true
failOnWarning = true

val releaseVersionRef = version.toString()
.takeIf { version -> version.matches(Regex("""\d+\.\d+\.\d+""")) }
?.let { version -> "v$version" }

if (releaseVersionRef != null) {
sourceLink {
localDirectory = projectDir.resolve("src")
remoteUrl.set(URL("https://github.com/BenWoodworth/Parameterize/tree/$releaseVersionRef/src"))
remoteLineSuffix = "#L"
}
}
}

doLast {
layout.buildDirectory.asFileTree.asSequence()
.filter { it.isFile && it.extension == "html" }
Expand Down
10 changes: 10 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@ plugins {
repositories {
gradlePluginPortal()
}

dependencies {
// https://github.com/gradle/gradle/issues/17963#issuecomment-1600751553
fun plugin(provider: Provider<PluginDependency>): Provider<String> =
provider.map { "${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}" }

implementation(plugin(libs.plugins.kotlin.multiplatform))
implementation(plugin(libs.plugins.dokka))
implementation(plugin(libs.plugins.binary.compatibility.validator))
}
24 changes: 24 additions & 0 deletions buildSrc/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2024 Ben Woodworth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2024 Ben Woodworth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import kotlinx.validation.ExperimentalBCVApi

plugins {
id("org.jetbrains.kotlinx.binary-compatibility-validator")
}

apiValidation {
@OptIn(ExperimentalBCVApi::class)
klib {
enabled = true
}
}
76 changes: 76 additions & 0 deletions buildSrc/src/main/kotlin/ci-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2024 Ben Woodworth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests

plugins {
kotlin("multiplatform")
}

val ciHostTargets = run {
val hostTargetPrefixes = mapOf(
"linux" to listOf("metadata", "jvm", "js", "linux", "android", "wasm"),
"macos" to listOf("macos", "ios", "watchos", "tvos"),
"windows" to listOf("mingw")
)

val hostTargets = hostTargetPrefixes.mapValues { (_, prefixes) ->
kotlin.targets.filter { target ->
prefixes.any { target.name.startsWith(it) }
}
}

val envCiHost = System.getenv("CI_HOST")
val osName = System.getProperty("os.name")
val host = when {
envCiHost != null -> envCiHost.also {
require(envCiHost in hostTargets) { "Invalid CI_HOST: $envCiHost. Must be one of: ${hostTargets.keys}" }
}

osName == "Linux" -> "linux"
osName == "Mac OS X" -> "macos"
osName.startsWith("Windows") -> "windows"
else -> error("Unable to determine CI Host for OS: $osName. CI_HOST env can be set instead.")
}

// Check for non-existent, unaccounted for, or double-counted targets
val allTargets = kotlin.targets.map { it.name }.sorted()
val groupedTargets = hostTargets.values.flatten().map { it.name }.sorted()
check(groupedTargets == allTargets) {
"Bad host target grouping.\n\tExpected: $allTargets\n\tActual: $groupedTargets"
}

hostTargets[host]!!.asSequence()
}

tasks.create("ciTest") {
ciHostTargets
.filterIsInstance<KotlinTargetWithTests<*, *>>()
.map { target -> "${target.name}Test" }
.forEach { targetTest ->
dependsOn(targetTest)
}
}

tasks.create("ciPublish") {
ciHostTargets
.map { it.name.replaceFirstChar(Char::uppercase) }
.map { if (it == "Metadata") "KotlinMultiplatform" else it }
.map { target -> "publish${target}PublicationToMavenRepository" }
.forEach { publishTarget ->
dependsOn(publishTarget)
}
}
41 changes: 41 additions & 0 deletions buildSrc/src/main/kotlin/dokka-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2024 Ben Woodworth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.jetbrains.dokka.gradle.DokkaTask
import java.net.URL

plugins {
id("org.jetbrains.dokka")
}

tasks.withType<DokkaTask>().configureEach {
dokkaSourceSets.configureEach {
reportUndocumented = true
failOnWarning = true

val releaseVersionRef = version.toString()
.takeIf { version -> version.matches(Regex("""\d+\.\d+\.\d+""")) }
?.let { version -> "v$version" }

if (releaseVersionRef != null) {
sourceLink {
localDirectory = rootDir
remoteUrl = URL("https://github.com/BenWoodworth/Parameterize/tree/$releaseVersionRef")
remoteLineSuffix = "#L"
}
}
}
}
Loading

0 comments on commit a0a7a0a

Please sign in to comment.