Skip to content

Commit 08b060d

Browse files
0xeranatario1
authored andcommitted
include kotlin_module files to final fat aar
1 parent 125230a commit 08b060d

File tree

5 files changed

+169
-71
lines changed

5 files changed

+169
-71
lines changed

gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
[versions]
22
agp = "8.1.4"
3+
apache-ant = "1.10.14"
34
asm-commons = "9.6"
45
android-tools = "31.1.4"
56
kotlin = "2.0.0"
67
shadow = "8.3.0"
78
publisher = "0.14.0"
9+
kotlinx-metadata = "0.9.0"
810

911
[libraries]
12+
apache-ant = { module = "org.apache.ant:ant", version.ref = "apache-ant" }
1013
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm-commons" }
1114
gradle-android-common = { module = "com.android.tools:common", version.ref = "android-tools" }
1215
gradle-android-sdk-common = { module = "com.android.tools:sdk-common", version.ref = "android-tools" }
1316
gradle-android-layoutlib = { module = "com.android.tools.layoutlib:layoutlib-api", version.ref = "android-tools" }
1417
gradle-android-build = { module = "com.android.tools.build:gradle", version.ref = "agp" }
1518
gradle-shadow = { module = "com.gradleup.shadow:shadow-gradle-plugin", version.ref = "shadow" }
19+
kotlinx-metadata-jvm = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version.ref = "kotlinx-metadata" }
1620

1721
[bundles]
1822
gradle-android = ["gradle-android-sdk-common", "gradle-android-build", "gradle-android-common", "gradle-android-layoutlib"]

grease/build.gradle.kts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group = "io.deepmedia.tools"
7-
version = "0.3.0"
7+
version = "0.7.0"
88

