1 /* 2 * 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.resources 18 19 import androidx.build.checkapi.ApiLocation 20 import java.io.File 21 import org.gradle.api.DefaultTask 22 import org.gradle.api.GradleException 23 import org.gradle.api.file.RegularFileProperty 24 import org.gradle.api.provider.Property 25 import org.gradle.api.tasks.CacheableTask 26 import org.gradle.api.tasks.InputFiles 27 import org.gradle.api.tasks.Internal 28 import org.gradle.api.tasks.PathSensitive 29 import org.gradle.api.tasks.PathSensitivity 30 import org.gradle.api.tasks.TaskAction 31 32 /** Task for verifying changes in the public Android resource surface, e.g. `public.xml`. */ 33 @CacheableTask 34 abstract class CheckResourceApiReleaseTask : DefaultTask() { 35 /** Reference resource API file (in source control). */ 36 @get:InputFiles // InputFiles allows non-existent files, whereas InputFile does not. 37 @get:PathSensitive(PathSensitivity.RELATIVE) 38 abstract val referenceApiFile: RegularFileProperty 39 40 /** Generated resource API file (in build output). */ 41 @get:Internal abstract val apiLocation: Property<ApiLocation> 42 43 @InputFiles 44 @PathSensitive(PathSensitivity.RELATIVE) getTaskInputnull45 fun getTaskInput(): File { 46 return apiLocation.get().resourceFile 47 } 48 49 @TaskAction checkResourceApiReleasenull50 fun checkResourceApiRelease() { 51 val referenceApiFile = referenceApiFile.get().asFile 52 val apiFile = apiLocation.get().resourceFile 53 54 // Read the current API surface, if any, into memory. 55 val newApiSet = 56 if (apiFile.exists()) { 57 apiFile.readLines().toSet() 58 } else { 59 emptySet() 60 } 61 62 // Read the reference API surface into memory. 63 val referenceApiSet = 64 if (referenceApiFile.exists()) { 65 referenceApiFile.readLines().toSet() 66 } else { 67 emptySet() 68 } 69 70 // POLICY: Ensure that no resources are removed from the last released version. 71 val removedApiSet = referenceApiSet - newApiSet 72 if (removedApiSet.isNotEmpty()) { 73 var removed = "" 74 for (e in removedApiSet) { 75 removed += "$e\n" 76 } 77 78 val errorMessage = 79 """Public resources have been removed since the previous revision 80 81 Previous definition is ${referenceApiFile.canonicalPath} 82 Current definition is ${apiFile.canonicalPath} 83 84 Public resources are considered part of the library's API surface 85 and may not be removed within a major version. 86 87 Removed resources: 88 $removed""" 89 90 throw GradleException(errorMessage) 91 } 92 } 93 } 94