1 /*
<lambda>null2 * Copyright (C) 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 com.android.tools.metalava
18
19 import com.android.tools.metalava.LibraryBuildInfoFile.Check
20 import com.google.gson.GsonBuilder
21 import org.gradle.api.DefaultTask
22 import org.gradle.api.GradleException
23 import org.gradle.api.Project
24 import org.gradle.api.provider.Property
25 import org.gradle.api.tasks.Input
26 import org.gradle.api.tasks.OutputFile
27 import org.gradle.api.tasks.TaskAction
28 import org.gradle.api.tasks.TaskProvider
29 import org.gradle.api.tasks.bundling.Zip
30 import java.io.File
31 import java.util.concurrent.TimeUnit
32
33 const val CREATE_BUILD_INFO_TASK = "createBuildInfo"
34
35 abstract class CreateLibraryBuildInfoTask : DefaultTask() {
36 @get:Input
37 abstract val artifactId: Property<String>
38 @get:Input
39 abstract val groupId: Property<String>
40 @get:Input
41 abstract val version: Property<String>
42 @get:Input
43 abstract val sha: Property<String>
44 @get:Input
45 abstract val projectZipPath: Property<String>
46
47 @get:OutputFile
48 abstract val outputFile: Property<File>
49
50 @get:OutputFile
51 abstract val aggregateOutputFile: Property<File>
52
53 @TaskAction
54 fun createFile() {
55 val info = LibraryBuildInfoFile()
56 info.artifactId = artifactId.get()
57 info.groupId = groupId.get()
58 info.groupIdRequiresSameVersion = false
59 info.version = version.get()
60 info.path = "/"
61 info.sha = sha.get()
62 info.projectZipPath = projectZipPath.get()
63 info.dependencies = arrayListOf()
64 info.checks = arrayListOf()
65 val gson = GsonBuilder().setPrettyPrinting().create()
66 val serializedInfo: String = gson.toJson(info)
67 outputFile.get().writeText(serializedInfo)
68
69 aggregateOutputFile.get().let {
70 it.writeText("{ \"artifacts\": [\n")
71 it.appendText(serializedInfo)
72 it.appendText("]}")
73 }
74 }
75 }
76
configureBuildInfoTasknull77 fun configureBuildInfoTask(
78 project: Project,
79 inCI: Boolean,
80 distributionDirectory: File,
81 archiveTaskProvider: TaskProvider<Zip>
82 ): TaskProvider<CreateLibraryBuildInfoTask> {
83 return project.tasks.register(CREATE_BUILD_INFO_TASK, CreateLibraryBuildInfoTask::class.java) {
84 it.artifactId.set(project.provider { project.name })
85 it.groupId.set(project.provider { project.group as String })
86 it.version.set(project.provider { project.version as String })
87 // Only set sha when in CI to keep local builds faster
88 it.sha.set(project.provider { if (inCI) getGitSha(project.projectDir) else "" })
89 it.projectZipPath.set(archiveTaskProvider.flatMap { task -> task.archiveFileName })
90 it.outputFile.set(project.provider {
91 File(
92 distributionDirectory,
93 "build-info/${project.group}_${project.name}_build_info.txt"
94 )
95 })
96 it.aggregateOutputFile.set(project.provider {
97 File(distributionDirectory, "androidx_aggregate_build_info.txt")}
98 )
99 }
100 }
101
getGitShanull102 fun getGitSha(directory: File): String {
103 val process = ProcessBuilder("git", "rev-parse", "--verify", "HEAD")
104 .directory(directory)
105 .redirectOutput(ProcessBuilder.Redirect.PIPE)
106 .redirectError(ProcessBuilder.Redirect.PIPE)
107 .start()
108 // Read output, waiting for process to finish, as needed
109 val stdout = process.inputStream.bufferedReader().readText()
110 val stderr = process.errorStream.bufferedReader().readText()
111 val message = stdout + stderr
112 // wait potentially a little bit longer in case Git was waiting for us to
113 // read its response before it exited
114 process.waitFor(10, TimeUnit.SECONDS)
115 if (stderr != "") {
116 throw GradleException("Unable to call git. Response was: $message")
117 }
118 check(process.exitValue() == 0) { "Nonzero exit value running git command." }
119 return stdout.trim()
120 }
121
122 /**
123 * Object outlining the format of a library's build info file.
124 * This object will be serialized to json.
125 * This file should match the corresponding class in Jetpad because
126 * this object will be serialized to json and the result will be parsed by Jetpad.
127 * DO NOT TOUCH.
128 *
129 * @property groupId library maven group Id
130 * @property artifactId library maven artifact Id
131 * @property version library maven version
132 * @property path local project directory path used for development, rooted at framework/support
133 * @property sha the sha of the latest commit to modify the library (aka a commit that
134 * touches a file within [path])
135 * @property groupIdRequiresSameVersion boolean that determines if all libraries with [groupId]
136 * have the same version
137 * @property dependencies a list of dependencies on other androidx libraries
138 * @property checks arraylist of [Check]s that is used by Jetpad
139 */
140 @Suppress("UNUSED")
141 class LibraryBuildInfoFile {
142 var groupId: String? = null
143 var artifactId: String? = null
144 var version: String? = null
145 var path: String? = null
146 var sha: String? = null
147 var projectZipPath: String? = null
148 var groupIdRequiresSameVersion: Boolean? = null
149 var dependencies: ArrayList<Dependency> = arrayListOf()
150 var checks: ArrayList<Check> = arrayListOf()
151
152 /**
153 * @property isTipOfTree boolean that specifies whether the dependency is tip-of-tree
154 */
155 inner class Dependency {
156 var groupId: String? = null
157 var artifactId: String? = null
158 var version: String? = null
159 var isTipOfTree = false
160 }
161
162 inner class Check {
163 var name: String? = null
164 var passing = false
165 }
166 }