Skip to content

Commit eb58fd7

Browse files
committed
more detailed time logging
1 parent a06a440 commit eb58fd7

File tree

5 files changed

+414
-30
lines changed

5 files changed

+414
-30
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
package com.tschuchort.compiletesting
2+
3+
import org.jetbrains.kotlin.backend.jvm.jvmPhases
4+
import org.jetbrains.kotlin.cli.common.*
5+
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
6+
import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension
7+
import org.jetbrains.kotlin.cli.common.extensions.ShellExtension
8+
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
9+
import org.jetbrains.kotlin.cli.common.messages.FilteringMessageCollector
10+
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
11+
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
12+
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
13+
import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder
14+
import org.jetbrains.kotlin.cli.common.modules.ModuleChunk
15+
import org.jetbrains.kotlin.cli.common.profiling.ProfilingCompilerPerformanceManager
16+
import org.jetbrains.kotlin.cli.jvm.*
17+
import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
18+
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
19+
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
20+
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
21+
import org.jetbrains.kotlin.codegen.CompilationException
22+
import org.jetbrains.kotlin.com.intellij.openapi.Disposable
23+
import org.jetbrains.kotlin.config.*
24+
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
25+
import org.jetbrains.kotlin.incremental.components.LookupTracker
26+
import org.jetbrains.kotlin.load.java.JavaClassesTracker
27+
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
28+
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
29+
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
30+
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
31+
import org.jetbrains.kotlin.modules.JavaRootPath
32+
import org.jetbrains.kotlin.utils.KotlinPaths
33+
import java.io.File
34+
35+
object KCTTimer {
36+
37+
private var start = now()
38+
39+
private val recordings = mutableListOf<Pair<String, Long>>()
40+
fun reset() {
41+
recordings.clear()
42+
start = now()
43+
}
44+
fun record(name: String) {
45+
recordings.add(name to now())
46+
}
47+
48+
fun dump() = buildString {
49+
val end = now()
50+
appendln("total time: ${end - start}")
51+
recordings.fold(start) { acc, next ->
52+
appendln("${next.first}: ${next.second - acc}")
53+
next.second
54+
}
55+
}
56+
57+
private inline fun now() = System.currentTimeMillis()
58+
}
59+
60+
class FastKotlinCompiler : CLICompiler<K2JVMCompilerArguments>(){
61+
override val performanceManager: CommonCompilerPerformanceManager
62+
get() = error("unsupported")
63+
64+
override fun createMetadataVersion(versionArray: IntArray): BinaryVersion {
65+
return JvmMetadataVersion(*versionArray)
66+
}
67+
68+
override fun doExecute(
69+
arguments: K2JVMCompilerArguments,
70+
configuration: CompilerConfiguration,
71+
rootDisposable: Disposable,
72+
paths: KotlinPaths?
73+
): ExitCode {
74+
return _doExecute(KCTTimer, arguments, configuration, rootDisposable, paths)
75+
}
76+
private fun _doExecute(
77+
timer:KCTTimer,
78+
arguments: K2JVMCompilerArguments,
79+
configuration: CompilerConfiguration,
80+
rootDisposable: Disposable,
81+
paths: KotlinPaths?
82+
): ExitCode {
83+
timer.record("begin")
84+
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
85+
86+
configuration.putIfNotNull(CLIConfigurationKeys.REPEAT_COMPILE_MODULES, arguments.repeatCompileModules?.toIntOrNull())
87+
configuration.put(CLIConfigurationKeys.PHASE_CONFIG, createPhaseConfig(jvmPhases, arguments, messageCollector))
88+
89+
if (!configuration.configureJdkHome(arguments)) return ExitCode.COMPILATION_ERROR
90+
91+
configuration.put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, arguments.disableStandardScript)
92+
timer.record("done jvm config")
93+
val pluginLoadResult = loadPlugins(paths, arguments, configuration)
94+
timer.record("loaded plugins")
95+
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
96+
97+
val moduleName = arguments.moduleName ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME
98+
configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
99+
100+
configuration.configureExplicitContentRoots(arguments)
101+
timer.record("configure content roots")
102+
configuration.configureStandardLibs(paths, arguments)
103+
timer.record("configure standard libs")
104+
configuration.configureAdvancedJvmOptions(arguments)
105+
timer.record("configure jvm options")
106+
configuration.configureKlibPaths(arguments)
107+
timer.record("configure klib paths")
108+
109+
if (arguments.buildFile == null && !arguments.version && !arguments.allowNoSourceFiles &&
110+
(arguments.script || arguments.expression != null || arguments.freeArgs.isEmpty())) {
111+
112+
// script or repl
113+
if (arguments.script && arguments.freeArgs.isEmpty()) {
114+
messageCollector.report(ERROR, "Specify script source path to evaluate")
115+
return ExitCode.COMPILATION_ERROR
116+
}
117+
118+
val projectEnvironment =
119+
KotlinCoreEnvironment.ProjectEnvironment(
120+
rootDisposable,
121+
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration)
122+
)
123+
timer.record("build project env")
124+
projectEnvironment.registerExtensionsFromPlugins(configuration)
125+
timer.record("registered extensions from plugins")
126+
127+
if (arguments.script || arguments.expression != null) {
128+
val scriptingEvaluator = ScriptEvaluationExtension.getInstances(projectEnvironment.project).find { it.isAccepted(arguments) }
129+
if (scriptingEvaluator == null) {
130+
messageCollector.report(ERROR, "Unable to evaluate script, no scripting plugin loaded")
131+
return ExitCode.COMPILATION_ERROR
132+
}
133+
return scriptingEvaluator.eval(arguments, configuration, projectEnvironment)
134+
} else {
135+
val shell = ShellExtension.getInstances(projectEnvironment.project).find { it.isAccepted(arguments) }
136+
if (shell == null) {
137+
messageCollector.report(ERROR, "Unable to run REPL, no scripting plugin loaded")
138+
return ExitCode.COMPILATION_ERROR
139+
}
140+
return shell.run(arguments, configuration, projectEnvironment)
141+
}
142+
}
143+
144+
messageCollector.report(LOGGING, "Configuring the compilation environment")
145+
try {
146+
timer.record("start building modules")
147+
val destination = arguments.destination?.let { File(it) }
148+
val buildFile = arguments.buildFile?.let { File(it) }
149+
150+
val moduleChunk = if (buildFile != null) {
151+
fun strongWarning(message: String) {
152+
messageCollector.report(STRONG_WARNING, message)
153+
}
154+
if (destination != null) {
155+
strongWarning("The '-d' option with a directory destination is ignored because '-Xbuild-file' is specified")
156+
}
157+
if (arguments.javaSourceRoots != null) {
158+
strongWarning("The '-Xjava-source-roots' option is ignored because '-Xbuild-file' is specified")
159+
}
160+
if (arguments.javaPackagePrefix != null) {
161+
strongWarning("The '-Xjava-package-prefix' option is ignored because '-Xbuild-file' is specified")
162+
}
163+
164+
val sanitizedCollector = FilteringMessageCollector(messageCollector, VERBOSE::contains)
165+
configuration.put(JVMConfigurationKeys.MODULE_XML_FILE, buildFile)
166+
CompileEnvironmentUtil.loadModuleChunk(buildFile, sanitizedCollector)
167+
} else {
168+
if (destination != null) {
169+
if (destination.path.endsWith(".jar")) {
170+
configuration.put(JVMConfigurationKeys.OUTPUT_JAR, destination)
171+
} else {
172+
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, destination)
173+
}
174+
}
175+
176+
val module = ModuleBuilder(moduleName, destination?.path ?: ".", "java-production")
177+
module.configureFromArgs(arguments)
178+
179+
ModuleChunk(listOf(module))
180+
}
181+
182+
val chunk = moduleChunk.modules
183+
// TODO https://github.com/JetBrains/kotlin/blob/master/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt#L253
184+
timer.record("modules ready")
185+
KotlinToJVMBytecodeCompiler.pleaseConfigureSourceRoots(configuration, chunk, buildFile)
186+
timer.record("source roots reads")
187+
188+
val environment = createCoreEnvironment(
189+
rootDisposable, configuration, messageCollector,
190+
chunk.map { input -> input.getModuleName() + "-" + input.getModuleType() }.let { names ->
191+
names.singleOrNull() ?: names.joinToString()
192+
}
193+
) ?: return ExitCode.COMPILATION_ERROR
194+
val end = System.currentTimeMillis()
195+
timer.record("env ready")
196+
environment.registerJavacIfNeeded(arguments).let {
197+
if (!it) return ExitCode.COMPILATION_ERROR
198+
}
199+
200+
if (environment.getSourceFiles().isEmpty() && !arguments.allowNoSourceFiles && buildFile == null) {
201+
if (arguments.version) return ExitCode.OK
202+
203+
messageCollector.report(ERROR, "No source files")
204+
return ExitCode.COMPILATION_ERROR
205+
}
206+
timer.record("pre compilation")
207+
KotlinToJVMBytecodeCompiler.pleaseCompileModules(environment, buildFile, chunk)
208+
timer.record("done compilation")
209+
return ExitCode.OK
210+
} catch (e: CompilationException) {
211+
messageCollector.report(
212+
EXCEPTION,
213+
OutputMessageUtil.renderException(e),
214+
MessageUtil.psiElementToMessageLocation(e.element)
215+
)
216+
return ExitCode.INTERNAL_ERROR
217+
} catch (th: Throwable) {
218+
println("what is this? ${th}")
219+
return ExitCode.INTERNAL_ERROR
220+
}
221+
}
222+
223+
private fun ModuleBuilder.configureFromArgs(args: K2JVMCompilerArguments) {
224+
args.friendPaths?.forEach { addFriendDir(it) }
225+
args.classpath?.split(File.pathSeparator)?.forEach { addClasspathEntry(it) }
226+
args.javaSourceRoots?.forEach {
227+
addJavaSourceRoot(JavaRootPath(it, args.javaPackagePrefix))
228+
}
229+
230+
val commonSources = args.commonSources?.toSet().orEmpty()
231+
for (arg in args.freeArgs) {
232+
if (arg.endsWith(".java")) {
233+
addJavaSourceRoot(JavaRootPath(arg, args.javaPackagePrefix))
234+
} else {
235+
addSourceFiles(arg)
236+
if (arg in commonSources) {
237+
addCommonSourceFiles(arg)
238+
}
239+
240+
if (File(arg).isDirectory) {
241+
addJavaSourceRoot(JavaRootPath(arg, args.javaPackagePrefix))
242+
}
243+
}
244+
}
245+
}
246+
247+
private fun createCoreEnvironment(
248+
rootDisposable: Disposable,
249+
configuration: CompilerConfiguration,
250+
messageCollector: MessageCollector,
251+
targetDescription: String
252+
): KotlinCoreEnvironment? {
253+
System.setProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true")
254+
if (messageCollector.hasErrors()) return null
255+
256+
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
257+
// createForTests is slower???
258+
// val environment = KotlinCoreEnvironment.createForTests(
259+
// rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
260+
// )
261+
262+
val sourceFiles = environment.getSourceFiles()
263+
// configuration[CLIConfigurationKeys.PERF_MANAGER]?.notifyCompilerInitialized(
264+
// sourceFiles.size, environment.countLinesOfCode(sourceFiles), targetDescription
265+
// )
266+
267+
return if (messageCollector.hasErrors()) null else environment
268+
}
269+
270+
271+
override fun setupPlatformSpecificArgumentsAndServices(
272+
configuration: CompilerConfiguration,
273+
arguments: K2JVMCompilerArguments,
274+
services: Services
275+
) {
276+
with(configuration) {
277+
if (IncrementalCompilation.isEnabledForJvm()) {
278+
putIfNotNull(CommonConfigurationKeys.LOOKUP_TRACKER, services[LookupTracker::class.java])
279+
280+
putIfNotNull(CommonConfigurationKeys.EXPECT_ACTUAL_TRACKER, services[ExpectActualTracker::class.java])
281+
282+
putIfNotNull(
283+
JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS,
284+
services[IncrementalCompilationComponents::class.java]
285+
)
286+
287+
putIfNotNull(JVMConfigurationKeys.JAVA_CLASSES_TRACKER, services[JavaClassesTracker::class.java])
288+
}
289+
setupJvmSpecificArguments(arguments)
290+
}
291+
}
292+
293+
override fun MutableList<String>.addPlatformOptions(arguments: K2JVMCompilerArguments) {
294+
if (arguments.scriptTemplates?.isNotEmpty() == true) {
295+
add("plugin:kotlin.scripting:script-templates=${arguments.scriptTemplates!!.joinToString(",")}")
296+
}
297+
if (arguments.scriptResolverEnvironment?.isNotEmpty() == true) {
298+
add(
299+
"plugin:kotlin.scripting:script-resolver-environment=${arguments.scriptResolverEnvironment!!.joinToString(
300+
","
301+
)}"
302+
)
303+
}
304+
}
305+
306+
override fun createArguments(): K2JVMCompilerArguments = K2JVMCompilerArguments().apply {
307+
if (System.getenv("KOTLIN_REPORT_PERF") != null) {
308+
reportPerf = true
309+
}
310+
}
311+
312+
override fun executableScriptFileName() = "kotlinc-jvm"
313+
protected class FastCompilerPerformanceManager : CommonCompilerPerformanceManager("Kotlin to JVM Compiler")
314+
override fun createPerformanceManager(arguments: K2JVMCompilerArguments, services: Services): CommonCompilerPerformanceManager {
315+
val externalManager = services[CommonCompilerPerformanceManager::class.java]
316+
if (externalManager != null) return externalManager
317+
val argument = arguments.profileCompilerCommand ?: return FastCompilerPerformanceManager()
318+
return ProfilingCompilerPerformanceManager.create(argument)
319+
}
320+
}
321+

0 commit comments

Comments
 (0)