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.resources
18 
19 import androidx.build.AndroidXImplPlugin.Companion.TASK_GROUP_API
20 import androidx.build.addToBuildOnServer
21 import androidx.build.addToCheckTask
22 import androidx.build.checkapi.ApiLocation
23 import androidx.build.checkapi.getRequiredCompatibilityApiLocation
24 import androidx.build.metalava.UpdateApiTask
25 import androidx.build.uptodatedness.cacheEvenIfNoOutputs
26 import org.gradle.api.Project
27 import org.gradle.api.file.RegularFile
28 import org.gradle.api.provider.Provider
29 
30 object ResourceTasks {
31     private const val GENERATE_RESOURCE_API_TASK = "generateResourceApi"
32     private const val CHECK_RESOURCE_API_RELEASE_TASK = "checkResourceApiRelease"
33     private const val CHECK_RESOURCE_API_TASK = "checkResourceApi"
34     private const val UPDATE_RESOURCE_API_TASK = "updateResourceApi"
35 
36     fun setupProject(
37         project: Project,
38         builtApiFile: Provider<RegularFile>,
39         builtApiLocation: ApiLocation,
40         outputApiLocations: List<ApiLocation>
41     ) {
42 
43         val outputApiFiles = outputApiLocations.map { location -> location.resourceFile }
44 
45         val generateResourceApi =
46             project.tasks.register(
47                 GENERATE_RESOURCE_API_TASK,
48                 GenerateResourceApiTask::class.java
49             ) { task ->
50                 task.group = "API"
51                 task.description = "Generates resource API files from source"
52                 task.builtApi.set(builtApiFile)
53                 task.apiLocation.set(builtApiLocation)
54             }
55 
56         // Policy: If the artifact has previously been released, e.g. has a beta or later API file
57         // checked in, then we must verify "release compatibility" against the work-in-progress
58         // API file.
59         val checkResourceApiRelease =
60             project.getRequiredCompatibilityApiLocation()?.let { lastReleasedApiFile ->
61                 project.tasks.register(
62                     CHECK_RESOURCE_API_RELEASE_TASK,
63                     CheckResourceApiReleaseTask::class.java
64                 ) { task ->
65                     task.referenceApiFile.set(lastReleasedApiFile.resourceFile)
66                     task.apiLocation.set(generateResourceApi.flatMap { it.apiLocation })
67                     // Since apiLocation isn't a File, we have to manually set up the dependency.
68                     task.dependsOn(generateResourceApi)
69                     task.cacheEvenIfNoOutputs()
70                 }
71             }
72 
73         // Policy: All changes to API surfaces for which compatibility is enforced must be
74         // explicitly confirmed by running the updateApi task. To enforce this, the implementation
75         // checks the "work-in-progress" built API file against the checked in current API file.
76         val checkResourceApi =
77             project.tasks.register(CHECK_RESOURCE_API_TASK, CheckResourceApiTask::class.java) { task
78                 ->
79                 task.group = TASK_GROUP_API
80                 task.description =
81                     "Checks that the resource API generated from source matches the " +
82                         "checked in resource API file"
83                 task.apiLocation.set(generateResourceApi.flatMap { it.apiLocation })
84                 // Since apiLocation isn't a File, we have to manually set up the dependency.
85                 task.dependsOn(generateResourceApi)
86                 task.cacheEvenIfNoOutputs()
87                 task.checkedInApiFiles.set(outputApiFiles)
88                 checkResourceApiRelease?.let { task.dependsOn(it) }
89             }
90 
91         val updateResourceApi =
92             project.tasks.register(UPDATE_RESOURCE_API_TASK, UpdateResourceApiTask::class.java) {
93                 task ->
94                 task.group = TASK_GROUP_API
95                 task.description =
96                     "Updates the checked in resource API files to match source code API"
97                 task.apiLocation.set(generateResourceApi.flatMap { it.apiLocation })
98                 // Since apiLocation isn't a File, we have to manually set up the dependency.
99                 task.dependsOn(generateResourceApi)
100                 task.outputApiLocations.set(outputApiLocations)
101                 task.forceUpdate.set(project.providers.gradleProperty("force").isPresent)
102                 checkResourceApiRelease?.let {
103                     // If a developer (accidentally) makes a non-backwards compatible change to an
104                     // API, the developer will want to be informed of it as soon as possible.
105                     // So, whenever a developer updates an API, if backwards compatibility checks
106                     // are
107                     // enabled in the library, then we want to check that the changes are backwards
108                     // compatible
109                     task.dependsOn(it)
110                 }
111             }
112 
113         // Ensure that this task runs as part of "updateApi" task from MetalavaTasks.
114         project.tasks.withType(UpdateApiTask::class.java).configureEach { task ->
115             task.dependsOn(updateResourceApi)
116         }
117 
118         project.addToCheckTask(checkResourceApi)
119         project.addToBuildOnServer(checkResourceApi)
120     }
121 }
122