Simplify public methods of KotlinToJVMBytecodeCompiler

Pass as much as possible in the single CompilerConfiguration instance instead
of in separate function parameters. Add corresponding keys to
JVMConfigurationKeys.

Re changes in compileModules: since output directory (stored now under the key
OUTPUT_DIRECTORY) is different for each module, the configuration of the whole
project is no longer applicable when compiling individual modules. Thus we copy
the project configuration for each module and add the output directory value
This commit is contained in:
Alexander Udalov
2016-05-20 18:41:51 +03:00
parent 8c0f78db50
commit 0fe39a186e
7 changed files with 97 additions and 118 deletions
@@ -129,6 +129,14 @@ open class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
JvmMetadataVersion.skipCheck = true
}
if (arguments.includeRuntime) {
configuration.put(JVMConfigurationKeys.INCLUDE_RUNTIME, true)
}
val friendPaths = arguments.friendPaths?.toList()
if (friendPaths != null) {
configuration.put(JVMConfigurationKeys.FRIEND_PATHS, friendPaths)
}
putAdvancedOptions(configuration, arguments)
messageSeverityCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment", CompilerMessageLocation.NO_LOCATION)
@@ -137,51 +145,47 @@ open class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
val destination = arguments.destination
val jar: File?
val outputDir: File?
if (destination != null) {
val isJar = destination.endsWith(".jar")
jar = if (isJar) File(destination) else null
outputDir = if (isJar) null else File(destination)
}
else {
jar = null
outputDir = null
}
val environment: KotlinCoreEnvironment
val friendPaths = arguments.friendPaths?.toList() ?: emptyList<String>()
if (arguments.module != null) {
val sanitizedCollector = FilteringMessageCollector(messageSeverityCollector, `in`(CompilerMessageSeverity.VERBOSE))
val moduleScript = CompileEnvironmentUtil.loadModuleDescriptions(arguments.module, sanitizedCollector)
if (outputDir != null) {
messageSeverityCollector.report(CompilerMessageSeverity.WARNING, "The '-d' option with a directory destination is ignored because '-module' is specified", CompilerMessageLocation.NO_LOCATION)
if (destination != null) {
messageSeverityCollector.report(
CompilerMessageSeverity.WARNING,
"The '-d' option with a directory destination is ignored because '-module' is specified",
CompilerMessageLocation.NO_LOCATION
)
}
val directory = File(arguments.module).absoluteFile.parentFile
val moduleFile = File(arguments.module)
val directory = moduleFile.absoluteFile.parentFile
val compilerConfiguration = KotlinToJVMBytecodeCompiler.createCompilerConfiguration(configuration, moduleScript.modules, directory)
compilerConfiguration.put(JVMConfigurationKeys.MODULE_XML_FILE_PATH, arguments.module)
environment = createCoreEnvironment(rootDisposable, compilerConfiguration)
KotlinToJVMBytecodeCompiler.configureSourceRoots(configuration, moduleScript.modules, directory)
configuration.put(JVMConfigurationKeys.MODULE_XML_FILE, moduleFile)
val environment = createCoreEnvironment(rootDisposable, configuration)
if (messageSeverityCollector.anyReported(CompilerMessageSeverity.ERROR)) return COMPILATION_ERROR
KotlinToJVMBytecodeCompiler.compileModules(environment, configuration, moduleScript.modules, directory, jar, friendPaths, arguments.includeRuntime)
KotlinToJVMBytecodeCompiler.compileModules(environment, directory)
}
else if (arguments.script) {
val scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size)
environment = createCoreEnvironment(rootDisposable, configuration)
val environment = createCoreEnvironment(rootDisposable, configuration)
if (messageSeverityCollector.anyReported(CompilerMessageSeverity.ERROR)) return COMPILATION_ERROR
return KotlinToJVMBytecodeCompiler.compileAndExecuteScript(configuration, paths, environment, scriptArgs)
return KotlinToJVMBytecodeCompiler.compileAndExecuteScript(environment, paths, scriptArgs)
}
else {
environment = createCoreEnvironment(rootDisposable, configuration)
if (destination != null) {
if (destination.endsWith(".jar")) {
configuration.put(JVMConfigurationKeys.OUTPUT_JAR, File(destination))
}
else {
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(destination))
}
}
val environment = createCoreEnvironment(rootDisposable, configuration)
if (messageSeverityCollector.anyReported(CompilerMessageSeverity.ERROR)) return COMPILATION_ERROR
if (environment.getSourceFiles().isEmpty()) {
@@ -192,13 +196,13 @@ open class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
return COMPILATION_ERROR
}
KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, friendPaths, arguments.includeRuntime)
KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment)
}
if (arguments.reportPerf) {
reportGCTime(environment.configuration)
reportCompilationTime(environment.configuration)
PerformanceCounter.report { s -> reportPerf(environment.configuration, s) }
reportGCTime(configuration)
reportCompilationTime(configuration)
PerformanceCounter.report { s -> reportPerf(configuration, s) }
}
return OK
}
@@ -206,7 +210,6 @@ open class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
messageSeverityCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), MessageUtil.psiElementToMessageLocation(e.element))
return INTERNAL_ERROR
}
}
private fun createCoreEnvironment(rootDisposable: Disposable, configuration: CompilerConfiguration): KotlinCoreEnvironment {
@@ -57,7 +57,7 @@ import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation.N
import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR;
public class CompileEnvironmentUtil {
private static Logger LOG = Logger.getInstance(CompileEnvironmentUtil.class);
private static final Logger LOG = Logger.getInstance(CompileEnvironmentUtil.class);
@NotNull
public static ModuleScriptData loadModuleDescriptions(String moduleDefinitionFile, MessageCollector messageCollector) {
@@ -162,9 +162,9 @@ public class CompileEnvironmentUtil {
if (vFile == null) {
String message = "Source file or directory not found: " + sourceRootPath;
String moduleFilePath = configuration.get(JVMConfigurationKeys.MODULE_XML_FILE_PATH);
File moduleFilePath = configuration.get(JVMConfigurationKeys.MODULE_XML_FILE);
if (moduleFilePath != null) {
String moduleFileContent = FileUtil.loadFile(new File(moduleFilePath));
String moduleFileContent = FileUtil.loadFile(moduleFilePath);
LOG.warn(message +
"\n\nmodule file path: " + moduleFilePath +
"\ncontent:\n" + moduleFileContent);
@@ -74,53 +74,45 @@ object KotlinToJVMBytecodeCompiler {
private fun writeOutput(
configuration: CompilerConfiguration,
outputFiles: OutputFileCollection,
outputDir: File?,
jarPath: File?,
jarRuntime: Boolean,
mainClass: FqName?
) {
val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR)
if (jarPath != null) {
CompileEnvironmentUtil.writeToJar(jarPath, jarRuntime, mainClass, outputFiles)
}
else {
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
outputFiles.writeAll(outputDir ?: File("."), messageCollector)
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, mainClass, outputFiles)
return
}
val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".")
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
outputFiles.writeAll(outputDir, messageCollector)
}
private fun createOutputFilesFlushingCallbackIfPossible(
configuration: CompilerConfiguration,
outputDir: File?,
jarPath: File?
): GenerationStateEventCallback {
if (jarPath != null) return GenerationStateEventCallback.DO_NOTHING
private fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback {
if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) {
return GenerationStateEventCallback.DO_NOTHING
}
return GenerationStateEventCallback { state ->
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
writeOutput(configuration, currentOutput, outputDir, jarPath = null, jarRuntime = false, mainClass = null)
writeOutput(configuration, currentOutput, mainClass = null)
state.factory.releaseGeneratedOutput()
}
}
fun compileModules(
environment: KotlinCoreEnvironment,
configuration: CompilerConfiguration,
chunk: List<Module>,
directory: File,
jarPath: File?,
friendPaths: List<String>,
jarRuntime: Boolean
): Boolean {
fun compileModules(environment: KotlinCoreEnvironment, directory: File): Boolean {
val outputFiles = hashMapOf<Module, ClassFileFactory>()
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
val projectConfiguration = environment.configuration
val chunk = projectConfiguration.getNotNull(JVMConfigurationKeys.MODULES)
for (module in chunk) {
moduleVisibilityManager.addModule(module)
}
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
for (path in friendPaths) {
moduleVisibilityManager.addFriendPath(path)
}
@@ -133,28 +125,31 @@ object KotlinToJVMBytecodeCompiler {
result.throwIfError()
val generationStates = ArrayList<GenerationState>()
val moduleConfigurations = ArrayList<CompilerConfiguration>(chunk.size)
val generationStates = ArrayList<GenerationState>(chunk.size)
for (module in chunk) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val ktFiles = CompileEnvironmentUtil.getKtFiles(
environment.project, getAbsolutePaths(directory, module), configuration
environment.project, getAbsolutePaths(directory, module), projectConfiguration
) { path -> throw IllegalStateException("Should have been checked before: $path") }
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
val onIndependentPartCompilationEnd =
createOutputFilesFlushingCallbackIfPossible(configuration, File(module.getOutputDirectory()), jarPath)
val moduleConfiguration = projectConfiguration.copy().apply {
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
}
val generationState = generate(environment, result, ktFiles, module, onIndependentPartCompilationEnd)
val generationState = generate(environment, moduleConfiguration, result, ktFiles, module)
outputFiles.put(module, generationState.factory)
moduleConfigurations.add(moduleConfiguration)
generationStates.add(generationState)
}
try {
for (module in chunk) {
for ((module, configuration) in chunk.zip(moduleConfigurations)) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
writeOutput(configuration, outputFiles[module]!!, File(module.getOutputDirectory()), jarPath, jarRuntime, null)
writeOutput(configuration, outputFiles[module]!!, null)
}
return true
}
@@ -165,13 +160,7 @@ object KotlinToJVMBytecodeCompiler {
}
}
fun createCompilerConfiguration(
base: CompilerConfiguration,
chunk: List<Module>,
directory: File
): CompilerConfiguration {
val configuration = base.copy()
fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, directory: File) {
for (module in chunk) {
configuration.addKotlinSourceRoots(getAbsolutePaths(directory, module))
}
@@ -189,8 +178,6 @@ object KotlinToJVMBytecodeCompiler {
}
configuration.addAll(JVMConfigurationKeys.MODULES, chunk)
return configuration
}
private fun findMainClass(generationState: GenerationState, files: List<KtFile>): FqName? {
@@ -205,28 +192,22 @@ object KotlinToJVMBytecodeCompiler {
.singleOrNull { it != null }
}
fun compileBunchOfSources(
environment: KotlinCoreEnvironment,
jar: File?,
outputDir: File?,
friendPaths: List<String>,
includeRuntime: Boolean
): Boolean {
fun compileBunchOfSources(environment: KotlinCoreEnvironment): Boolean {
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
for (path in friendPaths) {
moduleVisibilityManager.addFriendPath(path)
}
if (!checkKotlinPackageUsage(environment, environment.getSourceFiles())) return false
val onIndependentPartCompilationEnd = createOutputFilesFlushingCallbackIfPossible(environment.configuration, outputDir, jar)
val generationState = analyzeAndGenerate(environment, onIndependentPartCompilationEnd) ?: return false
val generationState = analyzeAndGenerate(environment) ?: return false
val mainClass = findMainClass(generationState, environment.getSourceFiles())
try {
writeOutput(environment.configuration, generationState.factory, outputDir, jar, includeRuntime, mainClass)
writeOutput(environment.configuration, generationState.factory, mainClass)
return true
}
finally {
@@ -234,13 +215,8 @@ object KotlinToJVMBytecodeCompiler {
}
}
fun compileAndExecuteScript(
configuration: CompilerConfiguration,
paths: KotlinPaths,
environment: KotlinCoreEnvironment,
scriptArgs: List<String>
): ExitCode {
val scriptClass = compileScript(configuration, paths, environment) ?: return ExitCode.COMPILATION_ERROR
fun compileAndExecuteScript(environment: KotlinCoreEnvironment, paths: KotlinPaths, scriptArgs: List<String>): ExitCode {
val scriptClass = compileScript(environment, paths) ?: return ExitCode.COMPILATION_ERROR
val scriptConstructor = getScriptConstructor(scriptClass)
try {
@@ -280,18 +256,13 @@ object KotlinToJVMBytecodeCompiler {
private fun getScriptConstructor(scriptClass: Class<*>): Constructor<*> =
scriptClass.getConstructor(Array<String>::class.java)
fun compileScript(
configuration: CompilerConfiguration,
paths: KotlinPaths,
environment: KotlinCoreEnvironment
): Class<*>? {
val state = analyzeAndGenerate(environment, GenerationStateEventCallback.DO_NOTHING) ?: return null
fun compileScript(environment: KotlinCoreEnvironment, paths: KotlinPaths): Class<*>? {
val state = analyzeAndGenerate(environment) ?: return null
val classLoader: GeneratedClassLoader
try {
val classPaths = arrayListOf(paths.runtimePath.toURI().toURL())
configuration.jvmClasspathRoots.mapTo(classPaths) { it.toURI().toURL() }
classLoader = GeneratedClassLoader(state.factory, URLClassLoader(classPaths.toTypedArray(), null))
environment.configuration.jvmClasspathRoots.mapTo(classPaths) { it.toURI().toURL() }
val classLoader = GeneratedClassLoader(state.factory, URLClassLoader(classPaths.toTypedArray(), null))
val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed")
return classLoader.loadClass(script.fqName.asString())
@@ -301,17 +272,14 @@ object KotlinToJVMBytecodeCompiler {
}
}
fun analyzeAndGenerate(
environment: KotlinCoreEnvironment,
onIndependentPartCompilationEnd: GenerationStateEventCallback
): GenerationState? {
fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? {
val result = analyze(environment, null) ?: return null
if (!result.shouldGenerateCode) return null
result.throwIfError()
return generate(environment, result, environment.getSourceFiles(), null, onIndependentPartCompilationEnd)
return generate(environment, environment.configuration, result, environment.getSourceFiles(), null)
}
private fun analyze(environment: KotlinCoreEnvironment, targetDescription: String?): AnalysisResult? {
@@ -365,10 +333,10 @@ object KotlinToJVMBytecodeCompiler {
private fun generate(
environment: KotlinCoreEnvironment,
configuration: CompilerConfiguration,
result: AnalysisResult,
sourceFiles: List<KtFile>,
module: Module?,
onIndependentPartCompilationEnd: GenerationStateEventCallback
module: Module?
): GenerationState {
val generationState = GenerationState(
environment.project,
@@ -376,12 +344,12 @@ object KotlinToJVMBytecodeCompiler {
result.moduleDescriptor,
result.bindingContext,
sourceFiles,
environment.configuration,
configuration,
GenerationState.GenerateClassFilter.GENERATE_ALL,
module?.let(::TargetId),
module?.let { it.getModuleName() },
module?.let { File(it.getOutputDirectory()) },
onIndependentPartCompilationEnd
createOutputFilesFlushingCallbackIfPossible(configuration)
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
@@ -19,12 +19,20 @@ package org.jetbrains.kotlin.config;
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents;
import org.jetbrains.kotlin.modules.Module;
import java.io.File;
import java.util.List;
public class JVMConfigurationKeys {
private JVMConfigurationKeys() {
}
public static final CompilerConfigurationKey<File> OUTPUT_DIRECTORY =
CompilerConfigurationKey.create("output directory");
public static final CompilerConfigurationKey<File> OUTPUT_JAR =
CompilerConfigurationKey.create("output .jar");
public static final CompilerConfigurationKey<Boolean> INCLUDE_RUNTIME =
CompilerConfigurationKey.create("include runtime to the resulting .jar");
public static final CompilerConfigurationKey<Boolean> DISABLE_CALL_ASSERTIONS =
CompilerConfigurationKey.create("disable not-null call assertions");
public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS =
@@ -41,7 +49,7 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<IncrementalCompilationComponents> INCREMENTAL_COMPILATION_COMPONENTS =
CompilerConfigurationKey.create("incremental cache provider");
public static final CompilerConfigurationKey<String> MODULE_XML_FILE_PATH =
public static final CompilerConfigurationKey<File> MODULE_XML_FILE =
CompilerConfigurationKey.create("path to module.xml");
public static final CompilerConfigurationKey<String> DECLARATIONS_JSON_PATH =
@@ -50,6 +58,9 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<List<Module>> MODULES =
CompilerConfigurationKey.create("module data");
public static final CompilerConfigurationKey<List<String>> FRIEND_PATHS =
CompilerConfigurationKey.create("friend module paths");
public static final CompilerConfigurationKey<String> MODULE_NAME =
CompilerConfigurationKey.create("module name");
}
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler;
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.ContentRootsKt;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
@@ -64,8 +63,7 @@ public class StdlibTest extends KotlinTestWithEnvironment {
}
public void testStdlib() throws ClassNotFoundException {
GenerationState state = KotlinToJVMBytecodeCompiler.INSTANCE.analyzeAndGenerate(
getEnvironment(), GenerationStateEventCallback.Companion.getDO_NOTHING());
GenerationState state = KotlinToJVMBytecodeCompiler.INSTANCE.analyzeAndGenerate(getEnvironment());
if (state == null) {
fail("There were compilation errors");
}
@@ -86,7 +86,7 @@ public class ScriptTest {
KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
try {
return KotlinToJVMBytecodeCompiler.INSTANCE.compileScript(configuration, paths, environment);
return KotlinToJVMBytecodeCompiler.INSTANCE.compileScript(environment, paths);
}
catch (CompilationException e) {
messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
@@ -43,7 +43,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler;
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot;
import org.jetbrains.kotlin.codegen.GeneratedClassLoader;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
@@ -181,7 +180,7 @@ public class ExecuteKotlinScriptMojo extends AbstractMojo {
KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
GenerationState state = KotlinToJVMBytecodeCompiler.INSTANCE.analyzeAndGenerate(environment, GenerationStateEventCallback.Companion.getDO_NOTHING());
GenerationState state = KotlinToJVMBytecodeCompiler.INSTANCE.analyzeAndGenerate(environment);
if (state == null) {
throw new ScriptExecutionException(scriptFile, "compile error");