1 /*
<lambda>null2 * Copyright (C) 2023 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 com.android.tools.metalava.buildinfo
18
19 import com.android.tools.metalava.buildinfo.CreateAggregateLibraryBuildInfoFileTask.Companion.CREATE_AGGREGATE_BUILD_INFO_FILES_TASK
20 import com.android.tools.metalava.getDistributionDirectory
21 import com.google.gson.Gson
22 import org.gradle.api.DefaultTask
23 import org.gradle.api.Project
24 import org.gradle.api.attributes.AttributeContainer
25 import org.gradle.api.attributes.Category
26 import org.gradle.api.file.ConfigurableFileCollection
27 import org.gradle.api.tasks.InputFiles
28 import org.gradle.api.tasks.OutputFile
29 import org.gradle.api.tasks.TaskAction
30 import org.gradle.kotlin.dsl.named
31 import org.gradle.work.DisableCachingByDefault
32 import java.io.File
33
34 /** Task for a json file of all dependencies for each artifactId */
35 @DisableCachingByDefault(because = "Not worth caching")
36 abstract class CreateAggregateLibraryBuildInfoFileTask : DefaultTask() {
37 init {
38 group = "Help"
39 description = "Generates a file containing library build information serialized to json"
40 }
41
42 /** List of each build_info.txt file for each project. */
43 @get:InputFiles
44 abstract val libraryBuildInfoFiles: ConfigurableFileCollection
45
46 @OutputFile
47 val outputFile =
48 File(getDistributionDirectory(project), getAndroidxAggregateBuildInfoFilename())
49
50 private fun getAndroidxAggregateBuildInfoFilename(): String {
51 return "androidx_aggregate_build_info.txt"
52 }
53
54 private data class AllLibraryBuildInfoFiles(val artifacts: ArrayList<LibraryBuildInfoFile>)
55
56 /** Reads in file and checks that json is valid */
57 private fun jsonFileIsValid(jsonFile: File, artifactList: MutableList<String>): Boolean {
58 if (!jsonFile.exists()) {
59 return false
60 }
61 val gson = Gson()
62 val jsonString: String = jsonFile.readText(Charsets.UTF_8)
63 val aggregateBuildInfoFile = gson.fromJson(jsonString, AllLibraryBuildInfoFiles::class.java)
64 aggregateBuildInfoFile.artifacts.forEach { artifact ->
65 if (!artifactList.contains("${artifact.groupId}_${artifact.artifactId}")) {
66 println("Failed to find ${artifact.artifactId} in artifact list!")
67 return false
68 }
69 }
70 return true
71 }
72
73 /**
74 * Create the output file to contain the final complete AndroidX project build info graph file.
75 * Iterate through the list of project-specific build info files, and collects all dependencies
76 * as a JSON string. Finally, write this complete dependency graph to a text file as a json list
77 * of every project's build information
78 */
79 @TaskAction
80 fun createAndroidxAggregateBuildInfoFile() {
81 // Loop through each file in the list of libraryBuildInfoFiles and collect all build info
82 // data from each of these $groupId-$artifactId-_build_info.txt files
83 val output = StringBuilder()
84 output.append("{ \"artifacts\": [\n")
85 val artifactList = mutableListOf<String>()
86 for (infoFile in libraryBuildInfoFiles.files) {
87 if (
88 (infoFile.isFile and (infoFile.name != outputFile.name)) and
89 (infoFile.name.contains("_build_info.txt"))
90 ) {
91 val fileText: String = infoFile.readText(Charsets.UTF_8)
92 output.append("$fileText,")
93 artifactList.add(infoFile.name.replace("_build_info.txt", ""))
94 }
95 }
96 // Remove final ',' from list (so a null object doesn't get added to the end of the list)
97 output.setLength(output.length - 1)
98 output.append("]}")
99 outputFile.writeText(output.toString(), Charsets.UTF_8)
100 if (!jsonFileIsValid(outputFile, artifactList)) {
101 throw RuntimeException("JSON written to $outputFile was invalid.")
102 }
103 }
104
105 companion object {
106 const val CREATE_AGGREGATE_BUILD_INFO_FILES_TASK = "createAggregateBuildInfoFiles"
107 }
108 }
109
setBuildInfoAttributesnull110 internal fun AttributeContainer.setBuildInfoAttributes(project: Project) {
111 attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named("build-info"))
112
113 }
114
Projectnull115 fun Project.setUpAggregateBuildInfoFileTask() {
116 val buildInfoConsumer = configurations.register("buildInfoConsumer") { configuration ->
117 configuration.isCanBeConsumed = false
118 configuration.isCanBeResolved = true
119 configuration.attributes.setBuildInfoAttributes(project)
120 }
121 subprojects { subproject ->
122 buildInfoConsumer.configure {
123 it.dependencies.add(dependencies.create(subproject))
124 }
125 }
126 val buildInfoCollection = buildInfoConsumer.map {
127 it.incoming.artifactView { it.lenient(true) }.files
128 }
129 tasks.create(
130 CREATE_AGGREGATE_BUILD_INFO_FILES_TASK,
131 CreateAggregateLibraryBuildInfoFileTask::class.java
132 ) {
133 it.libraryBuildInfoFiles.from(buildInfoCollection)
134 }
135 }
136