1 /*
<lambda>null2  * Copyright 2024 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.binarycompatibilityvalidator
18 
19 import androidx.binarycompatibilityvalidator.KlibDumpParser
20 import androidx.binarycompatibilityvalidator.ParseException
21 import javax.inject.Inject
22 import org.gradle.api.DefaultTask
23 import org.gradle.api.GradleException
24 import org.gradle.api.file.ConfigurableFileCollection
25 import org.gradle.api.file.DirectoryProperty
26 import org.gradle.api.file.FileSystemOperations
27 import org.gradle.api.file.RegularFileProperty
28 import org.gradle.api.provider.ListProperty
29 import org.gradle.api.provider.Property
30 import org.gradle.api.tasks.CacheableTask
31 import org.gradle.api.tasks.Classpath
32 import org.gradle.api.tasks.Input
33 import org.gradle.api.tasks.InputFile
34 import org.gradle.api.tasks.Internal
35 import org.gradle.api.tasks.OutputDirectory
36 import org.gradle.api.tasks.PathSensitive
37 import org.gradle.api.tasks.PathSensitivity
38 import org.gradle.api.tasks.TaskAction
39 import org.gradle.workers.WorkAction
40 import org.gradle.workers.WorkParameters
41 import org.gradle.workers.WorkerExecutor
42 import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
43 
44 @CacheableTask
45 abstract class UpdateAbiTask
46 @Inject
47 constructor(@Internal protected val workerExecutor: WorkerExecutor) : DefaultTask() {
48 
49     @get:Inject abstract val fileSystemOperations: FileSystemOperations
50 
51     @get:Input abstract val version: Property<String>
52 
53     @get:Input abstract val shouldWriteVersionedApiFile: Property<Boolean>
54 
55     @get:Input abstract val unsupportedNativeTargetNames: ListProperty<String>
56 
57     /** Text file from which API signatures will be read. */
58     @get:PathSensitive(PathSensitivity.RELATIVE)
59     @get:InputFile
60     abstract val inputApiLocation: RegularFileProperty
61 
62     /** Directory to which API signatures will be written. */
63     @get:OutputDirectory abstract val outputDir: DirectoryProperty
64 
65     @get:Classpath abstract val runtimeClasspath: ConfigurableFileCollection
66 
67     @TaskAction
68     fun execute() {
69         unsupportedNativeTargetNames.get().let { targets ->
70             if (targets.isNotEmpty()) {
71                 throw GradleException(
72                     "Cannot update API files because the current host doesn't support the " +
73                         "following targets: ${targets.joinToString(", ")}"
74                 )
75             }
76         }
77         fileSystemOperations.copy {
78             it.from(inputApiLocation)
79             it.into(outputDir)
80         }
81         if (shouldWriteVersionedApiFile.get()) {
82             fileSystemOperations.copy {
83                 it.from(inputApiLocation)
84                 it.into(outputDir)
85                 it.rename(CURRENT_API_FILE_NAME, "${version.get()}.txt")
86             }
87         }
88 
89         // Execute BCV code as a WorkAction to allow setting the classpath for the action.
90         // This is to work around the kotlin compiler needing to be a compileOnly dependency for
91         // buildSrc (https://kotl.in/gradle/internal-compiler-symbols, aosp/3368960).
92         val workQueue = workerExecutor.classLoaderIsolation { it.classpath.from(runtimeClasspath) }
93         workQueue.submit(UpdateAbiWorker::class.java) { params ->
94             params.abiFile.set(outputDir.file("current.txt"))
95         }
96     }
97 }
98 
99 private interface UpdateAbiParameters : WorkParameters {
100     val abiFile: RegularFileProperty
101 }
102 
103 private abstract class UpdateAbiWorker : WorkAction<UpdateAbiParameters> {
104     @OptIn(ExperimentalLibraryAbiReader::class)
executenull105     override fun execute() {
106         try {
107             KlibDumpParser(parameters.abiFile.get().asFile).parse()
108         } catch (e: ParseException) {
109             System.err.println(
110                 "Successfully updated API file but parser was unable to parse the generated output. " +
111                     "This is a bug in the parser and should be filed to $NEW_ISSUE_URL"
112             )
113             e.printStackTrace()
114         }
115     }
116 }
117