Support fileAccessHistoryReportFile in KAPT
This change expanded KAPT to support a new param 'fileAccessHistoryReportFile', which reports all the classes used during annotation processing into a file, in the form of a list of URIs. This is useful for build speed improvements described in https://engineering.fb.com/2017/11/09/android/rethinking-android-app-compilation-with-buck/. Essentially, using the list of used classes, we can compile only the dependencies that are really affected by the developer's code changes and improve Kotlin build speed. ^KT-52853
This commit is contained in:
+2
-1
@@ -366,7 +366,8 @@ private class KaptExecution @Inject constructor(
|
|||||||
|
|
||||||
processingClassLoader,
|
processingClassLoader,
|
||||||
disableClassloaderCacheForProcessors,
|
disableClassloaderCacheForProcessors,
|
||||||
/*processorsPerfReportFile=*/null
|
/*processorsPerfReportFile=*/null,
|
||||||
|
/*fileReadHistoryReportFile*/null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
preregisterLog(context)
|
preregisterLog(context)
|
||||||
KaptJavaFileManager.preRegister(context)
|
KaptJavaFileManager.preRegister(context, options.fileReadHistoryReportFile != null)
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
preregisterTreeMaker(context)
|
preregisterTreeMaker(context)
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ class KaptOptions(
|
|||||||
val processingClassLoader: ClassLoader?,
|
val processingClassLoader: ClassLoader?,
|
||||||
//construct new classloader for these processors instead of using one defined in processingClassLoader
|
//construct new classloader for these processors instead of using one defined in processingClassLoader
|
||||||
val separateClassloaderForProcessors: Set<String>,
|
val separateClassloaderForProcessors: Set<String>,
|
||||||
val processorsStatsReportFile: File?
|
val processorsStatsReportFile: File?,
|
||||||
|
val fileReadHistoryReportFile: File?
|
||||||
) : KaptFlags {
|
) : KaptFlags {
|
||||||
override fun get(flag: KaptFlag) = flags[flag]
|
override fun get(flag: KaptFlag) = flags[flag]
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ class KaptOptions(
|
|||||||
var mode: AptMode = AptMode.WITH_COMPILATION
|
var mode: AptMode = AptMode.WITH_COMPILATION
|
||||||
var detectMemoryLeaks: DetectMemoryLeaksMode = DetectMemoryLeaksMode.DEFAULT
|
var detectMemoryLeaks: DetectMemoryLeaksMode = DetectMemoryLeaksMode.DEFAULT
|
||||||
var processorsStatsReportFile: File? = null
|
var processorsStatsReportFile: File? = null
|
||||||
|
var fileReadHistoryReportFile: File? = null
|
||||||
|
|
||||||
fun build(): KaptOptions {
|
fun build(): KaptOptions {
|
||||||
val sourcesOutputDir = this.sourcesOutputDir ?: error("'sourcesOutputDir' must be set")
|
val sourcesOutputDir = this.sourcesOutputDir ?: error("'sourcesOutputDir' must be set")
|
||||||
@@ -88,7 +90,8 @@ class KaptOptions(
|
|||||||
mode, detectMemoryLeaks,
|
mode, detectMemoryLeaks,
|
||||||
processingClassLoader = null,
|
processingClassLoader = null,
|
||||||
separateClassloaderForProcessors = emptySet(),
|
separateClassloaderForProcessors = emptySet(),
|
||||||
processorsStatsReportFile = processorsStatsReportFile
|
processorsStatsReportFile = processorsStatsReportFile,
|
||||||
|
fileReadHistoryReportFile = fileReadHistoryReportFile
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -14,6 +14,7 @@ import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
|||||||
import com.sun.tools.javac.tree.JCTree
|
import com.sun.tools.javac.tree.JCTree
|
||||||
import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
||||||
import org.jetbrains.kotlin.kapt3.base.incremental.*
|
import org.jetbrains.kotlin.kapt3.base.incremental.*
|
||||||
|
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaFileManager
|
||||||
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
|
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
|
||||||
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
|
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
|
||||||
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
||||||
@@ -25,6 +26,7 @@ import javax.annotation.processing.RoundEnvironment
|
|||||||
import javax.lang.model.SourceVersion
|
import javax.lang.model.SourceVersion
|
||||||
import javax.lang.model.element.TypeElement
|
import javax.lang.model.element.TypeElement
|
||||||
import javax.tools.JavaFileObject
|
import javax.tools.JavaFileObject
|
||||||
|
import kotlin.collections.List
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
import com.sun.tools.javac.util.List as JavacList
|
import com.sun.tools.javac.util.List as JavacList
|
||||||
|
|
||||||
@@ -124,6 +126,10 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
|
|
||||||
options.processorsStatsReportFile?.let { dumpProcessorStats(wrappedProcessors, it, logger::info) }
|
options.processorsStatsReportFile?.let { dumpProcessorStats(wrappedProcessors, it, logger::info) }
|
||||||
|
|
||||||
|
options.fileReadHistoryReportFile?.let {
|
||||||
|
dumpFileReadHistory(fileManager, it, logger::info)
|
||||||
|
}
|
||||||
|
|
||||||
if (logger.isVerbose) {
|
if (logger.isVerbose) {
|
||||||
filer.displayState()
|
filer.displayState()
|
||||||
}
|
}
|
||||||
@@ -163,6 +169,12 @@ private fun dumpProcessorStats(wrappedProcessors: List<ProcessorWrapper>, apRepo
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun dumpFileReadHistory(fileManager: KaptJavaFileManager, reportFile: File, logger: (String) -> Unit) {
|
||||||
|
logger("Dumping KAPT file read history to ${reportFile.absolutePath}")
|
||||||
|
|
||||||
|
reportFile.writeText(fileManager.renderFileReadHistory())
|
||||||
|
}
|
||||||
|
|
||||||
private fun reportIfRunningNonIncrementally(
|
private fun reportIfRunningNonIncrementally(
|
||||||
listener: MentionedTypesTaskListener?,
|
listener: MentionedTypesTaskListener?,
|
||||||
cacheManager: JavaClassCacheManager?,
|
cacheManager: JavaClassCacheManager?,
|
||||||
@@ -296,4 +308,4 @@ private fun KaptContext.initModulesIfNeeded(files: JavacList<JCTree.JCCompilatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
+88
-16
@@ -9,12 +9,15 @@ import com.sun.tools.javac.file.JavacFileManager
|
|||||||
import com.sun.tools.javac.main.Option
|
import com.sun.tools.javac.main.Option
|
||||||
import com.sun.tools.javac.util.Context
|
import com.sun.tools.javac.util.Context
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.Reader
|
||||||
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.tools.JavaFileManager
|
import javax.tools.*
|
||||||
import javax.tools.JavaFileObject
|
|
||||||
import javax.tools.StandardLocation
|
class KaptJavaFileManager(context: Context, private val shouldRecordFileRead: Boolean) : JavacFileManager(context, true, null) {
|
||||||
|
private val fileReadHistory = mutableSetOf<URI>()
|
||||||
|
|
||||||
class KaptJavaFileManager(context: Context) : JavacFileManager(context, true, null) {
|
|
||||||
fun handleOptionJavac9(option: Option, value: String) {
|
fun handleOptionJavac9(option: Option, value: String) {
|
||||||
val handleOptionMethod = JavacFileManager::class.java
|
val handleOptionMethod = JavacFileManager::class.java
|
||||||
.getMethod("handleOption", Option::class.java, String::class.java)
|
.getMethod("handleOption", Option::class.java, String::class.java)
|
||||||
@@ -34,24 +37,70 @@ class KaptJavaFileManager(context: Context) : JavacFileManager(context, true, nu
|
|||||||
): MutableIterable<JavaFileObject> {
|
): MutableIterable<JavaFileObject> {
|
||||||
val originalList = super.list(location, packageName, kinds, recurse)
|
val originalList = super.list(location, packageName, kinds, recurse)
|
||||||
|
|
||||||
if (location == null
|
val shouldPackageBeFiltered =
|
||||||
|| location != StandardLocation.CLASS_PATH
|
location != null &&
|
||||||
|| rootsToFilter.isEmpty()
|
location == StandardLocation.CLASS_PATH &&
|
||||||
|| typeToIgnore.isEmpty()
|
rootsToFilter.isNotEmpty() &&
|
||||||
|| !filterThisPath(packageName)
|
typeToIgnore.isNotEmpty() &&
|
||||||
) {
|
filterThisPath(packageName)
|
||||||
return originalList
|
|
||||||
}
|
|
||||||
|
|
||||||
val filteredList = LinkedList<JavaFileObject>()
|
val filteredList = LinkedList<JavaFileObject>()
|
||||||
for (file in originalList)
|
for (file in originalList)
|
||||||
if (!shouldBeFiltered(packageName, file)) {
|
if (!shouldPackageBeFiltered || !shouldBeFiltered(packageName, file)) {
|
||||||
filteredList.add(file)
|
filteredList.add(wrapWithReadMonitoringIfNeeded(location, file) as JavaFileObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredList
|
return filteredList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getJavaFileForInput(location: JavaFileManager.Location?, className: String?, kind: JavaFileObject.Kind?): JavaFileObject? {
|
||||||
|
val file = super.getJavaFileForInput(location, className, kind)
|
||||||
|
return wrapWithReadMonitoringIfNeeded(location, file) as JavaFileObject?
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getJavaFileForOutput(
|
||||||
|
location: JavaFileManager.Location?,
|
||||||
|
className: String?,
|
||||||
|
kind: JavaFileObject.Kind?,
|
||||||
|
sibling: FileObject?
|
||||||
|
): JavaFileObject? {
|
||||||
|
val file = super.getJavaFileForOutput(location, className, kind, sibling)
|
||||||
|
return wrapWithReadMonitoringIfNeeded(location, file) as JavaFileObject?
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFileForInput(location: JavaFileManager.Location?, packageName: String?, relativeName: String?): FileObject? {
|
||||||
|
val file = super.getFileForInput(location, packageName, relativeName)
|
||||||
|
return wrapWithReadMonitoringIfNeeded(location, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFileForOutput(
|
||||||
|
location: JavaFileManager.Location?,
|
||||||
|
packageName: String?,
|
||||||
|
relativeName: String?,
|
||||||
|
sibling: FileObject?
|
||||||
|
): FileObject? {
|
||||||
|
val file = super.getFileForOutput(location, packageName, relativeName, sibling)
|
||||||
|
return wrapWithReadMonitoringIfNeeded(location, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** javac does not play nice with wrapped file objects in this method; so we unwrap */
|
||||||
|
override fun inferBinaryName(location: JavaFileManager.Location?, file: JavaFileObject): String? =
|
||||||
|
super.inferBinaryName(location, unwrapObject(file) as JavaFileObject)
|
||||||
|
|
||||||
|
/** javac does not play nice with wrapped file objects in this method; so we unwrap */
|
||||||
|
override fun isSameFile(a: FileObject, b: FileObject): Boolean {
|
||||||
|
return super.isSameFile(unwrapObject(a), unwrapObject(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapWithReadMonitoringIfNeeded(location: JavaFileManager.Location?, file: FileObject?) =
|
||||||
|
if (shouldRecordFileRead && location != StandardLocation.ANNOTATION_PROCESSOR_PATH && file != null && file is JavaFileObject)
|
||||||
|
ReadMonitoredJavaFileObject(file)
|
||||||
|
else file
|
||||||
|
|
||||||
|
private fun unwrapObject(file: FileObject): FileObject =
|
||||||
|
if (file is ReadMonitoredJavaFileObject) file.getJavaFileObject() else file
|
||||||
|
|
||||||
|
fun renderFileReadHistory() = fileReadHistory.sorted().joinToString("\n")
|
||||||
|
|
||||||
private fun filterThisPath(packageName: String?): Boolean {
|
private fun filterThisPath(packageName: String?): Boolean {
|
||||||
packageName ?: return false
|
packageName ?: return false
|
||||||
@@ -72,9 +121,32 @@ class KaptJavaFileManager(context: Context) : JavacFileManager(context, true, nu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class ReadMonitoredJavaFileObject(innerFile: JavaFileObject) : ForwardingJavaFileObject<JavaFileObject>(innerFile) {
|
||||||
|
fun getJavaFileObject(): JavaFileObject = fileObject
|
||||||
|
override fun toString(): String = fileObject.name
|
||||||
|
override fun openInputStream(): InputStream {
|
||||||
|
recordFileRead()
|
||||||
|
return super.openInputStream()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openReader(ignoreEncodingErrors: Boolean): Reader {
|
||||||
|
recordFileRead()
|
||||||
|
return super.openReader(ignoreEncodingErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence {
|
||||||
|
recordFileRead()
|
||||||
|
return super.getCharContent(ignoreEncodingErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordFileRead() {
|
||||||
|
fileReadHistory.add(fileObject.toUri())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
internal fun preRegister(context: Context) {
|
internal fun preRegister(context: Context, shouldRecordFileRead: Boolean) {
|
||||||
context.put(JavaFileManager::class.java, Context.Factory<JavaFileManager> { KaptJavaFileManager(it) })
|
context.put(JavaFileManager::class.java, Context.Factory { KaptJavaFileManager(it, shouldRecordFileRead) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,12 @@ class JavaKaptContextTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doAnnotationProcessing(javaSourceFile: File, processor: IncrementalProcessor, outputDir: File) {
|
private fun doAnnotationProcessing(
|
||||||
|
javaSourceFile: File,
|
||||||
|
processor: IncrementalProcessor,
|
||||||
|
outputDir: File,
|
||||||
|
fileReadOutput: File? = null
|
||||||
|
) {
|
||||||
val options = KaptOptions.Builder().apply {
|
val options = KaptOptions.Builder().apply {
|
||||||
projectBaseDir = javaSourceFile.parentFile
|
projectBaseDir = javaSourceFile.parentFile
|
||||||
|
|
||||||
@@ -67,6 +72,8 @@ class JavaKaptContextTest {
|
|||||||
stubsOutputDir = outputDir
|
stubsOutputDir = outputDir
|
||||||
incrementalDataOutputDir = outputDir
|
incrementalDataOutputDir = outputDir
|
||||||
|
|
||||||
|
fileReadHistoryReportFile = fileReadOutput
|
||||||
|
|
||||||
flags.add(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS)
|
flags.add(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS)
|
||||||
detectMemoryLeaks = DetectMemoryLeaksMode.NONE
|
detectMemoryLeaks = DetectMemoryLeaksMode.NONE
|
||||||
}.build()
|
}.build()
|
||||||
@@ -86,6 +93,26 @@ class JavaKaptContextTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDumpFileReadHistory() {
|
||||||
|
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
|
||||||
|
val fileReadOutputFile = File.createTempFile("kapt_read_history", ".txt")
|
||||||
|
try {
|
||||||
|
doAnnotationProcessing(
|
||||||
|
File(TEST_DATA_DIR, "Simple.java"),
|
||||||
|
simpleProcessor(),
|
||||||
|
sourceOutputDir,
|
||||||
|
fileReadOutput = fileReadOutputFile
|
||||||
|
)
|
||||||
|
assertTrue(fileReadOutputFile.exists())
|
||||||
|
assertTrue(fileReadOutputFile.readText().contains("generated/MyMethodMyAnnotation.java"))
|
||||||
|
assertTrue(fileReadOutputFile.readText().contains("java/lang/Enum.class"))
|
||||||
|
} finally {
|
||||||
|
sourceOutputDir.deleteRecursively()
|
||||||
|
fileReadOutputFile.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testException() {
|
fun testException() {
|
||||||
val exceptionMessage = "Here we are!"
|
val exceptionMessage = "Here we are!"
|
||||||
|
|||||||
@@ -187,6 +187,13 @@ enum class KaptCliOption(
|
|||||||
cliToolOption = CliToolOption("-Kapt-dump-processor-stats", VALUE)
|
cliToolOption = CliToolOption("-Kapt-dump-processor-stats", VALUE)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
DUMP_FILE_READ_HISTORY(
|
||||||
|
"dumpFileReadHistory",
|
||||||
|
"<path>",
|
||||||
|
"Dump list of files read by processors to the specified file",
|
||||||
|
cliToolOption = CliToolOption("-Kapt-dump-file-read-history", VALUE)
|
||||||
|
),
|
||||||
|
|
||||||
STRICT_MODE_OPTION(
|
STRICT_MODE_OPTION(
|
||||||
"strict",
|
"strict",
|
||||||
"true | false",
|
"true | false",
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class Kapt3CommandLineProcessor : CommandLineProcessor {
|
|||||||
|
|
||||||
SHOW_PROCESSOR_STATS -> setFlag(KaptFlag.SHOW_PROCESSOR_STATS, value)
|
SHOW_PROCESSOR_STATS -> setFlag(KaptFlag.SHOW_PROCESSOR_STATS, value)
|
||||||
DUMP_PROCESSOR_STATS -> processorsStatsReportFile = File(value)
|
DUMP_PROCESSOR_STATS -> processorsStatsReportFile = File(value)
|
||||||
|
DUMP_FILE_READ_HISTORY -> fileReadHistoryReportFile = File(value)
|
||||||
INCLUDE_COMPILE_CLASSPATH -> setFlag(KaptFlag.INCLUDE_COMPILE_CLASSPATH, value)
|
INCLUDE_COMPILE_CLASSPATH -> setFlag(KaptFlag.INCLUDE_COMPILE_CLASSPATH, value)
|
||||||
|
|
||||||
DETECT_MEMORY_LEAKS_OPTION -> setSelector(enumValues<DetectMemoryLeaksMode>(), value) { detectMemoryLeaks = it }
|
DETECT_MEMORY_LEAKS_OPTION -> setSelector(enumValues<DetectMemoryLeaksMode>(), value) { detectMemoryLeaks = it }
|
||||||
|
|||||||
Reference in New Issue
Block a user