1 /*
2  * Copyright 2025 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.android.build.api.dsl.ConsumerKeepRules
20 import com.android.build.api.dsl.LibraryBuildType
21 import java.io.File
22 import org.gradle.api.DefaultTask
23 import org.gradle.api.Project
24 import org.gradle.api.file.ConfigurableFileCollection
25 import org.gradle.api.file.DirectoryProperty
26 import org.gradle.api.file.RegularFileProperty
27 import org.gradle.api.plugins.JavaPluginExtension
28 import org.gradle.api.provider.Property
29 import org.gradle.api.tasks.Input
30 import org.gradle.api.tasks.InputFile
31 import org.gradle.api.tasks.InputFiles
32 import org.gradle.api.tasks.OutputDirectory
33 import org.gradle.api.tasks.PathSensitive
34 import org.gradle.api.tasks.PathSensitivity
35 import org.gradle.api.tasks.TaskAction
36 import org.gradle.work.DisableCachingByDefault
37 
38 /**
39  * Add a blank consumer proguard rules file to the JAR if the library has not set up an explicit set
40  * of rules.
41  */
setUpBlankProguardFileForJarIfNeedednull42 internal fun Project.setUpBlankProguardFileForJarIfNeeded(javaExtension: JavaPluginExtension) {
43     if (project.multiplatformExtension != null) return // skip KMP projects
44     val mainSources = javaExtension.sourceSets.getByName("main")
45     val provider =
46         tasks.register("emptyProguardFileCopy", BlankProguardFileGenerator::class.java) {
47             it.blankProguardFile.set(blankProguardRules())
48             it.outputDirectory.set(layout.buildDirectory.dir("blankProguard"))
49             it.nonGeneratedResources.from(mainSources.resources.sourceDirectories)
50             // unique name like "androidx-arch-core-core-common"
51             it.libraryName.set("androidx${project.path.replace(":", "-")}")
52         }
53     mainSources.output.dir(provider.flatMap { it.outputDirectory })
54 }
55 
56 /**
57  * Add a blank consumer proguard rules file to the AAR if the library has not set up an explicit set
58  * of rules.
59  */
setUpBlankProguardFileForAarIfNeedednull60 internal fun Project.setUpBlankProguardFileForAarIfNeeded(buildType: LibraryBuildType) {
61     if (buildType.consumerProguardFiles.isEmpty()) {
62         buildType.consumerProguardFiles.add(blankProguardRules())
63     }
64 }
65 
66 /**
67  * Add a blank consumer proguard rules file to the AAR if the library has not set up an explicit set
68  * of rules.
69  */
70 @Suppress("UnstableApiUsage") // b/393137152
setUpBlankProguardFileForKmpAarIfNeedednull71 internal fun Project.setUpBlankProguardFileForKmpAarIfNeeded(consumerKeepRules: ConsumerKeepRules) {
72     if (consumerKeepRules.files.isEmpty()) {
73         file(project.blankProguardRules())
74         consumerKeepRules.publish = true
75     }
76 }
77 
78 @DisableCachingByDefault
79 abstract class BlankProguardFileGenerator : DefaultTask() {
80     @get:[InputFile PathSensitive(PathSensitivity.NONE)]
81     abstract val blankProguardFile: RegularFileProperty
82 
83     @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
84     abstract val nonGeneratedResources: ConfigurableFileCollection
85 
86     @get:Input abstract val libraryName: Property<String>
87 
88     @get:OutputDirectory abstract val outputDirectory: DirectoryProperty
89 
90     @TaskAction
copyEmptyFilenull91     fun copyEmptyFile() {
92         outputDirectory.get().asFile.deleteRecursively()
93         val hasExplicitProguardFile =
94             nonGeneratedResources.any { File(it, "META-INF/proguard").exists() }
95         // Check if the library already contains explicit proguard file
96         if (hasExplicitProguardFile) return
97         blankProguardFile
98             .get()
99             .asFile
100             .copyTo(
101                 File(outputDirectory.get().asFile, "META-INF/proguard/${libraryName.get()}.pro")
102             )
103     }
104 }
105 
Projectnull106 private fun Project.blankProguardRules(): File =
107     project.getSupportRootFolder().resolve("buildSrc/blank-proguard-rules/proguard-rules.pro")
108