1 /* <lambda>null2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.build 18 19 import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 20 import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer 21 import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext 22 import org.apache.tools.zip.ZipOutputStream 23 import org.gradle.api.Project 24 import org.gradle.api.artifacts.Configuration 25 import org.gradle.api.attributes.Usage 26 import org.gradle.api.file.FileTreeElement 27 import org.gradle.api.tasks.Input 28 import org.gradle.api.tasks.Optional 29 import org.gradle.api.tasks.SourceSetContainer 30 import org.gradle.api.tasks.TaskProvider 31 import org.gradle.kotlin.dsl.named 32 33 /** Allow java and Android libraries to bundle other projects inside the project jar/aar. */ 34 object BundleInsideHelper { 35 val CONFIGURATION_NAME = "bundleInside" 36 val REPACKAGE_TASK_NAME = "repackageBundledJars" 37 38 /** 39 * Creates a configuration for the users to use that will be used to bundle these dependency 40 * jars inside of libs/ directory inside of the aar. 41 * 42 * ``` 43 * dependencies { 44 * bundleInside(project(":foo")) 45 * } 46 * ``` 47 * 48 * Used project are expected 49 * 50 * @param relocations a list of package relocations to apply 51 * @param dropResourcesWithSuffix used to drop Java resources if they match this suffix, null 52 * means no filtering 53 * @receiver the project that should bundle jars specified by this configuration 54 * @see forInsideAar(String, String) 55 */ 56 @JvmStatic 57 fun Project.forInsideAar(relocations: List<Relocation>?, dropResourcesWithSuffix: String?) { 58 val bundle = createBundleConfiguration() 59 val repackage = configureRepackageTaskForType(relocations, bundle, dropResourcesWithSuffix) 60 // Add to AGP's configuration so this jar get packaged inside of the aar. 61 dependencies.add("implementation", files(repackage.flatMap { it.archiveFile })) 62 } 63 64 /** 65 * Creates 3 configurations for the users to use that will be used bundle these dependency jars 66 * inside of libs/ directory inside of the aar. 67 * 68 * ``` 69 * dependencies { 70 * bundleInside(project(":foo")) 71 * } 72 * ``` 73 * 74 * Used project are expected 75 * 76 * @param from specifies from which package the rename should happen 77 * @param to specifies to which package to put the renamed classes 78 * @param dropResourcesWithSuffix used to drop Java resources if they match this suffix, null 79 * means no filtering 80 * @receiver the project that should bundle jars specified by these configurations 81 */ 82 @JvmStatic 83 fun Project.forInsideAar(from: String, to: String, dropResourcesWithSuffix: String?) { 84 forInsideAar(listOf(Relocation(from, to)), dropResourcesWithSuffix) 85 } 86 87 /** 88 * Creates a configuration for users to use that will bundle the dependency jars inside of this 89 * lint check's jar. This is required because lintPublish does not currently support 90 * dependencies, so instead we need to bundle any dependencies with the lint jar manually. 91 * (b/182319899) 92 * 93 * ``` 94 * dependencies { 95 * if (rootProject.hasProperty("android.injected.invoked.from.ide")) { 96 * compileOnly(LINT_API_LATEST) 97 * } else { 98 * compileOnly(LINT_API_MIN) 99 * } 100 * compileOnly(KOTLIN_STDLIB) 101 * // Include this library inside the resulting lint jar 102 * bundleInside(project(":foo-lint-utils")) 103 * } 104 * ``` 105 * 106 * @receiver the project that should bundle jars specified by these configurations 107 */ 108 @JvmStatic 109 fun Project.forInsideLintJar() { 110 val bundle = createBundleConfiguration() 111 val compileOnly = configurations.getByName("compileOnly") 112 val testImplementation = configurations.getByName("testImplementation") 113 114 compileOnly.extendsFrom(bundle) 115 testImplementation.extendsFrom(bundle) 116 117 // Relocation needed to avoid classpath conflicts with Android Studio (b/337980250) 118 // Can be removed if we migrate from using kotlin-metadata-jvm inside of lint checks 119 val relocations = listOf(Relocation("kotlin.metadata", "androidx.lint.kotlin.metadata")) 120 val repackage = configureRepackageTaskForType(relocations, bundle, null) 121 val sourceSets = extensions.getByType(SourceSetContainer::class.java) 122 repackage.configure { task -> 123 task.from(sourceSets.findByName("main")?.output) 124 // kotlin-metadata-jvm has a service descriptor that needs transformation 125 task.mergeServiceFiles() 126 // Exclude Kotlin metadata files from kotlin-metadata-jvm 127 task.exclude( 128 "META-INF/kotlin-metadata-jvm.kotlin_module", 129 "META-INF/kotlin-metadata.kotlin_module", 130 "META-INF/metadata.jvm.kotlin_module", 131 "META-INF/metadata.kotlin_module" 132 ) 133 } 134 135 listOf("apiElements", "runtimeElements").forEach { config -> 136 configurations.getByName(config).apply { 137 outgoing.artifacts.clear() 138 outgoing.artifact(repackage) 139 } 140 } 141 } 142 143 data class Relocation(val from: String, val to: String) 144 145 private fun Project.configureRepackageTaskForType( 146 relocations: List<Relocation>?, 147 configuration: Configuration, 148 dropResourcesWithSuffix: String? 149 ): TaskProvider<ShadowJar> { 150 return tasks.register(REPACKAGE_TASK_NAME, ShadowJar::class.java) { task -> 151 task.apply { 152 configurations = listOf(configuration) 153 if (relocations != null) { 154 for (relocation in relocations) { 155 relocate(relocation.from, relocation.to) 156 } 157 } 158 val dontIncludeResourceTransformer = DontIncludeResourceTransformer() 159 dontIncludeResourceTransformer.dropResourcesWithSuffix = dropResourcesWithSuffix 160 transformers.add(dontIncludeResourceTransformer) 161 archiveBaseName.set("repackaged") 162 archiveVersion.set("") 163 destinationDirectory.set(layout.buildDirectory.dir("repackaged")) 164 } 165 } 166 } 167 168 private fun Project.createBundleConfiguration(): Configuration { 169 val bundle = 170 configurations.create(CONFIGURATION_NAME) { 171 it.attributes { 172 it.attribute(Usage.USAGE_ATTRIBUTE, objects.named<Usage>(Usage.JAVA_RUNTIME)) 173 } 174 it.isCanBeConsumed = false 175 } 176 return bundle 177 } 178 179 class DontIncludeResourceTransformer : Transformer { 180 @Optional @Input var dropResourcesWithSuffix: String? = null 181 182 override fun getName(): String { 183 return "DontIncludeResourceTransformer" 184 } 185 186 override fun canTransformResource(element: FileTreeElement?): Boolean { 187 val path = element?.relativePath?.pathString 188 return dropResourcesWithSuffix != null && 189 (path?.endsWith(dropResourcesWithSuffix!!) == true) 190 } 191 192 override fun transform(context: TransformerContext?) { 193 // no op 194 } 195 196 override fun hasTransformedResource(): Boolean { 197 return true 198 } 199 200 override fun modifyOutputStream(zipOutputStream: ZipOutputStream?, b: Boolean) { 201 // no op 202 } 203 } 204 } 205