99
gradlePlugin {
1010
plugins {
@@ -19,6 +19,8 @@ dependencies {
1919
implementation(libs.asm.commons)
2020
implementation(libs.gradle.shadow)
2121
implementation(libs.bundles.gradle.android)
22+
implementation(libs.kotlinx.metadata.jvm)
23+
implementation(libs.apache.ant)
2224
}
2325

2426
deployer {
@@ -75,4 +77,13 @@ deployer {
7577
token = secret("GHUB_PERSONAL_ACCESS_TOKEN")
7678
}
7779
}
80+
}
81+
82+
publishing {
83+
repositories {
84+
maven {
85+
name = "Local"
86+
url = uri(rootProject.layout.buildDirectory.dir("grease_pub"))
87+
}
88+
}
7889
}

grease/src/main/kotlin/io/deepmedia/tools/grease/GreasePlugin.kt

Lines changed: 84 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
package io.deepmedia.tools.grease
44

5-
import com.android.build.api.component.analytics.AnalyticsEnabledLibraryVariant
65
import com.android.build.api.component.analytics.AnalyticsEnabledVariant
76
import com.android.build.api.variant.AndroidComponentsExtension
87
import com.android.build.api.variant.Variant
@@ -31,12 +30,13 @@ import com.android.ide.common.resources.CopyToOutputDirectoryResourceCompilation
3130
import com.android.manifmerger.ManifestMerger2
3231
import com.android.manifmerger.ManifestProvider
3332
import com.android.utils.StdLogger
34-
import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
33+
import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
3534
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
3635
import org.gradle.api.Plugin
3736
import org.gradle.api.Project
3837
import org.gradle.api.artifacts.Configuration
39-
import org.gradle.kotlin.dsl.get
38+
import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
39+
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
4040
import org.gradle.kotlin.dsl.support.unzipTo
4141
import org.gradle.kotlin.dsl.support.zipTo
4242
import java.io.File
@@ -56,40 +56,39 @@ open class GreasePlugin : Plugin<Project> {
5656

5757
@Suppress("NAME_SHADOWING")
5858
override fun apply(target: Project) {
59-
require(target.plugins.hasPlugin("com.android.library")) {
60-
"Grease must be applied after the com.android.library plugin."
61-
}
62-
val log = Logger(target, "grease")
63-
val android = target.extensions["android"] as LibraryExtension
64-
val androidComponents = target.extensions.getByType(AndroidComponentsExtension::class.java)
65-
val greaseExtension = target.extensions.create("grease", GreaseExtension::class.java)
66-
67-
debugGreasyConfigurationHierarchy(target, log)
68-
69-
// Create the configurations.
70-
fun createConfigurations(isTransitive: Boolean) {
71-
target.createRootConfiguration(isTransitive, log)
72-
target.createProductFlavorConfigurations(androidComponents, isTransitive, log)
73-
target.createBuildTypeConfigurations(android.buildTypes, isTransitive, log)
74-
target.createVariantConfigurations(androidComponents, isTransitive, log)
75-
}
76-
createConfigurations(false)
77-
createConfigurations(true)
78-
79-
fun configure(variant: Variant, vararg configurations: Configuration) {
80-
configureVariantManifest(target, variant, configurations, log)
81-
configureVariantJniLibs(target, variant, configurations, log)
82-
configureVariantResources(target, variant, configurations, log)
83-
configureVariantSources(target, variant, configurations, greaseExtension, log)
84-
configureVariantAssets(target, variant, configurations, log)
85-
configureVariantProguardFiles(target, variant, configurations, log)
86-
}
87-
// Configure all variants.
88-
androidComponents.onVariants { variant ->
89-
val log = log.child("configureVariant")
90-
log.d { "Configuring variant ${variant.name}..." }
91-
target.afterEvaluate {
92-
configure(variant, target.greaseOf(variant), target.greaseOf(variant, true))
59+
target.plugins.withId("com.android.library") {
60+
val log = Logger(target, "grease")
61+
val android = target.extensions.getByType(LibraryExtension::class.java)
62+
val androidComponents = target.extensions.getByType(AndroidComponentsExtension::class.java)
63+
val greaseExtension = target.extensions.create("grease", GreaseExtension::class.java)
64+
65+
debugGreasyConfigurationHierarchy(target, log)
66+
67+
// Create the configurations.
68+
fun createConfigurations(isTransitive: Boolean) {
69+
target.createRootConfiguration(isTransitive, log)
70+
target.createProductFlavorConfigurations(androidComponents, isTransitive, log)
71+
target.createBuildTypeConfigurations(android.buildTypes, isTransitive, log)
72+
target.createVariantConfigurations(androidComponents, isTransitive, log)
73+
}
74+
createConfigurations(false)
75+
createConfigurations(true)
76+
77+
fun configure(variant: Variant, vararg configurations: Configuration) {
78+
configureVariantManifest(target, variant, configurations, log)
79+
configureVariantJniLibs(target, variant, configurations, log)
80+
configureVariantResources(target, variant, configurations, log)
81+
configureVariantSources(target, variant, configurations, greaseExtension, log)
82+
configureVariantAssets(target, variant, configurations, log)
83+
configureVariantProguardFiles(target, variant, configurations, log)
84+
}
85+
// Configure all variants.
86+
androidComponents.onVariants { variant ->
87+
val log = log.child("configureVariant")
88+
log.d { "Configuring variant ${variant.name}..." }
89+
target.afterEvaluate {
90+
configure(variant, target.greaseOf(variant), target.greaseOf(variant, true))
91+
}
9392
}
9493
}
9594
}
@@ -432,7 +431,7 @@ open class GreasePlugin : Plugin<Project> {
432431

433432
fun injectClasses(inputJar: File) {
434433
log.d { "Processing inputJar=$inputJar outputDir=${jarExtractWorkdir}..." }
435-
val inputFiles = target.zipTree(inputJar).matching { include("**/*.class") }
434+
val inputFiles = target.zipTree(inputJar).matching { include("**/*.class", "**/*.kotlin_module") }
436435
target.copy {
437436
from(inputFiles)
438437
into(jarExtractWorkdir)
@@ -466,7 +465,7 @@ open class GreasePlugin : Plugin<Project> {
466465
destinationDirectory.set(greaseShadowDir)
467466

468467
from(greaseProcessTask.get().outputs)
469-
val packagesToReplace = mutableMapOf<String, String>()
468+
val addedPackagesNames = mutableSetOf<String>()
470469

471470
doFirst {
472471
target.delete(greaseShadowDir)
@@ -487,52 +486,35 @@ open class GreasePlugin : Plugin<Project> {
487486
.asSequence()
488487
.flatMap { inputFile -> inputFile.packageNames }
489488
.distinct()
490-
.forEach { packageName ->
491-
val newPackageName = "${relocationPrefix}.$packageName"
489+
.map { packageName -> packageName to "${relocationPrefix}.$packageName" }
490+
.distinct()
491+
.filterNot { (packageName, _) -> addedPackagesNames.any(packageName::contains) }
492+
.forEach { (packageName, newPackageName) ->
492493
log.d { "Relocate package from $packageName to $newPackageName" }
493494
relocate(packageName, newPackageName)
494-
packagesToReplace[packageName] = newPackageName
495+
addedPackagesNames += packageName
495496
}
496497
}
497498

498-
greaseExtension.relocators.get().forEach { relocator ->
499-
relocate(relocator)
500-
if (relocator is SimpleRelocator) {
501-
packagesToReplace[relocator.pattern] = relocator.shadedPattern
502-
}
503-
}
504-
499+
greaseExtension.relocators.get().forEach<Relocator?>(::relocate)
505500
greaseExtension.transformers.get().forEach(::transform)
501+
transform(KotlinModuleShadowTransformer(logger.child("kotlin_module")))
506502
}
507503

508504
doLast {
509505
val shadowJar = greaseShadowDir.file(jarFileName).asFile
510-
val shadowManifest = greaseShadowDir.file("AndroidManifest.xml").asFile
511506
log.d { "Copy shaded inputJar=${shadowJar} outputDir=$aarExtractWorkdir..." }
512507
target.copy {
513508
from(shadowJar)
514509
into(aarExtractWorkdir)
515510
}
516511

517-
val manifestWriter = shadowManifest.bufferedWriter()
518-
val manifestReader = aarExtractWorkdir.file("AndroidManifest.xml").asFile.bufferedReader()
519-
520-
manifestReader.useLines { strings ->
521-
strings
522-
.map { string ->
523-
packagesToReplace.entries.fold(string) { acc, (from, to) ->
524-
acc.replace(from, to)
525-
}
526-
}.forEach {
527-
manifestWriter.write(it)
528-
manifestWriter.newLine()
529-
}
530-
}
531-
manifestWriter.close()
532-
target.copy {
533-
from(shadowManifest)
534-
into(aarExtractWorkdir)
535-
}
512+
replacePackagesInFile(
513+
aarExtractWorkdir.file("AndroidManifest.xml").asFile,
514+
greaseShadowDir.file("AndroidManifest.xml").asFile,
515+
relocators,
516+
target,
517+
)
536518

537519
val oldArchive = bundleAar.archiveFile.get().asFile
538520
val archiveParent = oldArchive.parentFile
@@ -543,16 +525,48 @@ open class GreasePlugin : Plugin<Project> {
543525
}
544526

545527
bundleLibraryTask?.configure {
546-
finalizedBy(greaseExpandTask)
528+
outputs.upToDateWhen { false }
529+
finalizedBy(greaseShadowTask)
547530
}
548531
greaseExpandTask.configure {
532+
mustRunAfter(bundleLibraryTask)
549533
finalizedBy(greaseProcessTask)
550534
}
551535
greaseProcessTask.configure {
536+
mustRunAfter(bundleLibraryTask)
552537
finalizedBy(greaseShadowTask)
553538
}
554539
}
555540

541+
private fun replacePackagesInFile(
542+
input: File,
543+
output: File,
544+
relocators: List<Relocator>,
545+
target: Project,
546+
) {
547+
val reader = input.bufferedReader()
548+
val writer = output.bufferedWriter()
549+
reader.useLines { strings ->
550+
strings
551+
.map { string ->
552+
relocators
553+
.filterNot { it is RClassRelocator }
554+
.fold(string) { acc, relocator ->
555+
relocator.applyToSourceContent(acc)
556+
}
557+
}.forEach {
558+
writer.write(it)
559+
writer.newLine()
560+
}
561+
}
562+
writer.close()
563+
564+
target.copy {
565+
from(output)
566+
into(input.parentFile)
567+
}
568+
}
569+
556570
/**
557571
* Interesting tasks:
558572
* 1. generate<>Assets: See [MutableTaskContainer].
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.deepmedia.tools.grease
2+
3+
import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer
4+
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
5+
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
6+
import org.gradle.api.file.FileTreeElement
7+
import kotlinx.metadata.jvm.KotlinModuleMetadata
8+
import kotlinx.metadata.jvm.UnstableMetadataApi
9+
import org.apache.tools.zip.ZipEntry
10+
import org.apache.tools.zip.ZipOutputStream
11+
12+
// from kotlin sources
13+
14+
@CacheableTransformer
15+
@OptIn(UnstableMetadataApi::class)
16+
internal class KotlinModuleShadowTransformer(private val logger: Logger) : Transformer {
17+
@Suppress("ArrayInDataClass")
18+
private data class Entry(val path: String, val bytes: ByteArray)
19+
20+
private val data = mutableListOf<Entry>()
21+
22+
override fun getName() = "KotlinModuleShadowTransformer"
23+
24+
override fun canTransformResource(element: FileTreeElement): Boolean =
25+
element.path.substringAfterLast(".") == KOTLIN_MODULE
26+
27+
override fun transform(context: TransformerContext) {
28+
fun relocate(content: String): String =
29+
context.relocators
30+
.filterNot { it is RClassRelocator }
31+
.fold(content) { acc, relocator -> relocator.applyToSourceContent(acc) }
32+
33+
logger.i { "Transforming kotlin_module ${context.path}" }
34+
val metadata = KotlinModuleMetadata.read(context.`is`.readBytes())
35+
val module = metadata.kmModule
36+
37+
val packageParts = module.packageParts.toMap()
38+
module.packageParts.clear()
39+
packageParts.map { (fqName, parts) ->
40+
require(parts.multiFileClassParts.isEmpty()) { parts.multiFileClassParts } // There are no multi-file class parts in core
41+
42+
val fileFacades = parts.fileFacades.toList()
43+
parts.fileFacades.clear()
44+
fileFacades.mapTo(parts.fileFacades) { relocate(it) }
45+
46+
relocate(fqName) to parts
47+
}.toMap(module.packageParts)
48+
49+
data += Entry(context.path, metadata.write())
50+
}
51+
52+
override fun hasTransformedResource(): Boolean = data.isNotEmpty()
53+
54+
override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {
55+
for ((path, bytes) in data) {
56+
os.putNextEntry(ZipEntry(path))
57+
os.write(bytes)
58+
}
59+
data.clear()
60+
}
61+
62+
companion object {
63+
const val KOTLIN_MODULE = "kotlin_module"
64+
}
65+
}

tests/sample-dependency-pure/src/main/java/io/deepmedia/tools/grease/sample/dependency/pure/PureDependencyClass.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ package io.deepmedia.tools.grease.sample.dependency.pure
22

33
object PureDependencyClass {
44
fun foo() = "bar"
5+
}
6+
7+
fun PureDependencyFunction() {
8+
println("foo")
59
}

0 commit comments

Comments
 (0)