Skip to content

Commit

Permalink
feat: self-apply the plugin to itself immediately with included build
Browse files Browse the repository at this point in the history
Signed-off-by: Artyom Shendrik <[email protected]>
  • Loading branch information
amal committed Feb 27, 2024
1 parent f997e5b commit 8b4a2fc
Show file tree
Hide file tree
Showing 15 changed files with 244 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
- return the plugin artifact shrinking with R8 (saved 32.719%, 265.9 KB).
- update compatibility methods `NamedDomainObjectSet<T>.named*` for Gradle 8.6+ and older.

### Added
- self-apply the plugin to itself immediately with included build.

### Fixed
- prevent double escaping of cli arguments.

Expand Down
23 changes: 22 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@
* UTF 8 for Java compilation and Javadoc
* Reproducible artifacts
* Granular test reports per test case (method instead of class)
* iurysza
* A Gradle Plugin for visualizing your project's structure, powered by mermaidjs.
* https://github.com/iurysza/module-graph
* A project setup to bootstrap kotlin library development.
* https://github.com/iurysza/kotlin-scaffold
* https://github.com/BenWoodworth/Parameterize
* https://github.com/kotlin-hands-on/kotlin-swift-interopedia
* https://github.com/drewhamilton/poko/
Expand Down Expand Up @@ -209,5 +214,21 @@
* Validate & diff resulting artifacts
* https://github.com/JakeWharton/diffuse
* Gradle task to report native libs from dependencies (dependency + names of the native binaries)

* Gradle plugin for generating Android / KMP string resources from Google Spreadsheets.
* https://github.com/futuredapp/sheet-happens
* 🐘 A template to let you started with custom Gradle Plugins + Kotlin in a few seconds
* https://github.com/cortinico/kotlin-gradle-plugin-template
* Jsmints is a suite of libraries and gradle plugins for working with Kotlin JS, with a focus on testing and version updating.
* https://github.com/robertfmurdock/jsmints
* A Palantir set of Gradle plugins that configure default code quality tools for developers.
* https://github.com/palantir/gradle-baseline
* Gradle plugin for detecting use of legacy APIs which modern Java versions supersede.
* https://github.com/andygoossens/gradle-modernizer-plugin
* Check ABI compatibility at build time
* https://github.com/open-toast/expediter
* Kotlin/JS Fast Configuration
* https://github.com/turansky/kfc-plugins
* https://github.com/turansky/seskar (Kotlin/JS sugar)
* KtLint-cli setup
* https://github.com/pinterest/ktlint/blob/cb17bbf/ktlint-cli/build.gradle.kts#L44
</details>
2 changes: 1 addition & 1 deletion checks/main/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ kotlin.mpp.androidGradlePluginCompatibility.nowarn=true
# https://issuetracker.google.com/issues/185418482#comment32
# https://mvnrepository.com/artifact/com.android.tools.build/gradle?repo=google
# https://maven.google.com/web/index.html#com.android.tools.build:gradle
android.experimental.lint.version=8.4.0-alpha07
android.experimental.lint.version=8.4.0-alpha11

