diff options
author | Jiaxiang Chen <jiaxiang@google.com> | 2022-03-21 22:41:44 -0700 |
---|---|---|
committer | Jiaxiang Chen <roaringacw@gmail.com> | 2022-03-28 19:06:44 -0700 |
commit | 6c906beef9720a431343914c523d700af9c510a6 (patch) | |
tree | 8e6cabc439b3466232d5dba625eae94bc5b14d7d /kotlin-analysis-api | |
parent | d6f2b6eef0b466a35fc4b8e1e14ee8ef05cf03e6 (diff) | |
download | ksp-6c906beef9720a431343914c523d700af9c510a6.tar.gz |
* Extract CodeGeneratorImpl, PlatformInfoImpl into common-util module, add copyright to common-util module classes.
* Add KSP Command line processor and logger.
* Implement KSP main execution and initialization logic.
Diffstat (limited to 'kotlin-analysis-api')
5 files changed, 227 insertions, 62 deletions
diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/CommandLineKSPLogger.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/CommandLineKSPLogger.kt new file mode 100644 index 00000000..223e2db2 --- /dev/null +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/CommandLineKSPLogger.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2022 Google LLC + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * 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. + */ + +package com.google.devtools.ksp.impl + +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.symbol.KSNode + +class CommandLineKSPLogger : KSPLogger { + // TODO: support logging level. + val messager = System.err + override fun logging(message: String, symbol: KSNode?) { + messager.println(message) + } + + override fun info(message: String, symbol: KSNode?) { + messager.println(message) + } + + override fun warn(message: String, symbol: KSNode?) { + messager.println(message) + } + + override fun error(message: String, symbol: KSNode?) { + messager.println(message) + } + + override fun exception(e: Throwable) { + messager.println(e.message) + } +} diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KSPCommandLineProcessor.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KSPCommandLineProcessor.kt new file mode 100644 index 00000000..6b8676fd --- /dev/null +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KSPCommandLineProcessor.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Google LLC + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * 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. + */ + +package com.google.devtools.ksp.impl + +import com.google.devtools.ksp.KspOptions +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots +import org.jetbrains.kotlin.config.CompilerConfiguration +import java.io.File +import java.nio.file.Files + +class KSPCommandLineProcessor(val args: Array<String>) { + val compilerConfiguration = CompilerConfiguration() + // TODO: support KSP options + val sources = args.toList() + lateinit var kspOptions: KspOptions + + val ktFiles = sources + .map { File(it) } + .sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first + .flatMap { root -> root.walk().filter { it.isFile && it.extension == "kt" }.toList() } + .sortedBy { Files.isSymbolicLink(it.toPath()) } + .distinctBy { it.canonicalPath } + + val javaFiles = sources + .map { File(it) } + .sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first + .flatMap { root -> root.walk().filter { it.isFile && it.extension == "java" }.toList() } + .sortedBy { Files.isSymbolicLink(it.toPath()) } + .distinctBy { it.canonicalPath } + + init { + compilerConfiguration.addKotlinSourceRoots(ktFiles.map { it.absolutePath }) + compilerConfiguration.addJavaSourceRoots(javaFiles) + compilerConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) + + val kspOptionsBuilder = KspOptions.Builder() + kspOptions = kspOptionsBuilder.build() + } +} diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt index 2045102f..38f60eb2 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt @@ -1,5 +1,12 @@ package com.google.devtools.ksp.impl +import com.google.devtools.ksp.AnyChanges +import com.google.devtools.ksp.KspOptions +import com.google.devtools.ksp.impl.symbol.kotlin.KSFileImpl +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.processing.impl.CodeGeneratorImpl +import com.google.devtools.ksp.processing.impl.JvmPlatformInfoImpl +import com.google.devtools.ksp.symbol.KSAnnotated import com.intellij.mock.MockApplication import com.intellij.mock.MockProject import com.intellij.openapi.application.ApplicationManager @@ -8,66 +15,112 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem import com.intellij.psi.PsiManager -import org.jetbrains.kotlin.analysis.api.analyseWithReadAction import org.jetbrains.kotlin.analysis.api.standalone.configureApplicationEnvironment import org.jetbrains.kotlin.analysis.api.standalone.configureProjectEnvironment -import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots -import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.plugins.ServiceLoaderLite import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.psi.KtFile -import java.io.File -import java.nio.file.Files +import java.net.URLClassLoader -internal fun convertFilesToKtFiles(project: Project, files: List<File>): List<KtFile> { - val fs = StandardFileSystems.local() - val psiManager = PsiManager.getInstance(project) - val ktFiles = mutableListOf<KtFile>() - for (file in files) { - val vFile = fs.findFileByPath(file.absolutePath) ?: continue - val ktFile = psiManager.findFile(vFile) as? KtFile ?: continue - ktFiles.add(ktFile) - } - return ktFiles -} +class KotlinSymbolProcessing( + val compilerConfiguration: CompilerConfiguration, + val options: KspOptions, + val logger: KSPLogger, + val testProcessor: SymbolProcessorProvider? = null +) { -fun main(args: Array<String>) { - val kotlinSourceRoots = args.toList().map { File(it) } - val compilerConfiguration = CompilerConfiguration() - compilerConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) - val env = KotlinCoreEnvironment.createForProduction( + val providers: List<SymbolProcessorProvider> + val env: KotlinCoreEnvironment = KotlinCoreEnvironment.createForProduction( Disposer.newDisposable(), compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES ) + val project = env.project as MockProject + val kspCoreEnvironment = KSPCoreEnvironment(project) - val application = ApplicationManager.getApplication() as MockApplication - configureApplicationEnvironment(application) + var finished = false + val deferredSymbols = mutableMapOf<SymbolProcessor, List<KSAnnotated>>() + val ktFiles = convertFilesToKtFiles(project, compilerConfiguration.kotlinSourceRoots.map { it.path }) + val codeGenerator: CodeGeneratorImpl + val processors: List<SymbolProcessor> - val files = kotlinSourceRoots - .sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first - .flatMap { root -> root.walk().filter { it.isFile && it.extension == "kt" }.toList() } - .sortedBy { Files.isSymbolicLink(it.toPath()) } // This time is for .java files - .distinctBy { it.canonicalPath } - compilerConfiguration.addKotlinSourceRoots(files.map { it.absolutePath }) + init { + configureProjectEnvironment( + project, + compilerConfiguration, + env::createPackagePartProvider, + env.projectEnvironment.environment.jarFileSystem as CoreJarFileSystem + ) - val project = env.project as MockProject - val ktFiles = convertFilesToKtFiles(project, files) - configureProjectEnvironment( - project, - compilerConfiguration, - env::createPackagePartProvider, - env.projectEnvironment.environment.jarFileSystem as CoreJarFileSystem - ) - val kspCoreEnvironment = KSPCoreEnvironment(project) + val ksFiles = ktFiles.map { KSFileImpl(it) } + val anyChangesWildcard = AnyChanges(options.projectBaseDir) + codeGenerator = CodeGeneratorImpl( + options.classOutputDir, + options.javaOutputDir, + options.kotlinOutputDir, + options.resourceOutputDir, + options.projectBaseDir, + anyChangesWildcard, + ksFiles, + options.incremental + ) + val application = ApplicationManager.getApplication() as MockApplication + configureApplicationEnvironment(application) + providers = if (testProcessor != null) { + listOf(testProcessor) + } else { + val processingClasspath = options.processingClasspath + val classLoader = + URLClassLoader(processingClasspath.map { it.toURI().toURL() }.toTypedArray(), javaClass.classLoader) + + ServiceLoaderLite.loadImplementations(SymbolProcessorProvider::class.java, classLoader) + } - for (ktFile in ktFiles) { - analyseWithReadAction(ktFile) { - val fileSymbol = ktFile.getFileSymbol() - val members = fileSymbol.getFileScope().getAllSymbols() - members.filterIsInstance<KtFunctionSymbol>() + processors = providers.mapNotNull { provider -> + var processor: SymbolProcessor? = null + processor = provider.create( + SymbolProcessorEnvironment( + options.processingOptions, + options.languageVersion, + codeGenerator, + logger, + options.apiVersion, + options.compilerVersion, + // TODO: fix platform info + listOf(JvmPlatformInfoImpl("JVM", "1.8")) + ) + ) + processor.also { deferredSymbols[it] = mutableListOf() } } } + + fun execute() { + val resolver = ResolverAAImpl(ktFiles) + processors.forEach { it.process(resolver) } + } + + private fun convertFilesToKtFiles(project: Project, filePaths: List<String>): List<KtFile> { + val fs = StandardFileSystems.local() + val psiManager = PsiManager.getInstance(project) + val ktFiles = mutableListOf<KtFile>() + for (path in filePaths) { + val vFile = fs.findFileByPath(path) ?: continue + val ktFile = psiManager.findFile(vFile) as? KtFile ?: continue + ktFiles.add(ktFile) + } + return ktFiles + } +} + +fun main(args: Array<String>) { + val commandLineProcessor = KSPCommandLineProcessor(args) + val logger = CommandLineKSPLogger() + val kotlinSymbolProcessing = KotlinSymbolProcessing( + commandLineProcessor.compilerConfiguration, + commandLineProcessor.kspOptions, + logger + ) + kotlinSymbolProcessing.execute() } diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/AbstractKSPAATest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/AbstractKSPAATest.kt index 0cf16047..889b2229 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/AbstractKSPAATest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/AbstractKSPAATest.kt @@ -18,8 +18,9 @@ package com.google.devtools.ksp.impl.test import com.google.devtools.ksp.DualLookupTracker -import com.google.devtools.ksp.impl.ResolverAAImpl -import com.google.devtools.ksp.impl.convertFilesToKtFiles +import com.google.devtools.ksp.KspOptions +import com.google.devtools.ksp.impl.CommandLineKSPLogger +import com.google.devtools.ksp.impl.KotlinSymbolProcessing import com.google.devtools.ksp.processor.AbstractTestProcessor import com.google.devtools.ksp.testutils.AbstractKSPTest import com.intellij.mock.MockApplication @@ -29,6 +30,7 @@ import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem import org.jetbrains.kotlin.analysis.api.standalone.configureApplicationEnvironment import org.jetbrains.kotlin.analysis.api.standalone.configureProjectEnvironment import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot @@ -40,6 +42,7 @@ import org.jetbrains.kotlin.test.services.compilerConfigurationProvider import org.jetbrains.kotlin.test.services.isKtFile import org.jetbrains.kotlin.test.services.javaFiles import java.io.File +import java.nio.file.Files abstract class AbstractKSPAATest : AbstractKSPTest(FrontendKinds.FIR) { val TestModule.kotlinSrc @@ -85,7 +88,12 @@ abstract class AbstractKSPAATest : AbstractKSPTest(FrontendKinds.FIR) { val kotlinSourceFiles = mainModule.files.filter { it.isKtFile }.map { File(mainModule.kotlinSrc, it.relativePath) } - val ktFiles = convertFilesToKtFiles(kotlinCoreEnvironment.project, kotlinSourceFiles) + val ktFiles = kotlinSourceFiles + .sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first + .flatMap { root -> root.walk().filter { it.isFile && it.extension == "kt" }.toList() } + .sortedBy { Files.isSymbolicLink(it.toPath()) } + .distinctBy { it.canonicalPath } + compilerConfiguration.addKotlinSourceRoots(ktFiles.map { it.absolutePath }) configureProjectEnvironment( kotlinCoreEnvironment.project as MockProject, @@ -94,8 +102,22 @@ abstract class AbstractKSPAATest : AbstractKSPTest(FrontendKinds.FIR) { kotlinCoreEnvironment.projectEnvironment.environment.jarFileSystem as CoreJarFileSystem ) - val resolver = ResolverAAImpl(ktFiles) - testProcessor.process(resolver) + val testRoot = mainModule.testRoot + + val kspOptions = KspOptions.Builder().apply { + if (!mainModule.javaFiles.isEmpty()) { + javaSourceRoots.add(mainModule.javaDir) + } + classOutputDir = File(testRoot, "kspTest/classes/main") + javaOutputDir = File(testRoot, "kspTest/src/main/java") + kotlinOutputDir = File(testRoot, "kspTest/src/main/kotlin") + resourceOutputDir = File(testRoot, "kspTest/src/main/resources") + projectBaseDir = testRoot + cachesDir = File(testRoot, "kspTest/kspCaches") + kspOutputDir = File(testRoot, "kspTest") + }.build() + val ksp = KotlinSymbolProcessing(compilerConfiguration, kspOptions, CommandLineKSPLogger(), testProcessor) + ksp.execute() return testProcessor.toResult() } diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/KotlinAnalysisAPITest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/KotlinAnalysisAPITest.kt deleted file mode 100644 index b149570b..00000000 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/impl/test/KotlinAnalysisAPITest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.google.devtools.ksp.impl.test - -import com.google.devtools.ksp.impl.main -import org.junit.jupiter.api.Test - -class KotlinAnalysisAPITest { - - @Test - fun testHello() { - main(arrayOf("testData/api")) - } -} |