-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbuild.gradle.kts
346 lines (312 loc) · 14 KB
/
build.gradle.kts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.*
import pl.mareklangiewicz.defaults.*
import pl.mareklangiewicz.deps.*
import pl.mareklangiewicz.utils.*
plugins {
plugAll(plugs.KotlinMulti, plugs.MavenPublish, plugs.Signing)
}
val settings = rootExtLibDetails.settings.copy(
withJs = false,
withNativeLinux64 = false,
)
val details = rootExtLibDetails.copy(settings = settings)
defaultBuildTemplateForBasicMppLib(details)
kotlin {
@Suppress("UNUSED_VARIABLE")
sourceSets {
val commonMain by getting {
dependencies {
api(project(":uspekx"))
}
}
val jvmMain by getting {
dependencies {
implementation(JUnit.junit)
}
}
}
}
// region [[Kotlin Module Build Template]]
// Kind of experimental/temporary.. not sure how it will evolve yet,
// but currently I need these kind of substitutions/locals often enough
// especially when updating kground <-> kommandline (trans deps issues)
fun Project.setMyWeirdSubstitutions(
vararg rules: Pair<String, String>,
myProjectsGroup: String = "pl.mareklangiewicz",
tryToUseLocalProjects: Boolean = true,
) {
val foundLocalProjects: Map<String, Project?> =
if (tryToUseLocalProjects) rules.associate { it.first to findProject(":${it.first}") }
else emptyMap()
configurations.all {
resolutionStrategy.dependencySubstitution {
for ((projName, projVer) in rules)
substitute(module("$myProjectsGroup:$projName"))
.using(
// Note: there are different fun in gradle: Project.project; DependencySubstitution.project
if (foundLocalProjects[projName] != null) project(":$projName")
else module("$myProjectsGroup:$projName:$projVer")
)
}
}
}
fun RepositoryHandler.addRepos(settings: LibReposSettings) = with(settings) {
@Suppress("DEPRECATION")
if (withMavenLocal) mavenLocal()
if (withMavenCentral) mavenCentral()
if (withGradle) gradlePluginPortal()
if (withGoogle) google()
if (withKotlinx) maven(repos.kotlinx)
if (withKotlinxHtml) maven(repos.kotlinxHtml)
if (withComposeJbDev) maven(repos.composeJbDev)
if (withKtorEap) maven(repos.ktorEap)
if (withJitpack) maven(repos.jitpack)
}
// TODO_maybe: doc says it could be now also applied globally instead for each task (and it works for andro too)
// But it's only for jvm+andro, so probably this is better:
// https://kotlinlang.org/docs/gradle-compiler-options.html#for-all-kotlin-compilation-tasks
fun TaskCollection<Task>.defaultKotlinCompileOptions(
apiVer: KotlinVersion = KotlinVersion.KOTLIN_2_1,
jvmTargetVer: String? = null, // it's better to use jvmToolchain (normally done in fun allDefault)
renderInternalDiagnosticNames: Boolean = false,
) = withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions {
apiVersion.set(apiVer)
jvmTargetVer?.let { jvmTarget = JvmTarget.fromTarget(it) }
if (renderInternalDiagnosticNames) freeCompilerArgs.add("-Xrender-internal-diagnostic-names")
// useful, for example, to suppress some errors when accessing internal code from some library, like:
// @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_PARAMETER_TYPE", "EXPOSED_PROPERTY_TYPE", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
}
}
fun TaskCollection<Task>.defaultTestsOptions(
printStandardStreams: Boolean = true,
printStackTraces: Boolean = true,
onJvmUseJUnitPlatform: Boolean = true,
) = withType<AbstractTestTask>().configureEach {
testLogging {
showStandardStreams = printStandardStreams
showStackTraces = printStackTraces
}
if (onJvmUseJUnitPlatform) (this as? Test)?.useJUnitPlatform()
}
// Provide artifacts information requited by Maven Central
fun MavenPublication.defaultPOM(lib: LibDetails) = pom {
name put lib.name
description put lib.description
url put lib.githubUrl
licenses {
license {
name put lib.licenceName
url put lib.licenceUrl
}
}
developers {
developer {
id put lib.authorId
name put lib.authorName
email put lib.authorEmail
}
}
scm { url put lib.githubUrl }
}
/** See also: root project template-full: addDefaultStuffFromSystemEnvs */
fun Project.defaultSigning(
keyId: String = rootExtString["signing.keyId"],
key: String = rootExtReadFileUtf8TryOrNull("signing.keyFile") ?: rootExtString["signing.key"],
password: String = rootExtString["signing.password"],
) = extensions.configure<SigningExtension> {
useInMemoryPgpKeys(keyId, key, password)
sign(extensions.getByType<PublishingExtension>().publications)
}
fun Project.defaultPublishing(
lib: LibDetails,
readmeFile: File = File(rootDir, "README.md"),
withSignErrorWorkaround: Boolean = true,
withPublishingPrintln: Boolean = false, // FIXME_later: enabling brakes gradle android publications
) {
val readmeJavadocJar by tasks.registering(Jar::class) {
from(readmeFile) // TODO_maybe: use dokka to create real docs? (but it's not even java..)
archiveClassifier put "javadoc"
}
extensions.configure<PublishingExtension> {
// We have at least two cases:
// 1. With plug.KotlinMulti it creates publications automatically (so no need to create here)
// 2. With plug.KotlinJvm it does not create publications (so we have to create it manually)
if (plugins.hasPlugin("org.jetbrains.kotlin.jvm")) {
publications.create<MavenPublication>("jvm") {
from(components["kotlin"])
}
}
publications.withType<MavenPublication> {
artifact(readmeJavadocJar)
// Adding javadoc artifact generates warnings like:
// Execution optimizations have been disabled for task ':uspek:signJvmPublication'
// (UPDATE: now it's errors - see workaround below)
// It looks like a bug in kotlin multiplatform plugin:
// https://youtrack.jetbrains.com/issue/KT-46466
// FIXME_someday: Watch the issue.
// If it's a bug in kotlin multiplatform then remove this comment when it's fixed.
// Some related bug reports:
// https://youtrack.jetbrains.com/issue/KT-47936
// https://github.com/gradle/gradle/issues/17043
defaultPOM(lib)
}
}
if (withSignErrorWorkaround) tasks.withSignErrorWorkaround() // very much related to comments above too
if (withPublishingPrintln) tasks.withPublishingPrintln()
}
/*
Hacky workaround for gradle error with signing+publishing on gradle 8.1-rc-1:
FAILURE: Build failed with an exception.
* What went wrong:
A problem was found with the configuration of task ':template-full-lib:signJvmPublication' (type 'Sign').
- Gradle detected a problem with the following location: '/home/marek/code/kotlin/KGround/template-full/template-full-lib/build/libs/template-full-lib-0.0.02-javadoc.jar.asc'.
Reason: Task ':template-full-lib:publishJsPublicationToMavenLocal' uses this output of task ':template-full-lib:signJvmPublication' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
Possible solutions:
1. Declare task ':template-full-lib:signJvmPublication' as an input of ':template-full-lib:publishJsPublicationToMavenLocal'.
2. Declare an explicit dependency on ':template-full-lib:signJvmPublication' from ':template-full-lib:publishJsPublicationToMavenLocal' using Task#dependsOn.
3. Declare an explicit dependency on ':template-full-lib:signJvmPublication' from ':template-full-lib:publishJsPublicationToMavenLocal' using Task#mustRunAfter.
Please refer to https://docs.gradle.org/8.1-rc-1/userguide/validation_problems.html#implicit_dependency for more details about this problem.
*/
fun TaskContainer.withSignErrorWorkaround() =
withType<AbstractPublishToMaven>().configureEach { dependsOn(withType<Sign>()) }
fun TaskContainer.withPublishingPrintln() = withType<AbstractPublishToMaven>().configureEach {
val coordinates = publication.run { "$groupId:$artifactId:$version" }
when (this) {
is PublishToMavenRepository -> doFirst {
println("Publishing $coordinates to ${repository.url}")
}
is PublishToMavenLocal -> doFirst {
val localRepo = System.getenv("HOME")!! + "/.m2/repository"
val localPath = localRepo + publication.run { "/$groupId/$artifactId".replace('.', '/') }
println("Publishing $coordinates to $localPath")
}
}
}
// endregion [[Kotlin Module Build Template]]
// region [[MPP Module Build Template]]
/**
* Only for very standard small libs. In most cases it's better to not use this function.
*
* These ignoreXXX flags are hacky, but needed. see [allDefault] kdoc for details.
*/
fun Project.defaultBuildTemplateForBasicMppLib(
details: LibDetails = rootExtLibDetails,
ignoreCompose: Boolean = false, // so user have to explicitly say THAT he wants to ignore compose settings here.
ignoreAndroTarget: Boolean = false, // so user have to explicitly say IF he wants to ignore it.
ignoreAndroConfig: Boolean = false, // so user have to explicitly say THAT he wants to ignore it.
ignoreAndroPublish: Boolean = false, // so user have to explicitly say THAT he wants to ignore it.
addCommonMainDependencies: KotlinDependencyHandler.() -> Unit = {},
) {
require(ignoreCompose || details.settings.compose == null) { "defaultBuildTemplateForBasicMppLib can not configure compose stuff" }
details.settings.andro?.let {
require(ignoreAndroConfig) { "defaultBuildTemplateForBasicMppLib can not configure android stuff (besides just adding target)" }
require(ignoreAndroPublish || it.publishNoVariants) { "defaultBuildTemplateForBasicMppLib can not publish android stuff YET" }
}
repositories { addRepos(details.settings.repos) }
defaultGroupAndVerAndDescription(details)
extensions.configure<KotlinMultiplatformExtension> {
allDefault(
settings = details.settings,
ignoreCompose = ignoreCompose,
ignoreAndroTarget = ignoreAndroTarget,
ignoreAndroConfig = ignoreAndroConfig,
ignoreAndroPublish = ignoreAndroPublish,
addCommonMainDependencies = addCommonMainDependencies,
)
}
configurations.checkVerSync(warnOnly = true)
tasks.defaultKotlinCompileOptions(jvmTargetVer = null) // jvmVer is set in fun allDefault using jvmToolchain
tasks.defaultTestsOptions(onJvmUseJUnitPlatform = details.settings.withTestJUnit5)
if (plugins.hasPlugin("maven-publish")) {
defaultPublishing(details)
if (plugins.hasPlugin("signing")) defaultSigning()
else println("MPP Module ${name}: signing disabled")
} else println("MPP Module ${name}: publishing (and signing) disabled")
}
/**
* Only for very standard small libs. In most cases it's better to not use this function.
*
* These ignoreXXX flags are hacky, but needed because we want to inject this code also to such build files,
* where plugins for compose and/or android are not applied at all, so compose/android stuff should be explicitly ignored,
* and then configured right after this call, using code from another special region (region using compose and/or andro plugin stuff).
* Also kmp andro publishing is in the middle of big changes, so let's not support it yet, and let's wait for more clarity regarding:
* https://youtrack.jetbrains.com/issue/KT-61575/Publishing-a-KMP-library-handles-Android-target-inconsistently-requiring-an-explicit-publishLibraryVariants-call-to-publish
* https://youtrack.jetbrains.com/issue/KT-60623/Deprecate-publishAllLibraryVariants-in-kotlin-android
*/
fun KotlinMultiplatformExtension.allDefault(
settings: LibSettings,
ignoreCompose: Boolean = false, // so user have to explicitly say THAT he wants to ignore compose settings here.
ignoreAndroTarget: Boolean = false, // so user have to explicitly say IF he wants to ignore it.
ignoreAndroConfig: Boolean = false, // so user have to explicitly say THAT he wants to ignore it.
ignoreAndroPublish: Boolean = false, // so user have to explicitly say THAT he wants to ignore it.
addCommonMainDependencies: KotlinDependencyHandler.() -> Unit = {},
) = with(settings) {
require(ignoreCompose || compose == null) { "allDefault can not configure compose stuff" }
andro?.let {
require(ignoreAndroConfig) { "allDefault can not configure android stuff (besides just adding target)" }
require(ignoreAndroPublish || it.publishNoVariants) { "allDefault can not publish android stuff YET" }
}
if (withJvm) jvm()
if (withJs) jsDefault()
if (withNativeLinux64) linuxX64()
if (withAndro && !ignoreAndroTarget) androidTarget {
// TODO_someday some kmp andro publishing. See kdoc above why not yet.
}
withJvmVer?.let { jvmToolchain(it.toInt()) } // works for jvm and android
sourceSets {
val commonMain by getting {
dependencies {
if (withKotlinxHtml) implementation(KotlinX.html)
addCommonMainDependencies()
}
}
val commonTest by getting {
dependencies {
implementation(Kotlin.test)
if (withTestUSpekX) implementation(Langiewicz.uspekx)
}
}
if (withJvm) {
val jvmTest by getting {
dependencies {
if (withTestJUnit4) implementation(JUnit.junit)
if (withTestJUnit5) implementation(Org.JUnit.Jupiter.junit_jupiter_engine)
if (withTestUSpekX) {
implementation(Langiewicz.uspekx)
if (withTestJUnit4) implementation(Langiewicz.uspekx_junit4)
if (withTestJUnit5) implementation(Langiewicz.uspekx_junit5)
}
if (withTestGoogleTruth) implementation(Com.Google.Truth.truth)
if (withTestMockitoKotlin) implementation(Org.Mockito.Kotlin.mockito_kotlin)
}
}
}
if (withNativeLinux64) {
val linuxX64Main by getting
val linuxX64Test by getting
}
}
}
fun KotlinMultiplatformExtension.jsDefault(
withBrowser: Boolean = true,
withNode: Boolean = false,
testWithChrome: Boolean = true,
testHeadless: Boolean = true,
) {
js(IR) {
if (withBrowser) browser {
testTask {
useKarma {
when (testWithChrome to testHeadless) {
true to true -> useChromeHeadless()
true to false -> useChrome()
}
}
}
}
if (withNode) nodejs()
}
}
// endregion [[MPP Module Build Template]]