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 package androidx.build.kythe 17 18 import androidx.build.addToBuildOnServer 19 import androidx.build.checkapi.CompilationInputs 20 import androidx.build.getCheckoutRoot 21 import androidx.build.getPrebuiltsRoot 22 import java.io.File 23 import javax.inject.Inject 24 import org.gradle.api.DefaultTask 25 import org.gradle.api.Project 26 import org.gradle.api.artifacts.Configuration 27 import org.gradle.api.file.ConfigurableFileCollection 28 import org.gradle.api.file.DirectoryProperty 29 import org.gradle.api.file.RegularFileProperty 30 import org.gradle.api.provider.ListProperty 31 import org.gradle.api.tasks.CacheableTask 32 import org.gradle.api.tasks.Classpath 33 import org.gradle.api.tasks.Input 34 import org.gradle.api.tasks.InputFile 35 import org.gradle.api.tasks.InputFiles 36 import org.gradle.api.tasks.Internal 37 import org.gradle.api.tasks.OutputDirectory 38 import org.gradle.api.tasks.OutputFile 39 import org.gradle.api.tasks.PathSensitive 40 import org.gradle.api.tasks.PathSensitivity 41 import org.gradle.api.tasks.TaskAction 42 import org.gradle.api.tasks.compile.JavaCompile 43 import org.gradle.process.ExecOperations 44 45 /** Generates kzip files that are used to index the Java source code in Kythe. */ 46 @CacheableTask 47 abstract class GenerateJavaKzipTask 48 @Inject 49 constructor(private val execOperations: ExecOperations) : DefaultTask() { 50 51 /** Must be run in the checkout root so as to be free of relative markers */ 52 @get:Internal val checkoutRoot: File = project.getCheckoutRoot() 53 54 @get:InputFile 55 @get:PathSensitive(PathSensitivity.NONE) 56 abstract val javaExtractorJar: RegularFileProperty 57 58 @get:InputFiles 59 @get:PathSensitive(PathSensitivity.RELATIVE) 60 abstract val sourcePaths: ConfigurableFileCollection 61 62 @get:Input abstract val javacCompilerArgs: ListProperty<String> 63 64 /** Path to `vnames.json` file, used for name mappings within Kythe. */ 65 @get:InputFiles 66 @get:PathSensitive(PathSensitivity.NONE) 67 abstract val vnamesJson: RegularFileProperty 68 69 @get:Classpath abstract val dependencyClasspath: ConfigurableFileCollection 70 71 @get:Classpath abstract val compiledSources: ConfigurableFileCollection 72 73 @get:Classpath abstract val annotationProcessor: ConfigurableFileCollection 74 75 @get:OutputFile abstract val kzipOutputFile: RegularFileProperty 76 77 @get:OutputDirectory abstract val kytheBuildDirectory: DirectoryProperty 78 79 @TaskAction 80 fun exec() { 81 val sourceFiles = 82 sourcePaths.asFileTree.files 83 .filter { it.extension == "java" } 84 .map { it.relativeTo(checkoutRoot) } 85 86 if (sourceFiles.isEmpty()) { 87 return 88 } 89 90 val dependencyClasspath = 91 dependencyClasspath 92 .filter { it.extension == "jar" } 93 .let { filteredClasspath -> 94 if (sourcePaths.asFileTree.files.any { it.extension == "kt" }) { 95 filteredClasspath + compiledSources 96 } else { 97 filteredClasspath 98 } 99 } 100 101 val kytheBuildDirectory = kytheBuildDirectory.get().asFile.apply { mkdirs() } 102 103 execOperations.javaexec { 104 it.mainClass.set("-jar") 105 it.args(javaExtractorJar.get().asFile) 106 it.args("--class-path", dependencyClasspath.joinToString(":")) 107 it.args("--processor-path", annotationProcessor.joinToString(":")) 108 it.args(javacCompilerArgs.get()) 109 it.args("-d", kytheBuildDirectory) 110 it.args(sourceFiles) 111 it.jvmArgs( 112 // Without all these flags, the extractor fails to run. Copied from: 113 // https://github.com/kythe/kythe/blob/v0.0.67/kythe/release/release.BUILD#L99-L106 114 "--add-opens=java.base/java.nio=ALL-UNNAMED", 115 "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", 116 "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", 117 "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", 118 "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", 119 "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", 120 "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", 121 "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", 122 "--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED" 123 ) 124 it.environment("KYTHE_CORPUS", ANDROIDX_CORPUS) 125 it.environment("KYTHE_KZIP_ENCODING", "proto") 126 it.environment( 127 "KYTHE_OUTPUT_FILE", 128 kzipOutputFile.get().asFile.relativeTo(checkoutRoot).path 129 ) 130 it.environment("KYTHE_ROOT_DIRECTORY", checkoutRoot.path) 131 it.environment("KYTHE_VNAMES", vnamesJson.get().asFile.path) 132 it.workingDir = checkoutRoot 133 } 134 } 135 136 internal companion object { 137 fun setupProject( 138 project: Project, 139 compilationInputs: CompilationInputs, 140 compiledSources: Configuration, 141 ) { 142 val annotationProcessorPaths = 143 project.objects.fileCollection().apply { 144 project.tasks.withType(JavaCompile::class.java).configureEach { 145 it.options.annotationProcessorPath?.let { path -> from(path) } 146 } 147 } 148 149 val javacCompilerArgs = 150 project.objects.listProperty(String::class.java).apply { 151 project.tasks.withType(JavaCompile::class.java).configureEach { 152 addAll(it.options.compilerArgs) 153 } 154 } 155 156 project.tasks 157 .register("generateJavaKzip", GenerateJavaKzipTask::class.java) { task -> 158 task.apply { 159 javaExtractorJar.set( 160 File( 161 project.getPrebuiltsRoot(), 162 "build-tools/common/javac_extractor.jar" 163 ) 164 ) 165 sourcePaths.setFrom(compilationInputs.sourcePaths) 166 vnamesJson.set(project.getVnamesJson()) 167 dependencyClasspath.setFrom( 168 compilationInputs.dependencyClasspath + compilationInputs.bootClasspath 169 ) 170 this.compiledSources.setFrom(compiledSources) 171 kzipOutputFile.set( 172 project.layout.buildDirectory.file( 173 "kzips/${project.group}-${project.name}.java.kzip" 174 ) 175 ) 176 kytheBuildDirectory.set( 177 project.layout.buildDirectory.dir("kythe-java-classes") 178 ) 179 annotationProcessor.setFrom(annotationProcessorPaths) 180 this.javacCompilerArgs.set(javacCompilerArgs) 181 // Needed so generated files (e.g. protos) are present when generating kzip 182 // Without this, javac_extractor will throw a compilation error 183 dependsOn(project.tasks.withType(JavaCompile::class.java)) 184 } 185 } 186 .also { project.addToBuildOnServer(it) } 187 } 188 } 189 } 190