Last active
October 10, 2025 14:20
-
-
Save NikolaDespotoski/acce94ac347a152df2afbcb14d9ff41b to your computer and use it in GitHub Desktop.
Generate proguard rules out of output of apiDump task of Kotlin binary compatibility plugin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tasks.register("updateProguardFromApiDump") { | |
group = "verification" | |
description = | |
"Updates proguard-rules.pro with -keep rules for public classes found in a .api dump (default: uses the first .api file under api/)." | |
dependsOn("apiDump") | |
doLast { | |
val apiDir = file("api") | |
val proguardFile = file("proguard-rules.pro") | |
val apiFilePath = if (project.hasProperty("apiFile")) project.property("apiFile") else null | |
val apiFile = if (apiFilePath != null) { | |
file(apiFilePath.toString()) | |
} else { | |
val candidates = | |
apiDir.listFiles { _: File, name: String -> name.endsWith(".api") }.orEmpty() | |
if (candidates.isEmpty()) { | |
throw GradleException("No .api files found in $apiDir") | |
} | |
println("No -PapiFile specified, using: ${candidates.first()}") | |
} | |
val apiRelativePath = | |
project.rootDir.toPath().relativize(apiFile.toPath()).toString().replace("\\", "/") | |
// Check if the .api file is modified in the current changelist (uncommitted) | |
val statusProc = ProcessBuilder("git", "status", "--porcelain", apiRelativePath) | |
.directory(project.rootDir) | |
.start() | |
statusProc.waitFor() | |
val statusOut = statusProc.inputStream.bufferedReader().readText().trim() | |
if (statusOut.isEmpty()) { | |
println("No uncommitted changes to ${apiRelativePath}. Skipping ProGuard rules update.") | |
return@doLast | |
} else { | |
println("Uncommitted changes to $apiRelativePath found. Updating ProGuard rules.") | |
} | |
if (!apiFile.exists()) { | |
println(".api dump not found at ${apiFile}. Attempting to resolve from git...") | |
val proc = ProcessBuilder("git", "checkout", "HEAD", "--", apiRelativePath) | |
.directory(project.rootDir) | |
.start() | |
proc.waitFor() | |
if (!apiFile.exists()) { | |
throw GradleException(".api dump could not be resolved from git. Make sure you have committed the file.") | |
} else { | |
println(".api file resolved from git.") | |
} | |
} | |
// Step 1. Extract all public top-level classes from API dump | |
val apiLines = apiFile.readLines() | |
val classPattern = Regex("^public (((final|abstract) )?(class|interface|enum)) ([^ ]+) .*") | |
val classNames = apiLines.filter { it.matches(classPattern) }.mapNotNull { line -> | |
val match = classPattern.find(line) ?: return@mapNotNull null | |
val className = match.groupValues[5] | |
className.replace("/", ".") | |
} | |
// Step 2. Prepare proguard keep rules | |
val newRules = classNames.map { "-keep public class $it { public *; }" } | |
val proguardText = proguardFile.readText() | |
val missingRules = newRules.filter { rule -> | |
!proguardText.contains(rule) | |
} | |
if (missingRules.isNotEmpty()) { | |
missingRules.forEach { rule -> | |
proguardFile.appendText("$rule\n") | |
} | |
println("Added new ProGuard rules from ${apiFile.name}:\n${missingRules.joinToString("\n")}") | |
} else { | |
println("No new ProGuard rules to add from ${apiFile.name}.") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment