1 /* 2 * Copyright (C) 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 com.android.tools.metalava.jar 18 19 import com.android.tools.metalava.ApiAnalyzer 20 import com.android.tools.metalava.ProgressTracker 21 import com.android.tools.metalava.model.Codebase 22 import com.android.tools.metalava.model.annotation.DefaultAnnotationManager 23 import com.android.tools.metalava.model.source.EnvironmentManager 24 import com.android.tools.metalava.model.source.SourceModelProvider 25 import com.android.tools.metalava.model.source.SourceParser 26 import com.android.tools.metalava.model.visitors.ApiPredicate 27 import com.android.tools.metalava.reporter.Reporter 28 import java.io.Closeable 29 import java.io.File 30 31 /** Provides support for loading [Codebase]s from jar files. */ 32 sealed interface JarCodebaseLoader { 33 34 /** Load a [Codebase] from a jar file. */ loadFromJarFilenull35 fun loadFromJarFile( 36 apiJar: File, 37 apiAnalyzerConfig: ApiAnalyzer.Config = ApiAnalyzer.Config(), 38 freezeCodebase: Boolean = true, 39 classPath: List<File> = emptyList(), 40 ): Codebase 41 42 companion object { 43 /** Create an instance fo [JarCodebaseLoader] from an existing [SourceParser]. */ 44 fun createForSourceParser( 45 progressTracker: ProgressTracker, 46 reporter: Reporter, 47 sourceParser: SourceParser, 48 ): JarCodebaseLoader { 49 return FromSourceParser(progressTracker, reporter, sourceParser) 50 } 51 } 52 53 /** A [JarCodebaseLoader] created from an existing [SourceParser]. */ 54 private class FromSourceParser( 55 private val progressTracker: ProgressTracker, 56 private val reporter: Reporter, 57 private val sourceParser: SourceParser, 58 ) : JarCodebaseLoader { loadFromJarFilenull59 override fun loadFromJarFile( 60 apiJar: File, 61 apiAnalyzerConfig: ApiAnalyzer.Config, 62 freezeCodebase: Boolean, 63 classPath: List<File>, 64 ): Codebase { 65 progressTracker.progress("Processing jar file: ") 66 67 val apiPredicateConfig = apiAnalyzerConfig.apiPredicateConfig 68 val apiEmit = 69 ApiPredicate( 70 config = apiPredicateConfig.copy(ignoreShown = true), 71 ) 72 val apiReference = apiEmit 73 74 val codebase = sourceParser.loadFromJar(apiJar, classPath) 75 val analyzer = ApiAnalyzer(sourceParser, codebase, reporter, apiAnalyzerConfig) 76 analyzer.mergeExternalInclusionAnnotations() 77 analyzer.computeApi() 78 analyzer.mergeExternalQualifierAnnotations() 79 analyzer.generateInheritedStubs(apiEmit, apiReference) 80 81 if (freezeCodebase) { 82 // Prevent the codebase from being mutated. 83 codebase.freezeClasses() 84 } 85 86 return codebase 87 } 88 } 89 } 90 91 /** 92 * A [JarCodebaseLoader] that owns its own [EnvironmentManager] and supports releasing its resources 93 * through the [close] method. 94 */ 95 class StandaloneJarCodebaseLoader 96 private constructor( 97 /** 98 * The [EnvironmentManager] that created the [SourceParser] that is used to read the jar files. 99 */ 100 private val environmentManager: EnvironmentManager, 101 102 /** The underlying [JarCodebaseLoader] to which this will delegate the [loadFromJarFile]. */ 103 private val delegate: JarCodebaseLoader, 104 ) : Closeable, JarCodebaseLoader by delegate { 105 106 /** Free up any resources held by [environmentManager]. */ closenull107 override fun close() { 108 environmentManager.close() 109 } 110 111 companion object { 112 /** 113 * Create a [StandaloneJarCodebaseLoader]. 114 * 115 * The caller must ensure that the [close] method is called after this has been finished 116 * with to ensure prompt release of resources, e.g. using `...use { jarCodebaseLoader -> }`. 117 */ createnull118 fun create( 119 disableStderrDumping: Boolean, 120 progressTracker: ProgressTracker, 121 reporter: Reporter, 122 sourceModelProvider: SourceModelProvider = SourceModelProvider.getImplementation("psi"), 123 ): StandaloneJarCodebaseLoader { 124 125 val environmentManager = 126 sourceModelProvider.createEnvironmentManager( 127 disableStderrDumping, 128 ) 129 130 val annotationManager = DefaultAnnotationManager() 131 val codebaseConfig = 132 Codebase.Config( 133 annotationManager = annotationManager, 134 reporter = reporter, 135 ) 136 137 val sourceParser = 138 environmentManager.createSourceParser( 139 codebaseConfig, 140 ) 141 142 val jarLoader = 143 JarCodebaseLoader.createForSourceParser( 144 progressTracker, 145 reporter, 146 sourceParser, 147 ) 148 149 return StandaloneJarCodebaseLoader(environmentManager, jarLoader) 150 } 151 } 152 } 153