Unify JVM compilation with and without -Xbuild-file

This commit is contained in:
Alexey Tsvetkov
2019-02-28 15:48:13 +03:00
parent 4154e97aa3
commit 5b7cee6221
2 changed files with 94 additions and 95 deletions
@@ -29,11 +29,12 @@ import org.jetbrains.kotlin.cli.common.messages.FilteringMessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder
import org.jetbrains.kotlin.cli.common.modules.ModuleChunk
import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
import org.jetbrains.kotlin.codegen.CompilationException
@@ -46,6 +47,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.modules.JavaRootPath
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
@@ -72,26 +74,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
val pluginLoadResult = loadPlugins(arguments, configuration)
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
val commonSources = arguments.commonSources?.toSet().orEmpty()
if (!arguments.script && arguments.buildFile == null) {
for (arg in arguments.freeArgs) {
val file = File(arg)
if (file.extension == JavaFileType.DEFAULT_EXTENSION) {
configuration.addJavaSourceRoot(file, arguments.javaPackagePrefix)
} else {
configuration.addKotlinSourceRoot(arg, isCommon = arg in commonSources)
if (file.isDirectory) {
configuration.addJavaSourceRoot(file, arguments.javaPackagePrefix)
}
}
}
for (path in arguments.javaSourceRoots ?: emptyArray()) {
configuration.addJavaSourceRoot(File(path), arguments.javaPackagePrefix)
}
}
configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME)
val moduleName = arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME
configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
configuration.configureExplicitContentRoots(arguments)
configuration.configureStandardLibs(paths, arguments)
@@ -109,9 +93,26 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
messageCollector.report(LOGGING, "Configuring the compilation environment")
try {
val destination = arguments.destination
if (arguments.script) {
val sourcePath = arguments.freeArgs.first()
configuration.addKotlinSourceRoot(sourcePath)
if (arguments.buildFile != null) {
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
val scriptingEvaluator = ScriptEvaluationExtension.getInstances(environment.project).find { it.isAccepted(arguments) }
if (scriptingEvaluator == null) {
messageCollector.report(ERROR, "Unable to evaluate script, no scripting plugin found")
return COMPILATION_ERROR
}
return scriptingEvaluator.eval(arguments, environment)
}
val destination = arguments.destination?.let { File(it) }
val buildFile = arguments.buildFile?.let { File(it) }
val moduleChunk = if (buildFile != null) {
fun strongWarning(message: String) {
messageCollector.report(STRONG_WARNING, message)
}
@@ -126,66 +127,37 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
val sanitizedCollector = FilteringMessageCollector(messageCollector, VERBOSE::contains)
val buildFile = File(arguments.buildFile)
val moduleChunk = CompileEnvironmentUtil.loadModuleChunk(buildFile, sanitizedCollector)
configuration.put(JVMConfigurationKeys.MODULE_XML_FILE, buildFile)
KotlinToJVMBytecodeCompiler.configureSourceRoots(configuration, moduleChunk.modules, buildFile)
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
environment.registerJavacIfNeeded(arguments).let {
if (!it) return COMPILATION_ERROR
}
KotlinToJVMBytecodeCompiler.compileModules(environment, buildFile, moduleChunk.modules)
} else if (arguments.script) {
val sourcePath = arguments.freeArgs.first()
configuration.addKotlinSourceRoot(sourcePath)
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
val scriptingEvaluator = ScriptEvaluationExtension.getInstances(environment.project).find { it.isAccepted(arguments) }
if (scriptingEvaluator == null) {
messageCollector.report(ERROR, "Unable to evaluate script, no scripting plugin found")
return COMPILATION_ERROR
}
return scriptingEvaluator.eval(arguments, environment)
CompileEnvironmentUtil.loadModuleChunk(buildFile, sanitizedCollector)
} else {
if (destination != null) {
if (destination.endsWith(".jar")) {
configuration.put(JVMConfigurationKeys.OUTPUT_JAR, File(destination))
if (destination.path.endsWith(".jar")) {
configuration.put(JVMConfigurationKeys.OUTPUT_JAR, destination)
} else {
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(destination))
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, destination)
}
}
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
val module = ModuleBuilder(moduleName, destination?.path ?: ".", "java-production")
module.configureFromArgs(arguments)
environment.registerJavacIfNeeded(arguments).let {
if (!it) return COMPILATION_ERROR
}
if (module.getSourceFiles().isEmpty()) {
if (arguments.version) return OK
if (environment.getSourceFiles().isEmpty()) {
if (arguments.version) {
return OK
}
messageCollector.report(ERROR, "No source files")
return COMPILATION_ERROR
}
KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment)
compileJavaFilesIfNeeded(environment, arguments).let {
if (!it) return COMPILATION_ERROR
}
ModuleChunk(listOf(module))
}
KotlinToJVMBytecodeCompiler.configureSourceRoots(configuration, moduleChunk.modules, buildFile)
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
environment.registerJavacIfNeeded(arguments).let {
if (!it) return COMPILATION_ERROR
}
KotlinToJVMBytecodeCompiler.compileModules(environment, buildFile, moduleChunk.modules)
return OK
} catch (e: CompilationException) {
messageCollector.report(
@@ -197,6 +169,30 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
}
private fun ModuleBuilder.configureFromArgs(args: K2JVMCompilerArguments) {
args.friendPaths?.forEach { addFriendDir(it) }
args.classpath?.split(File.pathSeparator)?.forEach { addClasspathEntry(it) }
args.javaSourceRoots?.forEach {
addJavaSourceRoot(JavaRootPath(it, args.javaPackagePrefix))
}
val commonSources = args.commonSources?.toSet().orEmpty()
for (arg in args.freeArgs) {
if (arg.endsWith(JavaFileType.DOT_DEFAULT_EXTENSION)) {
addJavaSourceRoot(JavaRootPath(arg, args.javaPackagePrefix))
} else {
addSourceFiles(arg)
if (arg in commonSources) {
addCommonSourceFiles(arg)
}
if (File(arg).isDirectory) {
addJavaSourceRoot(JavaRootPath(arg, args.javaPackagePrefix))
}
}
}
}
private fun loadPlugins(arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration): ExitCode {
var pluginClasspaths: Iterable<String> = arguments.pluginClasspaths?.asIterable() ?: emptyList()
val pluginOptions = arguments.pluginOptions?.toMutableList() ?: ArrayList()
@@ -240,16 +236,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
return PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, configuration)
}
private fun compileJavaFilesIfNeeded(
environment: KotlinCoreEnvironment,
arguments: K2JVMCompilerArguments
): Boolean {
if (arguments.compileJava) {
return JavacWrapper.getInstance(environment.project).use { it.compile() }
}
return true
}
private fun createCoreEnvironment(
rootDisposable: Disposable,
configuration: CompilerConfiguration,
@@ -45,10 +45,7 @@ import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.codegen.*
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
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.javac.JavacWrapper
@@ -101,7 +98,7 @@ object KotlinToJVMBytecodeCompiler {
}
}
internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File, chunk: List<Module>): Boolean {
internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File?, chunk: List<Module>): Boolean {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
@@ -131,22 +128,37 @@ object KotlinToJVMBytecodeCompiler {
for (module in chunk) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val (moduleSourceDirs, moduleSourceFiles) =
getAbsolutePaths(buildFile, module.getSourceFiles())
val ktFiles = if (chunk.size > 1) {
// filter out source files from other modules
assert(buildFile != null) { "Compiling multiple modules, but build file is null" }
val (moduleSourceDirs, moduleSourceFiles) =
getBuildFilePaths(buildFile, module.getSourceFiles())
.mapNotNull(localFileSystem::findFileByPath)
.partition(VirtualFile::isDirectory)
val ktFiles = environment.getSourceFiles().filter { file ->
val virtualFile = file.virtualFile
virtualFile in moduleSourceFiles || moduleSourceDirs.any { dir ->
VfsUtilCore.isAncestor(dir, virtualFile, true)
environment.getSourceFiles().filter { file ->
val virtualFile = file.virtualFile
virtualFile in moduleSourceFiles || moduleSourceDirs.any { dir ->
VfsUtilCore.isAncestor(dir, virtualFile, true)
}
}
} else {
environment.getSourceFiles()
}
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
val moduleConfiguration = projectConfiguration.copy().apply {
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
if (buildFile != null) {
fun checkKeyIsNull(key: CompilerConfigurationKey<*>, name: String) {
assert(get(key) == null) { "$name should be null, when buildFile is used" }
}
checkKeyIsNull(JVMConfigurationKeys.OUTPUT_DIRECTORY, "OUTPUT_DIRECTORY")
checkKeyIsNull(JVMConfigurationKeys.OUTPUT_JAR, "OUTPUT_JAR")
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
}
}
outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module)
@@ -180,11 +192,11 @@ object KotlinToJVMBytecodeCompiler {
}
}
internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, buildFile: File) {
internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, buildFile: File? = null) {
for (module in chunk) {
val commonSources = getAbsolutePaths(buildFile, module.getCommonSourceFiles()).toSet()
val commonSources = getBuildFilePaths(buildFile, module.getCommonSourceFiles()).toSet()
for (path in getAbsolutePaths(buildFile, module.getSourceFiles())) {
for (path in getBuildFilePaths(buildFile, module.getSourceFiles())) {
configuration.addKotlinSourceRoot(path, isCommon = path in commonSources)
}
}
@@ -226,8 +238,9 @@ object KotlinToJVMBytecodeCompiler {
configuration.addAll(JVMConfigurationKeys.MODULES, chunk)
}
private fun getAbsolutePaths(buildFile: File, sourceFilePaths: List<String>): List<String> =
sourceFilePaths.map { path ->
private fun getBuildFilePaths(buildFile: File?, sourceFilePaths: List<String>): List<String> =
if (buildFile == null) sourceFilePaths
else sourceFilePaths.map { path ->
(File(path).takeIf(File::isAbsolute) ?: buildFile.resolveSibling(path)).absolutePath
}