MAX_DEBUG=true
23 changes: 21 additions & 2 deletions checks/main/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
pluginManagement {
repositories {
google()
mavenCentral()
// Google/Firebase/GMS/Androidx libraries
// Don't use exclusiveContent for androidx libraries so that snapshots work.
google {
content {
includeGroupByRegex("androidx.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("android\\.arch.*")
includeGroupByRegex("org\\.chromium.*")
}
}

// R8 repo for R8/D8 releases
exclusiveContent {
forRepository {
maven("https://storage.googleapis.com/r8-releases/raw") { name = "R8-releases" }
}
filter { includeModule("com.android.tools", "r8") }
}

// For Gradle plugins only. Last because proxies to mavenCentral.
gradlePluginPortal()
}
includeBuild("../../")
Expand Down
6 changes: 4 additions & 2 deletions dependencies/classpath.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
:
com.diffplug.durian:durian-collect:1.2.0
com.diffplug.durian:durian-core:1.2.0
com.diffplug.durian:durian-io:1.2.0
Expand Down Expand Up @@ -29,8 +30,6 @@ com.squareup.okio:okio-jvm:3.6.0
com.squareup.okio:okio:3.6.0
commons-codec:commons-codec:1.16.0
dev.equo.ide:solstice:1.7.5
io.github.fluxo-kt.fluxo-kmp-conf:io.github.fluxo-kt.fluxo-kmp-conf.gradle.plugin:0.7.0-alpha2
io.github.fluxo-kt:fluxo-kmp-conf:0.7.0-alpha2
io.github.java-diff-utils:java-diff-utils:4.12
io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.5
jakarta.activation:jakarta.activation-api:1.2.1
Expand Down Expand Up @@ -67,6 +66,9 @@ org.jetbrains.kotlin:kotlin-scripting-common:1.9.22
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.9.22
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.9.22
org.jetbrains.kotlin:kotlin-scripting-jvm:1.9.22
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.20
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10
org.jetbrains.kotlin:kotlin-stdlib:1.9.20
org.jetbrains.kotlin:kotlin-tooling-core:1.9.22
org.jetbrains.kotlin:kotlin-util-io:1.9.22
Expand Down
4 changes: 3 additions & 1 deletion fluxo-kmp-conf/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<ID>ForbiddenComment:FluxoConfigurationExtensionPublicationImpl.kt$FluxoConfigurationExtensionPublicationImpl$// FIXME: Should be converted to a lazy provider!</ID>
<ID>ForbiddenComment:FluxoConfigurationExtensionPublicationImpl.kt$FluxoConfigurationExtensionPublicationImpl$// TODO: Add validation for value. Shouldn't be url, but `namespace/name`</ID>
<ID>ForbiddenComment:FluxoKmpConfContext.kt$FluxoKmpConfContext$// TODO: Better integration with `gradle-idea-ext-plugin` or `idea` plugins.</ID>
<ID>ForbiddenComment:FluxoKmpConfContext.kt$FluxoKmpConfContext$// TODO: GC stats</ID>
<ID>ForbiddenComment:FluxoKmpConfPlugin.kt$FluxoKmpConfPlugin$// TODO: Support version catalog declarations if available</ID>
<ID>ForbiddenComment:FluxoPublicationConfig.kt$// TODO: Make an immutable resulting class</ID>
<ID>ForbiddenComment:GetSetOfRequestedKmpTargets.kt$// TODO: Support "metadata_only"/metadataOnly mode (see arkivanov/gradle-setup-plugin)</ID>
Expand Down Expand Up @@ -64,13 +65,14 @@
<ID>ForbiddenComment:SetupDetekt.kt$DetectedTaskPlatform$// TODO: Detect common/metadata tasks?</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: Check BuildConfig tasks (not called on IDE sync!)</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: Disambiguate existing javadoc and sources tasks</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: Spotless stup</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: Spotless setup</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: check all features</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: detekt plugins https://detekt.dev/marketplace/</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: git hooks</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// FIXME: https://github.com/topjohnwu/libsu/blob/01570d643af91b0e271de018465a219eed8db322/service/build.gradle.kts#L21</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// TODO: Retry create pluginId from configuration if null?</ID>
<ID>ForbiddenComment:SetupGradlePlugin.kt$// TODO: Try avoid this cast?</ID>
<ID>ForbiddenComment:SetupKotlin.kt$// FIXME: Configure common compilerOptions via KotlinProjectExtension.compilerOptions</ID>
<ID>ForbiddenComment:SetupKotlin.kt$// TODO: Check KSP setup for KMP modules</ID>
<ID>ForbiddenComment:SetupKotlin.kt$// TODO: Detect if KMP is already applied and what targets are already configured.</ID>
<ID>ForbiddenComment:SetupKotlinCompatibility.kt$// TODO: Detekt doesn't support 21 yet, enable when it does</ID>
Expand Down
2 changes: 1 addition & 1 deletion fluxo-kmp-conf/src/main/kotlin/SetupGradlePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public fun Project.setupGradlePlugin(

pluginName ?: return@configuration

// Configure Gradle plugin eagerly!
// `gradlePlugin`: Configure Gradle plugin eagerly!
// Otherwise, it's not available for composite builds.
gradlePluginExt.plugins.maybeCreate(pluginName).apply {
// TODO: Retry create pluginId from configuration if null?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ internal abstract class FluxoKmpConfContext
"$CPUs CPUs, " +
"${readableByteSize(XMX)} XMX"

// TODO: GC stats
// https://github.com/gradle/gradle/blob/3eda2dd/platforms/core-runtime/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthStats.java#L87
val ram = TOTAL_OS_MEMORY
if (ram > 0) {
m += " with ${readableByteSize(ram)} RAM"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public class FluxoKmpConfPlugin : Plugin<Project> {
} catch (e: Throwable) {
// TODO: Support version catalog declarations if available
val msg = """
Kotlin plugin not found in classpath.
Kotlin plugin not found in classpath (${e.javaClass.simpleName}).
Please apply any Kotlin plugin before applying the "$PLUGIN_ID" plugin.
Example:
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ private fun KotlinProjectExtension.setupKotlinExtensionAndProject(
}
}

// FIXME: Configure common compilerOptions via KotlinProjectExtension.compilerOptions
@Suppress("CyclomaticComplexMethod")
private fun KotlinProjectExtension.setupTargets(
conf: FluxoConfigurationExtensionImpl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal fun Project.setupSamWithReceiver(ctx: FluxoKmpConfContext) {
return
}

// Gradle kotlin-dsl like setup. See:
// https://github.com/gradle/gradle/blob/4817230/build-logic/kotlin-dsl/src/main/kotlin/gradlebuild.kotlin-dsl-sam-with-receiver.gradle.kts#L22
try {
val swre = extensions.getByName(KT_SAM_RECEIVER_EXTENSION)
val hasImplicitReceiver = requireNotNull(HasImplicitReceiver::class.qualifiedName)
Expand Down
3 changes: 1 addition & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
[versions]

version = "0.7.0-SNAPSHOT"
fluxo-conf = "0.7.0-alpha2"

# Java/Kotlin compatibility
# WARNING: kotlinApiVersion can't be greater than kotlinLangVersion!
Expand Down Expand Up @@ -214,7 +213,7 @@ pinned = ["okhttp", "okio", "okio-jvm", "guava", "json"]

[plugins]

fluxo-conf = { id = "io.github.fluxo-kt.fluxo-kmp-conf", version.ref = "fluxo-conf" }
fluxo-conf = { id = "io.github.fluxo-kt.fluxo-kmp-conf", version.ref = "version" }

kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Expand Down
146 changes: 146 additions & 0 deletions self/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
plugins {
alias(libs.plugins.gradle.plugin.publish)
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.sam.receiver)
alias(libs.plugins.build.config)
}

val pluginDir = "fluxo-kmp-conf"
val pluginId = libs.plugins.fluxo.conf.get().pluginId
version = libs.versions.version.get()

// FIXME: Find a way to deduplicate configuration between `self` and `plugin` modules.

kotlin {
sourceSets["main"].kotlin.srcDir("../$pluginDir/src/main/kotlin")

explicitApi()
compilerOptions {
@Suppress("MaxLineLength")
freeCompilerArgs.addAll(
"-Xcontext-receivers",
// Kotlin's assignment overloading for Gradle plugins.
// Lookup the Gradle repo for more details. Also:
// https://stackoverflow.com/a/76022933/1816338
"-P=plugin:org.jetbrains.kotlin.assignment:annotation=org.gradle.api.SupportsKotlinAssignmentOverloading",
)
}
}

// https://github.com/gradle/gradle/blob/4817230/build-logic/kotlin-dsl/src/main/kotlin/gradlebuild.kotlin-dsl-sam-with-receiver.gradle.kts#L22
samWithReceiver {
annotation(requireNotNull(HasImplicitReceiver::class.qualifiedName))
}

gradlePlugin {
plugins {
create("$pluginDir-buildSrc") {
id = pluginId
implementationClass = "fluxo.conf.FluxoKmpConfPlugin"
}
}
}

dependencies {
// Spotless util classes are used internally
implementation(libs.plugin.spotless)
// Detekt ReportMergeTask is used internally
implementation(libs.plugin.detekt)

implementation(platform(libs.okhttp.bom))

compileOnly(libs.detekt.core)
compileOnly(libs.ktlint)

compileOnly(libs.plugin.android)
compileOnly(libs.plugin.intellij)
compileOnly(libs.plugin.jetbrains.compose)
compileOnly(libs.plugin.kotlin)
compileOnly(libs.plugin.ksp)

compileOnly(libs.plugins.gradle.enterprise.toModuleDependency())
}

buildConfig {
className("BuildConstants")
packageName("fluxo.conf.data")
buildConfigField("String", "PLUGIN_ID", "\"$pluginId\"")
buildConfigField("int", "DEFAULT_ANDROID_MIN_SDK", libs.versions.androidMinSdk.get())
buildConfigField("int", "DEFAULT_ANDROID_TARGET_SDK", libs.versions.androidTargetSdk.get())
buildConfigField("int", "DEFAULT_ANDROID_COMPILE_SDK", libs.versions.androidCompileSdk.get())

fun buildConfigField(
name: String,
p: Provider<PluginDependency>,
alias: String? = null,
alias2: String? = null,
implementation: Boolean = false,
) {
val aliasName = alias ?: name.lowercase().replace('_', '-')
buildConfigField("String", "${name}_PLUGIN_ALIAS", "\"$aliasName\"")

alias2?.let {
buildConfigField("String", "${name}_PLUGIN_ALIAS2", "\"$it\"")
}

val pd = p.get()
val pluginId = pd.pluginId
buildConfigField("String", "${name}_PLUGIN_ID", "\"$pluginId\"")

"${pd.version}".ifBlank { null }?.let { version ->
buildConfigField("String", "${name}_PLUGIN_VERSION", "\"$version\"")
}

p.toModuleDependency().let { dependency ->
when {
implementation -> dependencies.implementation(dependency)
else -> dependencies.compileOnly(dependency)
}
}
}

buildConfigField("KOTLIN_SAM_RECEIVER", libs.plugins.kotlin.sam.receiver)
buildConfigField(
"KOTLINX_BCV",
libs.plugins.kotlinx.binCompatValidator,
alias2 = "kotlinx-binCompatValidator",
)
buildConfigField("FLUXO_BCV_JS", libs.plugins.fluxo.bcv.js)
buildConfigField("DOKKA", libs.plugins.dokka)
buildConfigField("GRADLE_PLUGIN_PUBLISH", libs.plugins.gradle.plugin.publish)
buildConfigField("COMPLETE_KOTLIN", libs.plugins.complete.kotlin)
buildConfigField("DEPS_VERSIONS", libs.plugins.deps.versions, implementation = true)
buildConfigField("DEPS_ANALYSIS", libs.plugins.deps.analysis)
buildConfigField("DEPS_GUARD", libs.plugins.deps.guard, implementation = true)
buildConfigField("TASK_TREE", libs.plugins.task.tree)
buildConfigField("TASK_INFO", libs.plugins.task.info)
buildConfigField("MODULE_DEPENDENCY_GRAPH", libs.plugins.module.dependency.graph)
buildConfigField("BUILD_CONFIG", libs.plugins.build.config)

fun buildConfigField(
fieldName: String,
p: Provider<MinimalExternalModuleDependency>,
compileOnly: Boolean = true,
) {
p.get().apply {
buildConfigField("String", fieldName, "\"$group:$name:$version\"")
}
if (compileOnly) {
dependencies.compileOnly(p)
}
}
buildConfigField("PROGUARD_PLUGIN", libs.proguard.plugin)
buildConfigField("PROGUARD_CORE", libs.proguard.core)
buildConfigField("KOTLINX_METADATA_JVM", libs.kotlinx.metadata.jvm)
buildConfigField("R8", libs.r8)
}

fun Provider<PluginDependency>.toModuleDependency(): Provider<String> = map {
it.toModuleDependency()
}

fun PluginDependency.toModuleDependency(): String {
val version = version.toString()
val v = if (!version.isNullOrBlank()) ":$version" else ""
return pluginId.let { "$it:$it.gradle.plugin$v" }
}
22 changes: 22 additions & 0 deletions self/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pluginManagement {
repositories {
gradlePluginPortal()
}
}

plugins {
id("com.gradle.enterprise") version "3.16.2"
}

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
gradlePluginPortal()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
14 changes: 14 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
@file:Suppress("StructuralWrap")

enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

pluginManagement {
repositories {
gradlePluginPortal()
}

// region Self-apply the plugin to itself immediately with included build.
// `buildSrc` is not used to avoid caching and configuration issues.
// References:
// - https://github.com/hakanai/self-applying-gradle-plugin
// - https://gist.github.com/johnrengelman/9a20697b2246a9bfaca2
// - https://discuss.gradle.org/t/in-a-gradle-plugin-project-how-can-i-apply-the-plugin-itself-in-the-build/5700
// - https://docs.gradle.org/8.6/userguide/sharing_build_logic_between_subprojects.html#sec:using_buildsrc
includeBuild("self")
// endregion
}

// TODO: Limit the repositories in packages (check all declarations in project!)
Expand Down

0 comments on commit 8b4a2fc

Please sign in to comment.