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 java.io.File
23 import org.gradle.api.DefaultTask
24 import org.gradle.api.Project
25 import org.gradle.api.provider.ListProperty
26 import org.gradle.api.provider.Provider
27 import org.gradle.api.tasks.Input
28 import org.gradle.api.tasks.OutputFile
29 import org.gradle.api.tasks.TaskAction
30 import org.gradle.work.DisableCachingByDefault
31 
32 /** Task for a json file of all dependencies for each artifactId */
33 @DisableCachingByDefault(because = "Not worth caching")
34 abstract class CreateAggregateLibraryBuildInfoFileTask : DefaultTask() {
35     init {
36         group = "Help"
37         description = "Generates a file containing library build information serialized to json"
38     }
39 
40     /** List of each build_info.txt file for each project. */
41     @get:Input abstract val libraryBuildInfoFiles: ListProperty<File>
42 
43     @OutputFile
44     val outputFile =
45         File(getDistributionDirectory(project), getAndroidxAggregateBuildInfoFilename())
46 
47     private fun getAndroidxAggregateBuildInfoFilename(): String {
48         return "androidx_aggregate_build_info.txt"
49     }
50 
51     private data class AllLibraryBuildInfoFiles(val artifacts: ArrayList<LibraryBuildInfoFile>)
52 
53     /** Reads in file and checks that json is valid */
54     private fun jsonFileIsValid(jsonFile: File, artifactList: MutableList<String>): Boolean {
55         if (!jsonFile.exists()) {
56             return false
57         }
58         val gson = Gson()
59         val jsonString: String = jsonFile.readText(Charsets.UTF_8)
60         val aggregateBuildInfoFile = gson.fromJson(jsonString, AllLibraryBuildInfoFiles::class.java)
61         aggregateBuildInfoFile.artifacts.forEach { artifact ->
62             if (!artifactList.contains("${artifact.groupId}_${artifact.artifactId}")) {
63                 println("Failed to find ${artifact.artifactId} in artifact list!")
64                 return false
65             }
66         }
67         return true
68     }
69 
70     /**
71      * Create the output file to contain the final complete AndroidX project build info graph file.
72      * Iterate through the list of project-specific build info files, and collects all dependencies
73      * as a JSON string. Finally, write this complete dependency graph to a text file as a json list
74      * of every project's build information
75      */
76     @TaskAction
77     fun createAndroidxAggregateBuildInfoFile() {
78         // Loop through each file in the list of libraryBuildInfoFiles and collect all build info
79         // data from each of these $groupId-$artifactId-_build_info.txt files
80         val output = StringBuilder()
81         output.append("{ \"artifacts\": [\n")
82         val artifactList = mutableListOf<String>()
83         for (infoFile in libraryBuildInfoFiles.get()) {
84             if (
85                 (infoFile.isFile and (infoFile.name != outputFile.name)) and
86                     (infoFile.name.contains("_build_info.txt"))
87             ) {
88                 val fileText: String = infoFile.readText(Charsets.UTF_8)
89                 output.append("$fileText,")
90                 artifactList.add(infoFile.name.replace("_build_info.txt", ""))
91             }
92         }
93         // Remove final ',' from list (so a null object doesn't get added to the end of the list)
94         output.setLength(output.length - 1)
95         output.append("]}")
96         outputFile.writeText(output.toString(), Charsets.UTF_8)
97         if (!jsonFileIsValid(outputFile, artifactList)) {
98             throw RuntimeException("JSON written to $outputFile was invalid.")
99         }
100     }
101 
102     companion object {
103         const val CREATE_AGGREGATE_BUILD_INFO_FILES_TASK = "createAggregateBuildInfoFiles"
104     }
105 }
106 
Projectnull107 fun Project.addTaskToAggregateBuildInfoFileTask(task: Provider<CreateLibraryBuildInfoTask>) {
108     rootProject.tasks.named(CREATE_AGGREGATE_BUILD_INFO_FILES_TASK).configure {
109         val aggregateLibraryBuildInfoFileTask = it as CreateAggregateLibraryBuildInfoFileTask
110         aggregateLibraryBuildInfoFileTask.libraryBuildInfoFiles.add(
111             task.flatMap { task -> task.outputFile }
112         )
113     }
114 }
115