forked from detekt/detekt
/
KotlinEnvironmentUtils.kt
127 lines (115 loc) · 4.97 KB
/
KotlinEnvironmentUtils.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package io.github.detekt.parser
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
import org.jetbrains.kotlin.com.intellij.mock.MockProject
import org.jetbrains.kotlin.com.intellij.openapi.Disposable
import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.com.intellij.pom.PomModel
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import java.io.File
import java.net.URLClassLoader
import java.nio.file.Path
/**
* Creates an environment instance which can be used to compile source code to KtFile's.
* This environment also allows to modify the resulting AST files.
*/
fun createKotlinCoreEnvironment(
configuration: CompilerConfiguration = CompilerConfiguration(),
disposable: Disposable = Disposer.newDisposable()
): KotlinCoreEnvironment {
// https://github.com/JetBrains/kotlin/commit/2568804eaa2c8f6b10b735777218c81af62919c1
setIdeaIoUseFallback()
configuration.put(
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY,
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)
)
configuration.put(CommonConfigurationKeys.MODULE_NAME, "detekt")
val environment = KotlinCoreEnvironment.createForProduction(
disposable,
configuration,
EnvironmentConfigFiles.JVM_CONFIG_FILES
)
val projectCandidate = environment.project
val project = requireNotNull(projectCandidate as? MockProject) {
"MockProject type expected, actual - ${projectCandidate.javaClass.simpleName}"
}
project.registerService(PomModel::class.java, DetektPomModel(project))
return environment
}
/**
* Creates a compiler configuration for the kotlin compiler with all known sources and classpath jars.
* Be aware that if any path of [pathsToAnalyze] is a directory it is scanned for java and kotlin files.
*/
fun createCompilerConfiguration(
pathsToAnalyze: List<Path>,
classpath: List<String>,
languageVersion: LanguageVersion?,
jvmTarget: JvmTarget,
jdkHome: Path?,
): CompilerConfiguration {
val javaFiles = pathsToAnalyze.flatMap { path ->
path.toFile().walk()
.filter { it.isFile && it.extension.equals("java", true) }
.toList()
}
val kotlinFiles = pathsToAnalyze.flatMap { path ->
path.toFile().walk()
.filter { it.isFile }
.filter { it.extension.equals("kt", true) || it.extension.equals("kts", true) }
.map { it.absolutePath }
.toList()
}
val classpathFiles = classpath.map { File(it) }
val retrievedLanguageVersion = languageVersion ?: classpathFiles.getKotlinLanguageVersion()
val languageVersionSettings: LanguageVersionSettings? = retrievedLanguageVersion?.let {
LanguageVersionSettingsImpl(
languageVersion = it,
apiVersion = ApiVersion.createByLanguageVersion(it)
)
}
return CompilerConfiguration().apply {
if (languageVersionSettings != null) {
put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, languageVersionSettings)
}
put(JVMConfigurationKeys.JVM_TARGET, jvmTarget)
addJavaSourceRoots(javaFiles)
addKotlinSourceRoots(kotlinFiles)
addJvmClasspathRoots(classpathFiles)
jdkHome?.let { put(JVMConfigurationKeys.JDK_HOME, it.toFile()) }
configureJdkClasspathRoots()
}
}
/**
* Infer the language version from the files representing classpath.
*/
internal fun Iterable<File>.getKotlinLanguageVersion(): LanguageVersion? {
val urls = map { it.toURI().toURL() }
if (urls.isEmpty()) {
return null
}
return URLClassLoader(urls.toTypedArray()).use { classLoader ->
runCatching {
val clazz = classLoader.loadClass("kotlin.KotlinVersion")
val field = clazz.getField("CURRENT")
field.isAccessible = true
val versionObj = field[null]
val versionString = versionObj?.toString()
versionString?.let { LanguageVersion.fromFullVersionString(it) }
}.getOrNull()
}
}