/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Integration test project that analyzes an external dependency: see libraryToRunAgainst. // Set INTEGRATION=true to enable. import com.android.build.api.attributes.BuildTypeAttr plugins { id("com.android.library") // needed for bootClasspath and AAR transforms to work } repositories { google() mavenCentral() } // Create two configurations used in dependencies {} block. val runner: Configuration by configurations.creating val libraryToRunAgainst: Configuration by configurations.creating { isCanBeResolved = false isCanBeConsumed = false } dependencies { libraryToRunAgainst("androidx.compose.foundation:foundation:1.4.3") runner(project(":metalava")) } android { // minimal set up to make com.android.library plugin work compileSdkVersion = "android-33" namespace = "com.android.tools.metalava.integration" } /** * Configuration used to resolve source jars for projects in libraryToRunAgainst */ val sources: Configuration by configurations.creating { extendsFrom(libraryToRunAgainst) attributes { attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME)) attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.DOCUMENTATION)) attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.objects.named(DocsType.SOURCES)) attribute( LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR) ) } } fun Configuration.setResolveClasspathForUsage(usage: String) { isCanBeConsumed = false attributes { attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(usage)) attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY)) attribute(BuildTypeAttr.ATTRIBUTE, project.objects.named("release")) } extendsFrom(sources) } /** * Configuration used to resolve compile classpath for projects in libraryToRunAgainst */ val sourcesCompileClasspath: Configuration by configurations.creating { setResolveClasspathForUsage(Usage.JAVA_API) } /** * Configuration used to resolve runtime classpath for projects in libraryToRunAgainst */ val sourcesRuntimeClasspath: Configuration by configurations.creating { setResolveClasspathForUsage(Usage.JAVA_RUNTIME) } /** * Full classpath of all the dependencies needed to analyze projects in libraryToRunAgainst * This includes android.jar, compile and runtime jars */ val sourceDependencyClasspath: FileCollection = files(android.bootClasspath) + sourcesCompileClasspath.incoming.artifactView { attributes { attribute(Attribute.of("artifactType", String::class.java), "android-classes") } }.files + sourcesRuntimeClasspath.incoming.artifactView { attributes { attribute(Attribute.of("artifactType", String::class.java), "android-classes") } }.files @CacheableTask abstract class MetalavaRunner : DefaultTask() { @get:Inject abstract val execOperations: ExecOperations @get:[InputFiles PathSensitive(PathSensitivity.NONE)] abstract var sources: File @get:[InputFile PathSensitive(PathSensitivity.NONE)] abstract val apiLintBaseline: RegularFileProperty @get:Classpath abstract val dependencyClasspath: ConfigurableFileCollection @get:Classpath abstract val metalavaClasspath: ConfigurableFileCollection @get:OutputFile abstract val signatureFile: RegularFileProperty @TaskAction fun doThings() { // An approximation of AndroidX usage of metalava for tracking public // api surface of a library and running API lint against it. execOperations.javaexec { mainClass.set("com.android.tools.metalava.Driver") classpath = metalavaClasspath args = listOf( "--source-path", sources.absolutePath, "--api", signatureFile.get().asFile.absolutePath, "--classpath", dependencyClasspath.files.joinToString(File.pathSeparator), "--hide-annotation", "androidx.annotation.RestrictTo", "--show-unannotated", "--api-lint", "--baseline", apiLintBaseline.get().asFile.absolutePath, "--hide", listOf( "Enum", "StartWithLower", "MissingJvmstatic", "ArrayReturn", "UserHandleName", ).joinToString(), ) } } } interface Injected { @get:Inject val archiveOperations: ArchiveOperations } /** * A task that will extract all of the source jars that are added to libraryToRunAgainst * configuration. */ val copyInputSources = tasks.register("copyInputSources") { // Store archiveOperations into a local variable to prevent access to project object // during the task execution, as that breaks configuration caching. val archiveOperations = project.objects.newInstance().archiveOperations from( sources.incoming.artifactView { }.files.elements.map { jars -> jars.map { jar -> archiveOperations.zipTree(jar) } } ) into(layout.buildDirectory.dir("inputSources")) } tasks.register("run") { dependsOn(copyInputSources) sources = copyInputSources.get().destinationDir dependencyClasspath.from(sourceDependencyClasspath) metalavaClasspath.from(runner) signatureFile.set(layout.buildDirectory.file("current.txt")) apiLintBaseline.set(layout.projectDirectory.file("api_lint.ignore")) outputs.upToDateWhen { false } }