1 /* 2 * Copyright 2018 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.metalava 18 19 import androidx.build.checkapi.ApiBaselinesLocation 20 import androidx.build.checkapi.SourceSetInputs 21 import java.io.File 22 import javax.inject.Inject 23 import org.gradle.api.DefaultTask 24 import org.gradle.api.file.ConfigurableFileCollection 25 import org.gradle.api.file.FileCollection 26 import org.gradle.api.file.RegularFileProperty 27 import org.gradle.api.provider.ListProperty 28 import org.gradle.api.provider.Property 29 import org.gradle.api.tasks.CacheableTask 30 import org.gradle.api.tasks.Classpath 31 import org.gradle.api.tasks.Input 32 import org.gradle.api.tasks.InputFile 33 import org.gradle.api.tasks.Internal 34 import org.gradle.api.tasks.Optional 35 import org.gradle.api.tasks.PathSensitive 36 import org.gradle.api.tasks.PathSensitivity 37 import org.gradle.workers.WorkerExecutor 38 import org.jetbrains.kotlin.gradle.dsl.KotlinVersion 39 40 /** Base class for invoking Metalava. */ 41 @CacheableTask 42 abstract class MetalavaTask 43 @Inject 44 constructor(@Internal protected val workerExecutor: WorkerExecutor) : DefaultTask() { 45 /** Classpath containing Metalava and its dependencies. */ 46 @get:Classpath abstract val metalavaClasspath: ConfigurableFileCollection 47 48 /** Android's boot classpath */ 49 @get:Classpath lateinit var bootClasspath: FileCollection 50 51 /** Dependencies (compiled classes) of the project. */ 52 @get:Classpath lateinit var dependencyClasspath: FileCollection 53 54 @get:Input abstract val k2UastEnabled: Property<Boolean> 55 56 @get:Input abstract val kotlinSourceLevel: Property<KotlinVersion> 57 runWithArgsnull58 fun runWithArgs(args: List<String>) { 59 runMetalavaWithArgs( 60 metalavaClasspath, 61 args, 62 k2UastEnabled.get(), 63 kotlinSourceLevel.get(), 64 workerExecutor 65 ) 66 } 67 } 68 69 /** A metalava task that takes source code as input (other tasks take signature files). */ 70 @CacheableTask 71 internal abstract class SourceMetalavaTask(workerExecutor: WorkerExecutor) : 72 MetalavaTask(workerExecutor) { 73 /** 74 * Specifies both the source files and their corresponding compiled class files 75 * 76 * We specify the source files to pass to Metalava because that's the format that Metalava 77 * needs. 78 * 79 * However, Metalava is only supposed to read the public API, so we don't need to rerun Metalava 80 * if no API changes occurred. 81 * 82 * Gradle doesn't offer all of the same abilities as Metalava for writing a signature file and 83 * validating its compatibility, but Gradle does offer the ability to check whether two sets of 84 * classes have the same API. 85 * 86 * So, we ask Gradle to rerun this task only if the public API changes, which we implement by 87 * declaring the compiled classes as inputs rather than the sources 88 */ 89 /** Source files against which API signatures will be validated. */ 90 @get:Internal // UP-TO-DATE checking is done based on the compiled classes 91 var sourcePaths: FileCollection = project.files() 92 93 /** Class files compiled from sourcePaths */ 94 @get:Classpath var compiledSources: FileCollection = project.files() 95 96 @get:[Optional InputFile PathSensitive(PathSensitivity.NONE)] 97 abstract val manifestPath: RegularFileProperty 98 99 @get:Internal // already expressed by getApiLintBaseline() 100 abstract val baselines: Property<ApiBaselinesLocation> 101 102 @Optional 103 @PathSensitive(PathSensitivity.NONE) 104 @InputFile getInputApiLintBaselinenull105 fun getInputApiLintBaseline(): File? { 106 val baseline = baselines.get().apiLintFile 107 return if (baseline.exists()) baseline else null 108 } 109 110 @get:Input abstract val targetsJavaConsumers: Property<Boolean> 111 112 /** 113 * Information about all source sets for multiplatform projects. Non-multiplatform projects 114 * should be represented as a list with one source set. 115 * 116 * This is marked as [Internal] because [compiledSources] is what should determine whether to 117 * rerun metalava. 118 */ 119 @get:Internal abstract val sourceSets: ListProperty<SourceSetInputs> 120 121 /** 122 * Creates an XML file representing the project structure. 123 * 124 * This should only be called during task execution. 125 */ createProjectXmlFilenull126 protected fun createProjectXmlFile(): File { 127 val sourceSets = sourceSets.get() 128 check(sourceSets.isNotEmpty()) { "Project must have at least one source set." } 129 val outputFile = File(temporaryDir, "project.xml") 130 ProjectXml.create( 131 sourceSets, 132 bootClasspath.files, 133 compiledSources.singleFile, 134 outputFile, 135 ) 136 return outputFile 137 } 138 } 139