jps bazel compiler - part 4

GitOrigin-RevId: 3e901088d0b54fb09bdec17937f2dd74c0e56905
This commit is contained in:
Vladimir Krivosheev
2025-01-14 13:35:31 +01:00
committed by intellij-monorepo-bot
parent 9d3a497d12
commit ec80730780
36 changed files with 3433 additions and 2716 deletions

3458
MODULE.bazel.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -588,7 +588,7 @@ def _run_jps_builder(
outputs = [output_jar]
abi_jar = output_jar
if not "kt_abi_plugin_incompatible" in ctx.attr.tags:
if False and not "kt_abi_plugin_incompatible" in ctx.attr.tags:
abi_jar = ctx.actions.declare_file(ctx.label.name + ".abi.jar")
outputs.append(abi_jar)
args.add("--abi-out", abi_jar)

View File

@@ -0,0 +1,28 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
kt_jvm_library(
name = "abi",
srcs = glob(["*.kt"]),
kotlinc_opts = "//:rules_jvm_bootstrap_kotlinc_options",
deps = [
"@lib//:kotlin-stdlib",
"//:kotlinx-coroutines-core",
"@lib//:asm",
"//:kotlin-metadata",
"//zip:build-zip",
],
visibility = ["//src/jps-builder/packager:__pkg__"],
)
java_binary(
name = "test-abi-generator",
runtime_deps = [":abi"],
main_class = "org.jetbrains.bazel.jvm.abi.TestAbiGenerator",
jvm_flags = [
"-Xms1024m",
"-Xmx6144m",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
],
)

View File

@@ -1,4 +1,4 @@
package org.jetbrains.bazel.jvm.jps
package org.jetbrains.bazel.jvm.abi
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.Opcodes

View File

@@ -0,0 +1,32 @@
package org.jetbrains.bazel.jvm.abi
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassWriter
import java.nio.file.Path
internal object TestAbiGenerator {
@JvmStatic
fun main(args: Array<String>) {
val userHomeDir = Path.of(System.getProperty("user.home"))
val zip = java.util.zip.ZipFile(userHomeDir.resolve("kotlin-worker/ide-impl.jar").toFile())
try {
//val className = "com.intellij.codeInsight.folding.impl.FoldingUtil"
val className = "com.intellij.ui.AppUIUtilKt"
val classReader = ClassReader(zip.getInputStream(zip.getEntry(className.replace('.', '/') + ".class")))
val classesToBeDeleted = HashSet<String>()
val classWriter = ClassWriter(classReader, 0)
val abiVisitor = AbiClassVisitor(
classVisitor = classWriter,
classesToBeDeleted = classesToBeDeleted,
treatInternalAsPrivate = false,
)
classReader.accept(abiVisitor, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES)
val bytes = classWriter.toByteArray()
//val classNode = ClassNode()
//ClassReader(bytes).accept(classNode, 0)
}
finally {
zip.close()
}
}
}

View File

@@ -1,27 +1,55 @@
package org.jetbrains.bazel.jvm.jps
package org.jetbrains.bazel.jvm.abi
import kotlinx.coroutines.channels.Channel
import org.jetbrains.intellij.build.io.writeZipUsingTempFile
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.tree.AnnotationNode
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.nio.ByteBuffer
import java.nio.file.Path
import kotlin.metadata.*
import kotlin.metadata.jvm.KotlinClassMetadata
import kotlin.metadata.jvm.getterSignature
import kotlin.metadata.jvm.localDelegatedProperties
import kotlin.metadata.jvm.signature
suspend fun writeAbi(abiJar: Path, classChannel: Channel<Pair<ByteArray, ByteArray>>) {
writeZipUsingTempFile(abiJar, indexWriter = null) { stream ->
val classesToBeDeleted = HashSet<String>()
for ((name, classData) in classChannel) {
val classWriter = ClassWriter(0)
val abiClassVisitor = AbiClassVisitor(
classVisitor = classWriter,
classesToBeDeleted = classesToBeDeleted,
treatInternalAsPrivate = false,
)
ClassReader(classData).accept(abiClassVisitor, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES)
if (!abiClassVisitor.isApiClass) {
continue
}
val abiData = classWriter.toByteArray()
stream.writeDataRawEntryWithoutCrc(ByteBuffer.wrap(abiData), name)
}
}
}
/**
* ClassVisitor that strips non-public methods/fields and Kotlin `internal` methods.
*/
internal class AbiClassVisitor(
classVisitor: ClassVisitor,
private val classesToBeDeleted: MutableSet<String>,
private val treatInternalAsPrivate: Boolean,
) : ClassVisitor(Opcodes.API_VERSION, classVisitor) {
// tracks if this class has any public API members
var isApiClass: Boolean = false
var isApiClass: Boolean = true
private set
private val classAnnotations = mutableListOf<Pair<AnnotationNode, Boolean>>()
private val visibleAnnotations = ArrayList<AnnotationNode>()
private val invisibleAnnotations = ArrayList<AnnotationNode>()
private val fields = mutableListOf<FieldNode>()
private val methods = mutableListOf<MethodNode>()
private var kotlinMetadata: Pair<String, KotlinClassMetadata>? = null
@@ -36,19 +64,25 @@ internal class AbiClassVisitor(
}
else {
val annotationNode = AnnotationNode(api, descriptor)
classAnnotations.add(annotationNode to visible)
(if (visible) visibleAnnotations else invisibleAnnotations).add(annotationNode)
return annotationNode
}
}
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<String>?) {
if (access and Opcodes.ACC_PUBLIC == 0) {
classesToBeDeleted.add(name)
}
else {
isApiClass = true
super.visit(version, access, name, signature, superName, interfaces)
isApiClass = (access and Opcodes.ACC_PUBLIC) != 0
if (isApiClass) {
val visibility = getKotlinMetaClass()?.visibility
if (visibility == null || !isPrivateVisibility(visibility, treatInternalAsPrivate)) {
super.visit(version, access, name, signature, superName, interfaces)
return
}
else {
isApiClass = false
}
}
classesToBeDeleted.add(name)
}
override fun visitField(
@@ -58,7 +92,10 @@ internal class AbiClassVisitor(
signature: String?,
value: Any?,
): FieldVisitor? {
if (access and Opcodes.ACC_PUBLIC == 0) {
if ((access and Opcodes.ACC_PUBLIC) == 0) {
return null
}
if (treatInternalAsPrivate && isFieldKotlinInternal(name)) {
return null
}
@@ -74,20 +111,23 @@ internal class AbiClassVisitor(
signature: String?,
exceptions: Array<String>?,
): MethodVisitor? {
// retain public methods and exclude Kotlin `internal` methods
val isPublic = access and Opcodes.ACC_PUBLIC != 0
val isInternal = isKotlinInternal(name)
if (!isPublic || isInternal) {
if ((access and Opcodes.ACC_PUBLIC) == 0) {
return null
}
if (treatInternalAsPrivate && isMethodKotlinInternal(name)) {
return null
}
val methodNode = MethodNode(api, access, name, descriptor, signature, exceptions)
methods.add(methodNode)
return methodNode
val method = MethodNode(api, access, name, descriptor, signature, exceptions)
methods.add(method)
//return ReplaceWithEmptyBody(method, (Type.getArgumentsAndReturnSizes(method.desc) shr 2) - 1)
return method
}
override fun visitEnd() {
classAnnotations.sortBy { it.first.desc }
visibleAnnotations.sortBy { it.desc }
invisibleAnnotations.sortBy { it.desc }
fields.sortBy { it.name }
methods.sortBy { it.name }
@@ -97,12 +137,16 @@ internal class AbiClassVisitor(
descriptor = descriptor,
classVisitor = cv,
classesToBeDeleted = classesToBeDeleted,
treatInternalAsPrivate = treatInternalAsPrivate,
)
}
for ((annotation, visible) in classAnnotations) {
val annotationVisitor = cv.visitAnnotation(annotation.desc, visible)
annotation.accept(annotationVisitor)
for (annotation in visibleAnnotations) {
annotation.accept(cv.visitAnnotation(annotation.desc, true))
}
for (annotation in invisibleAnnotations) {
annotation.accept(cv.visitAnnotation(annotation.desc, false))
}
for (field in fields) {
@@ -110,22 +154,28 @@ internal class AbiClassVisitor(
}
for (method in methods) {
method.accept(cv)
//val exceptionsArray = if (method.exceptions == null) null else method.exceptions.toTypedArray()
//val methodVisitor = cv.visitMethod(method.access, method.name, method.desc, method.signature, exceptionsArray)
//method.accept(methodVisitor)
val mv = cv.visitMethod(method.access, method.name, method.desc, method.signature, method.exceptions?.toTypedArray())
val stripper = MethodBodyStripper(mv)
stripper.visitCode()
stripper.visitMaxs(0, 0)
stripper.visitEnd()
//ReplaceWithEmptyBody(
// targetWriter = method,
// newMaxLocals = (Type.getArgumentsAndReturnSizes(method.desc) shr 2) - 1,
//)
//
//val mv = cv.visitMethod(method.access, method.name, method.desc, method.signature, method.exceptions?.toTypedArray())
//val stripper = MethodBodyStripper(mv)
//stripper.visitCode()
//stripper.visitMaxs(0, 0)
//stripper.visitEnd()
}
super.visitEnd()
}
private fun isKotlinInternal(methodName: String?): Boolean {
val kClass = (kotlinMetadata?.second as? KotlinClassMetadata.Class)?.kmClass ?: return false
private fun isMethodKotlinInternal(methodName: String?): Boolean {
val kClass = getKotlinMetaClass() ?: return false
for (function in kClass.functions) {
if (function.visibility == Visibility.INTERNAL && function.name == methodName) {
return true
@@ -133,6 +183,46 @@ internal class AbiClassVisitor(
}
return false
}
private fun isFieldKotlinInternal(name: String?): Boolean {
val kClass = getKotlinMetaClass() ?: return false
for (property in kClass.properties) {
if (property.visibility == Visibility.INTERNAL && property.name == name) {
return true
}
}
return false
}
private fun getKotlinMetaClass(): KmClass? = (kotlinMetadata?.second as? KotlinClassMetadata.Class)?.kmClass
}
// now, we're not passing the writer to the superclass for our radical changes
private class ReplaceWithEmptyBody(
private val targetWriter: MethodVisitor,
private val newMaxLocals: Int,
) : MethodVisitor(Opcodes.API_VERSION) {
// we're only override the minimum to create a code attribute with a sole RETURN
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
targetWriter.visitMaxs(0, newMaxLocals)
}
override fun visitCode() {
targetWriter.visitCode()
targetWriter.visitInsn(Opcodes.RETURN)
}
override fun visitEnd() {
targetWriter.visitEnd()
}
override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor {
return targetWriter.visitAnnotation(desc, visible)
}
override fun visitParameter(name: String?, access: Int) {
targetWriter.visitParameter(name, access)
}
}
private class MethodBodyStripper(mv: MethodVisitor) : MethodVisitor(Opcodes.API_VERSION, mv) {
@@ -152,8 +242,8 @@ private fun transformAndWriteKotlinMetadata(
descriptor: String,
classVisitor: ClassVisitor,
classesToBeDeleted: Set<String>,
treatInternalAsPrivate: Boolean,
) {
val treatInternalAsPrivate = false
when (metadata) {
is KotlinClassMetadata.Class -> {
removePrivateDeclarationsForClass(
@@ -219,10 +309,10 @@ private fun removePrivateDeclarationsForClass(
pruneClass: Boolean,
treatInternalAsPrivate: Boolean,
) {
klass.constructors.removeIf { pruneClass || it.visibility.shouldRemove(treatInternalAsPrivate) }
klass.constructors.removeIf { pruneClass || isPrivateVisibility(it.visibility, treatInternalAsPrivate) }
removePrivateDeclarationsForDeclarationContainer(
container = klass as KmDeclarationContainer,
copyFunShouldBeDeleted = klass.copyFunShouldBeDeleted(removeDataClassCopy = removeCopyAlongWithConstructor),
copyFunShouldBeDeleted = copyFunShouldBeDeleted(klass = klass, removeDataClassCopy = removeCopyAlongWithConstructor),
preserveDeclarationOrder = preserveDeclarationOrder,
pruneClass = pruneClass,
treatInternalAsPrivate = treatInternalAsPrivate,
@@ -232,12 +322,15 @@ private fun removePrivateDeclarationsForClass(
klass.localDelegatedProperties.clear()
}
private fun KmClass.copyFunShouldBeDeleted(removeDataClassCopy: Boolean): Boolean {
return removeDataClassCopy && isData && constructors.none { !it.isSecondary }
private fun copyFunShouldBeDeleted(klass: KmClass, removeDataClassCopy: Boolean): Boolean {
return removeDataClassCopy && klass.isData && klass.constructors.none { !it.isSecondary }
}
private fun Visibility.shouldRemove(treatInternalAsPrivate: Boolean): Boolean {
return this == Visibility.PRIVATE || this == Visibility.PRIVATE_TO_THIS || this == Visibility.LOCAL || (treatInternalAsPrivate && this == Visibility.INTERNAL)
private fun isPrivateVisibility(visibility: Visibility, treatInternalAsPrivate: Boolean): Boolean {
return visibility == Visibility.PRIVATE ||
visibility == Visibility.PRIVATE_TO_THIS ||
visibility == Visibility.LOCAL ||
(treatInternalAsPrivate && visibility == Visibility.INTERNAL)
}
private fun removePrivateDeclarationsForDeclarationContainer(
@@ -248,10 +341,10 @@ private fun removePrivateDeclarationsForDeclarationContainer(
treatInternalAsPrivate: Boolean,
) {
container.functions.removeIf {
pruneClass || it.visibility.shouldRemove(treatInternalAsPrivate) || (copyFunShouldBeDeleted && it.name == "copy")
pruneClass || isPrivateVisibility(it.visibility, treatInternalAsPrivate) || (copyFunShouldBeDeleted && it.name == "copy")
}
container.properties.removeIf {
pruneClass || it.visibility.shouldRemove(treatInternalAsPrivate)
pruneClass || isPrivateVisibility(it.visibility, treatInternalAsPrivate)
}
if (!preserveDeclarationOrder) {

View File

@@ -4,7 +4,12 @@ package org.jetbrains.bazel.jvm.kotlin
import java.util.*
class ArgMap<T : Enum<T>> internal constructor(private val map: EnumMap<T, MutableList<String>>) {
fun mandatorySingle(key: T): String = requireNotNull(optionalSingle(key)) { "$key is not optional" }
fun mandatorySingle(key: T): String {
val value = optionalSingle(key)
requireNotNull(value) { "$key is not optional" }
require(value.isNotBlank()) { "--$key should not be blank" }
return value
}
fun optionalSingle(key: T): String? {
return map[key]?.let {

View File

@@ -1,20 +1,17 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.bazel.jvm.kotlin
import org.jetbrains.intellij.build.io.W_OVERWRITE
import org.jetbrains.intellij.build.io.ZipArchiveOutputStream
import org.jetbrains.intellij.build.io.ZipIndexWriter
import org.jetbrains.intellij.build.io.writeZipUsingTempFile
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
import org.jetbrains.kotlin.name.FqName
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.Path
import java.util.jar.Attributes
import java.util.jar.JarFile
import java.util.jar.Manifest
import java.util.zip.ZipEntry
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
import org.jetbrains.kotlin.name.FqName
import java.nio.channels.FileChannel
import java.nio.file.Files
data class JarOwner(
@JvmField val jar: Path,
@@ -22,8 +19,7 @@ data class JarOwner(
@JvmField val aspect: String? = null,
) {
companion object {
// These attributes are used by JavaBuilder, Turbine, and ijar.
// They must all be kept in sync.
// These attributes are used by JavaBuilder, Turbine, and `ijar`. They must all be kept in sync.
@JvmField
val TARGET_LABEL = Attributes.Name("Target-Label")
@@ -40,17 +36,7 @@ private fun writeManifest(manifest: Manifest, out: ZipArchiveOutputStream) {
val manifestOut = ByteArrayOutputStream()
manifest.write(manifestOut)
val data = manifestOut.toByteArray()
val size = data.size
out.writeDataRawEntry(
data = ByteBuffer.wrap(data),
name = MANIFEST_NAME_BYTES,
size = size,
compressedSize = size,
method = ZipEntry.STORED,
crc = 0,
)
out.writeDataRawEntryWithoutCrc(data = ByteBuffer.wrap(manifestOut.toByteArray()), name = MANIFEST_NAME_BYTES)
}
fun createJar(
@@ -63,11 +49,7 @@ fun createJar(
outFile.parent?.let {
Files.createDirectories(it)
}
ZipArchiveOutputStream(
channel = FileChannel.open(outFile, W_OVERWRITE),
zipIndexWriter = ZipIndexWriter(indexWriter = null),
).use { out ->
writeZipUsingTempFile(outFile, indexWriter = null) { out ->
val manifest = Manifest()
val attributes = manifest.mainAttributes
attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0")
@@ -83,14 +65,7 @@ fun createJar(
for (outputFile in outputFiles.asList()) {
val data = outputFile.asByteArray()
out.writeDataRawEntry(
data = ByteBuffer.wrap(data),
name = outputFile.relativePath.toByteArray(),
size = data.size,
compressedSize = data.size,
method = ZipEntry.STORED,
crc = 0,
)
out.writeDataRawEntryWithoutCrc(data = ByteBuffer.wrap(data), name = outputFile.relativePath.toByteArray())
}
}
}

View File

@@ -1,6 +1,7 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_jvm//:jvm.bzl", "jvm_import")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
load("//:src/jvm-args.bzl", "get_jvm_flags")
kt_jvm_library(
name = "worker-lib",
@@ -49,20 +50,12 @@ java_binary(
"@kotlin-serialization-compiler-plugin//file",
],
main_class = "org.jetbrains.bazel.jvm.jps.JpsBuildWorker",
jvm_flags = [
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED",
"--add-opens=java.base/java.nio=ALL-UNNAMED",
"-Xms1024m",
"-Xmx6144m",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
jvm_flags = get_jvm_flags([
"-Dkotlin.environment.keepalive=true",
"-Djps.use.experimental.storage=true",
"-Djps.kotlin.home=$(rlocationpath @kotlinc//:kotlinc_dist)",
"-Dorg.jetbrains.kotlin.kotlin-serialization-compiler-plugin.path=$(rlocationpath @kotlin-serialization-compiler-plugin//file)",
],
]),
visibility = ["//visibility:public"],
)
@@ -74,18 +67,10 @@ java_binary(
"@kotlin-serialization-compiler-plugin//file",
],
main_class = "org.jetbrains.bazel.jvm.jps.TestJpsBuildWorker",
jvm_flags = [
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED",
"--add-opens=java.base/java.nio=ALL-UNNAMED",
"-Xms1024m",
"-Xmx6144m",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
jvm_flags = get_jvm_flags([
"-Dkotlin.environment.keepalive=true",
"-Djps.use.experimental.storage=true",
"-Djps.kotlin.home=$(rlocationpath @kotlinc//:kotlinc_dist)",
"-Dorg.jetbrains.kotlin.kotlin-serialization-compiler-plugin.path=$(rlocationpath @kotlin-serialization-compiler-plugin//file)",
],
]),
)

View File

@@ -47,7 +47,6 @@ internal fun loadJpsModel(
classPathRootDir: Path,
classOutDir: Path,
dependencyFileToDigest: Map<Path, ByteArray>,
messageHandler: ConsoleMessageHandler,
): Pair<JpsModel, TargetConfigurationDigestContainer> {
val model = jpsElementFactory.createModel()
@@ -61,7 +60,7 @@ internal fun loadJpsModel(
// extension.loadModuleOptions not needed for us (not implemented for java)
val module = JpsModuleImpl(
JpsJavaModuleType.INSTANCE,
args.mandatorySingle(JvmBuilderFlags.TARGET_LABEL),
args.mandatorySingle(JvmBuilderFlags.KOTLIN_MODULE_NAME),
jpsElementFactory.createDummyElement(),
)
val jpsJavaModuleExtension = JpsJavaExtensionService.getInstance().getOrCreateModuleExtension(module)
@@ -79,10 +78,6 @@ internal fun loadJpsModel(
module.addSourceRoot(JpsModuleSourceRootImpl(source.toUri().toString(), JavaSourceRootType.SOURCE, properties))
}
if (messageHandler.isDebugEnabled) {
messageHandler.info("sources:\n ${sources.joinToString(separator = "\n ") { it.invariantSeparatorsPathString }}\n")
}
configureKotlinCompiler(module = module, args = args, classPathRootDir = classPathRootDir, configHash = configHash)
val dependencyList = module.dependenciesList

View File

@@ -3,8 +3,6 @@
package org.jetbrains.bazel.jvm.jps
import org.jetbrains.bazel.jvm.jps.impl.BazelKotlinBuilder
import org.jetbrains.jps.backwardRefs.JavaBackwardReferenceIndexBuilder
import org.jetbrains.jps.builders.AdditionalRootsProviderService
import org.jetbrains.jps.builders.PreloadedDataExtension
import org.jetbrains.jps.builders.impl.java.JavacCompilerTool
@@ -13,11 +11,9 @@ import org.jetbrains.jps.builders.java.JavaCompilingTool
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
import org.jetbrains.jps.incremental.BuilderService
import org.jetbrains.jps.incremental.ModuleLevelBuilder
import org.jetbrains.jps.incremental.java.JavaBuilder
import org.jetbrains.jps.model.*
import org.jetbrains.jps.service.JpsServiceManager
import org.jetbrains.jps.service.SharedThreadPool
import org.jetbrains.kotlin.jps.incremental.KotlinCompilerReferenceIndexBuilder
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@@ -117,14 +113,8 @@ private object BazelJavaBuilderService : BuilderService() {
// remove ResourcesTargetType.ALL_TYPES and ProjectDependenciesResolver.ProjectDependenciesResolvingTargetType.INSTANCE
override fun getTargetTypes() = listOf(JavaModuleBuildTargetType.PRODUCTION)
// remove RmiStubsGenerator and DependencyResolvingBuilder
// remove RmiStubsGenerator and DependencyResolvingBuilder
override fun createModuleLevelBuilders(): List<ModuleLevelBuilder> {
return listOf(
JavaBuilder(SharedThreadPool.getInstance()),
//NotNullInstrumentingBuilder(),
JavaBackwardReferenceIndexBuilder(),
BazelKotlinBuilder(),
KotlinCompilerReferenceIndexBuilder(),
)
throw IllegalStateException("must not be called")
}
}

View File

@@ -0,0 +1,149 @@
@file:Suppress("UnstableApiUsage")
package org.jetbrains.bazel.jvm.jps
import com.google.protobuf.CodedInputStream
import com.google.protobuf.CodedOutputStream
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import org.jetbrains.intellij.build.io.unmapBuffer
import org.jetbrains.intellij.build.io.writeFileUsingTempFile
import org.jetbrains.jps.incremental.storage.PathTypeAwareRelativizer
import org.jetbrains.jps.incremental.storage.RelativePathType
import org.jetbrains.kotlin.utils.addToStdlib.enumSetOf
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.util.zip.CRC32
private const val STATE_FILE_FORMAT_VERSION = 1
private val WRITE_FILE_OPTION = enumSetOf(StandardOpenOption.WRITE, StandardOpenOption.CREATE)
private val READ_FILE_OPTION = enumSetOf(StandardOpenOption.READ)
internal fun loadBuildState(
buildStateFile: Path,
relativizer: PathTypeAwareRelativizer,
log: RequestLog,
): HashMap<Path, SourceDescriptor>? {
var map: MappedByteBuffer? = null
try {
map = FileChannel.open(buildStateFile, READ_FILE_OPTION).use {
it.map(FileChannel.MapMode.READ_ONLY, 0, it.size())
}
return doLoad(map, relativizer)
}
catch (_: NoSuchFileException) {
return null
}
catch (e: Throwable) {
log.error("cannot load $buildStateFile", e)
// will be deleted by caller
return null
}
finally {
map?.let { unmapBuffer(it) }
}
}
internal fun saveBuildState(buildStateFile: Path, list: Array<SourceDescriptor>, relativizer: PathTypeAwareRelativizer) {
val byteArrayOutputStream = BufferExposingByteArrayOutputStream()
val output = CodedOutputStream.newInstance(byteArrayOutputStream)
output.writeFixed32NoTag(STATE_FILE_FORMAT_VERSION)
// allocate for checksum
output.writeFixed64NoTag(0L)
output.writeUInt32NoTag(list.size)
for (descriptor in list) {
output.writeStringNoTag(relativizer.toRelative(descriptor.sourceFile, RelativePathType.SOURCE))
output.writeByteArrayNoTag(descriptor.digest)
val outputs = descriptor.outputs
if (outputs == null) {
output.writeUInt32NoTag(0)
}
else {
output.writeUInt32NoTag(outputs.size)
for (outputPath in outputs) {
output.writeStringNoTag(outputPath)
}
}
}
// allocate for checksum
output.writeFixed64NoTag(0L)
output.flush()
val data = byteArrayOutputStream.internalBuffer
val dataSize = byteArrayOutputStream.size()
val crc32 = CRC32()
val dataOffset = Int.SIZE_BYTES + Long.SIZE_BYTES
crc32.update(data, dataOffset, dataSize - dataOffset)
val checksum = crc32.value
CodedOutputStream.newInstance(data, Int.SIZE_BYTES, dataOffset).writeFixed64NoTag(checksum)
CodedOutputStream.newInstance(data, dataSize - Long.SIZE_BYTES, dataOffset).writeFixed64NoTag(checksum)
val parent = buildStateFile.parent
Files.createDirectories(parent)
writeFileUsingTempFile(buildStateFile) { tempFile ->
FileChannel.open(tempFile, WRITE_FILE_OPTION).use {
it.write(ByteBuffer.wrap(data, 0, dataSize), 0)
}
}
}
private fun doLoad(byteBuffer: MappedByteBuffer, relativizer: PathTypeAwareRelativizer): HashMap<Path, SourceDescriptor> {
val fileSize = byteBuffer.limit()
val crc32 = CRC32()
byteBuffer.mark()
byteBuffer.position(Int.SIZE_BYTES + Long.SIZE_BYTES).limit(fileSize - Long.SIZE_BYTES)
crc32.update(byteBuffer)
val expectedChecksum = crc32.value
byteBuffer.position(0).limit(fileSize)
val input = CodedInputStream.newInstance(byteBuffer)
val formatVersion = input.readRawLittleEndian32()
if (formatVersion != STATE_FILE_FORMAT_VERSION) {
throw IOException("format version mismatch: expected $STATE_FILE_FORMAT_VERSION, actual $formatVersion")
}
val storedChecksum = input.readRawLittleEndian64()
if (storedChecksum != expectedChecksum) {
throw IOException("checksum mismatch: expected $expectedChecksum, actual $storedChecksum (file start)")
}
val size = input.readRawVarint32()
val map = HashMap<Path, SourceDescriptor>(size)
repeat(size) {
val source = input.readStringRequireUtf8()
val digest = input.readByteArray()
val inputSize = input.readRawVarint32()
val outputs = if (inputSize == 0) {
null
}
else {
Array(inputSize) { input.readStringRequireUtf8() }.asList()
}
val sourceFile = relativizer.toAbsoluteFile(source, RelativePathType.SOURCE)
map.put(sourceFile, SourceDescriptor(
sourceFile = sourceFile,
digest = digest,
outputs = outputs,
))
}
val storedChecksumEnd = input.readRawLittleEndian64()
if (storedChecksumEnd != expectedChecksum) {
throw IOException("checksum mismatch: expected $expectedChecksum, actual $storedChecksumEnd (file end)")
}
return map
}

View File

@@ -5,10 +5,8 @@ package org.jetbrains.bazel.jvm.jps
import com.intellij.openapi.diagnostic.IdeaLogRecordFormatter
import com.intellij.openapi.diagnostic.Logger
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import com.intellij.openapi.util.io.FileUtilRt
import kotlinx.coroutines.*
import org.jetbrains.bazel.jvm.WorkRequest
import org.jetbrains.bazel.jvm.WorkRequestExecutor
import org.jetbrains.bazel.jvm.jps.impl.*
@@ -21,13 +19,18 @@ import org.jetbrains.bazel.jvm.logging.LogWriter
import org.jetbrains.bazel.jvm.processRequests
import org.jetbrains.jps.api.CanceledStatus
import org.jetbrains.jps.api.GlobalOptions
import org.jetbrains.jps.backwardRefs.JavaBackwardReferenceIndexBuilder
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
import org.jetbrains.jps.incremental.*
import org.jetbrains.jps.incremental.CompileContextImpl
import org.jetbrains.jps.incremental.CompileScopeImpl
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.incremental.RebuildRequestedException
import org.jetbrains.jps.incremental.java.JavaBuilder
import org.jetbrains.jps.incremental.relativizer.PathRelativizerService
import org.jetbrains.jps.incremental.storage.ExperimentalSourceToOutputMapping
import org.jetbrains.jps.incremental.storage.StorageManager
import org.jetbrains.jps.model.JpsModel
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.jps.incremental.KotlinCompilerReferenceIndexBuilder
import java.io.Writer
import java.nio.file.Files
import java.nio.file.Path
@@ -66,10 +69,13 @@ object JpsBuildWorker : WorkRequestExecutor {
override suspend fun execute(request: WorkRequest, writer: Writer, baseDir: Path): Int {
val dependencyFileToDigest = HashMap<Path, ByteArray>()
val sourceFileToDigest = HashMap<Path, ByteArray>(request.inputs.size)
val sources = ArrayList<Path>()
for (input in request.inputs) {
if (input.path.endsWith(".kt") || input.path.endsWith(".java")) {
sources.add(baseDir.resolve(input.path).normalize())
val file = baseDir.resolve(input.path).normalize()
sources.add(file)
sourceFileToDigest.put(file, input.digest)
}
else if (input.path.endsWith(".jar")) {
dependencyFileToDigest.put(baseDir.resolve(input.path).normalize(), input.digest)
@@ -83,6 +89,7 @@ object JpsBuildWorker : WorkRequestExecutor {
sources = sources,
dependencyFileToDigest = dependencyFileToDigest,
isDebugEnabled = request.verbosity > 0,
sourceFileToDigest = sourceFileToDigest,
)
}
}
@@ -93,9 +100,10 @@ internal suspend fun buildUsingJps(
out: Writer,
sources: List<Path>,
dependencyFileToDigest: Map<Path, ByteArray>,
sourceFileToDigest: Map<Path, ByteArray>,
isDebugEnabled: Boolean,
): Int {
val messageHandler = ConsoleMessageHandler(out = out, isDebugEnabled = isDebugEnabled)
val log = RequestLog(out = out, isDebugEnabled = isDebugEnabled)
val abiJar = args.optionalSingle(JvmBuilderFlags.ABI_OUT)?.let { baseDir.resolve(it).normalize() }
val outJar = baseDir.resolve(args.mandatorySingle(JvmBuilderFlags.OUT)).normalize()
@@ -111,8 +119,7 @@ internal suspend fun buildUsingJps(
args = args,
classPathRootDir = baseDir,
classOutDir = classOutDir,
dependencyFileToDigest = dependencyFileToDigest,
messageHandler = messageHandler
dependencyFileToDigest = dependencyFileToDigest
)
val moduleTarget = BazelModuleBuildTarget(
outDir = classOutDir,
@@ -123,9 +130,28 @@ internal suspend fun buildUsingJps(
val relativizer = createPathRelativizer(baseDir = baseDir, classOutDir = classOutDir)
// if class output dir doesn't exist, make sure that we do not to use existing cache - pass `isRebuild` as true in this case
val ok = initAndBuild(
isRebuild = Files.notExists(classOutDir),
messageHandler = messageHandler,
var isRebuild = Files.notExists(classOutDir)
val buildStateFile = dataDir.resolve("$prefix-state-v1.db")
val typeAwareRelativizer = relativizer.typeAwareRelativizer!!
val buildState = loadBuildState(buildStateFile, typeAwareRelativizer, log)
if (buildState == null) {
FileUtilRt.deleteRecursively(dataDir)
FileUtilRt.deleteRecursively(classOutDir)
isRebuild = true
}
val buildDataProvider = BazelBuildDataProvider(
relativizer = typeAwareRelativizer,
actualDigestMap = sourceFileToDigest,
sourceToDescriptor = buildState ?: HashMap(sourceFileToDigest.size),
storeFile = buildStateFile,
)
var exitCode = initAndBuild(
isRebuild = isRebuild,
messageHandler = log,
dataDir = dataDir,
classOutDir = classOutDir,
targetDigests = targetDigests,
@@ -134,11 +160,13 @@ internal suspend fun buildUsingJps(
abiJar = abiJar,
relativizer = relativizer,
jpsModel = jpsModel,
buildDataProvider = buildDataProvider,
)
if (!ok) {
initAndBuild(
if (exitCode == -1) {
log.resetState()
exitCode = initAndBuild(
isRebuild = true,
messageHandler = messageHandler,
messageHandler = log,
dataDir = dataDir,
classOutDir = classOutDir,
targetDigests = targetDigests,
@@ -147,15 +175,16 @@ internal suspend fun buildUsingJps(
abiJar = abiJar,
relativizer = relativizer,
jpsModel = jpsModel,
buildDataProvider = buildDataProvider,
)
}
return if (messageHandler.hasErrors()) 1 else 0
return exitCode
}
private suspend fun initAndBuild(
isRebuild: Boolean,
messageHandler: ConsoleMessageHandler,
messageHandler: RequestLog,
dataDir: Path,
classOutDir: Path,
targetDigests: TargetConfigurationDigestContainer,
@@ -164,9 +193,8 @@ private suspend fun initAndBuild(
abiJar: Path?,
relativizer: PathRelativizerService,
jpsModel: JpsModel,
): Boolean {
messageHandler.resetState()
buildDataProvider: BazelBuildDataProvider,
): Int {
if (messageHandler.isDebugEnabled) {
messageHandler.info("build (isRebuild=$isRebuild)")
}
@@ -180,7 +208,13 @@ private suspend fun initAndBuild(
}
try {
val projectDescriptor = storageInitializer.createProjectDescriptor(messageHandler, jpsModel, moduleTarget, relativizer)
val projectDescriptor = storageInitializer.createProjectDescriptor(
messageHandler = messageHandler,
jpsModel = jpsModel,
moduleTarget = moduleTarget,
relativizer = relativizer,
buildDataProvider = buildDataProvider,
)
try {
val compileScope = CompileScopeImpl(
/* types = */ java.util.Set.of(JavaModuleBuildTargetType.PRODUCTION),
@@ -199,27 +233,48 @@ private suspend fun initAndBuild(
override fun isCanceled(): Boolean = !coroutineContext.isActive
},
)
JpsProjectBuilder(
builderRegistry = BuilderRegistry.getInstance(),
messageHandler = messageHandler,
val builders = arrayOf(
JavaBuilder(BazelSharedThreadPool),
//NotNullInstrumentingBuilder(),
JavaBackwardReferenceIndexBuilder(),
BazelKotlinBuilder(isKotlinBuilderInDumbMode = false, log = messageHandler),
KotlinCompilerReferenceIndexBuilder(),
)
builders.sortBy { it.category.ordinal }
val exitCode = JpsProjectBuilder(
log = messageHandler,
isCleanBuild = storageInitializer.isCleanBuild,
).build(context, moduleTarget)
if (!messageHandler.hasErrors()) {
postBuild(
messageHandler = messageHandler,
moduleTarget = moduleTarget,
outJar = outJar,
abiJar = abiJar,
classOutDir = classOutDir,
context = context,
targetDigests = targetDigests,
storageManager = storageManager,
)
dataManager = buildDataProvider,
).build(context = context, moduleTarget = moduleTarget, builders = builders)
if (exitCode == 0) {
try {
postBuild(
messageHandler = messageHandler,
moduleTarget = moduleTarget,
outJar = outJar,
abiJar = abiJar,
classOutDir = classOutDir,
context = context,
targetDigests = targetDigests,
storageManager = storageManager,
buildDataProvider = buildDataProvider,
)
}
catch (e: Throwable) {
// in case of any error during packaging - clear build
storageManager.forceClose()
projectDescriptor.release()
storageInitializer.clearStorage()
throw e
}
}
return true
return exitCode
}
catch (_: RebuildRequestedException) {
return false
return -1
}
finally {
projectDescriptor.release()
@@ -231,7 +286,7 @@ private suspend fun initAndBuild(
}
private suspend fun postBuild(
messageHandler: ConsoleMessageHandler,
messageHandler: RequestLog,
moduleTarget: ModuleBuildTarget,
outJar: Path,
abiJar: Path?,
@@ -239,9 +294,11 @@ private suspend fun postBuild(
context: CompileContextImpl,
targetDigests: TargetConfigurationDigestContainer,
storageManager: StorageManager,
buildDataProvider: BazelBuildDataProvider,
) {
coroutineScope {
val dataManager = context.projectDescriptor.dataManager
val sourceDescriptors = buildDataProvider.getFinalList()
launch(CoroutineName("save caches")) {
// save config state only in the end
saveTargetState(
@@ -251,18 +308,21 @@ private suspend fun postBuild(
)
dataManager.flush(/* memoryCachesOnly = */ false)
ensureActive()
saveBuildState(buildDataProvider.storeFile, sourceDescriptors, relativizer = buildDataProvider.relativizer)
}
launch(CoroutineName("create output JAR and ABI JAR")) {
// pack to jar
messageHandler.measureTime("pack and abi") {
val sourceToOutputMap = dataManager.getSourceToOutputMap(moduleTarget) as ExperimentalSourceToOutputMapping
packageToJar(
outJar = outJar,
abiJar = abiJar,
sourceToOutputMap = sourceToOutputMap,
sourceDescriptors = sourceDescriptors,
classOutDir = classOutDir,
messageHandler = messageHandler,
log = messageHandler,
)
}
}

View File

@@ -6,6 +6,7 @@ package org.jetbrains.bazel.jvm.jps
import com.intellij.openapi.util.io.FileUtilRt
import kotlinx.coroutines.ensureActive
import org.h2.mvstore.MVStore
import org.jetbrains.bazel.jvm.jps.impl.BazelBuildDataProvider
import org.jetbrains.bazel.jvm.jps.impl.BazelModuleBuildTarget
import org.jetbrains.bazel.jvm.jps.impl.loadJpsProject
import org.jetbrains.bazel.jvm.jps.state.TargetConfigurationDigestContainer
@@ -33,7 +34,7 @@ internal class StorageInitializer(private val dataDir: Path, private val classOu
var isCleanBuild: Boolean = false
private set
fun clearAndInit(messageHandler: ConsoleMessageHandler): StorageManager {
fun clearAndInit(messageHandler: RequestLog): StorageManager {
isCheckRebuildRequired = false
wasCleared = true
isCleanBuild = true
@@ -47,7 +48,7 @@ internal class StorageInitializer(private val dataDir: Path, private val classOu
.also { storageManager = it }
}
suspend fun init(messageHandler: ConsoleMessageHandler, targetDigests: TargetConfigurationDigestContainer): StorageManager {
suspend fun init(messageHandler: RequestLog, targetDigests: TargetConfigurationDigestContainer): StorageManager {
val logger = createLogger(messageHandler)
coroutineContext.ensureActive()
@@ -100,7 +101,7 @@ internal class StorageInitializer(private val dataDir: Path, private val classOu
return tryOpenMvStore(file = cacheDbFile, readOnly = false, autoCommitDelay = 0, logger = logger)
}
private fun createLogger(messageHandler: ConsoleMessageHandler): StoreLogger {
private fun createLogger(messageHandler: RequestLog): StoreLogger {
return { m: String, e: Throwable, isWarn: Boolean ->
val message = "$m: ${e.stackTraceToString()}"
if (isWarn) {
@@ -113,10 +114,11 @@ internal class StorageInitializer(private val dataDir: Path, private val classOu
}
fun createProjectDescriptor(
messageHandler: ConsoleMessageHandler,
messageHandler: RequestLog,
jpsModel: JpsModel,
moduleTarget: BazelModuleBuildTarget,
relativizer: PathRelativizerService,
buildDataProvider: BazelBuildDataProvider,
): ProjectDescriptor {
try {
return loadJpsProject(
@@ -128,6 +130,7 @@ internal class StorageInitializer(private val dataDir: Path, private val classOu
moduleTarget = moduleTarget,
relativizer = relativizer,
storageManager = storageManager!!,
buildDataProvider = buildDataProvider,
)
}
catch (e: Throwable) {
@@ -144,10 +147,11 @@ internal class StorageInitializer(private val dataDir: Path, private val classOu
jpsModel = jpsModel,
moduleTarget = moduleTarget,
relativizer = relativizer,
buildDataProvider = buildDataProvider,
)
}
private fun clearStorage() {
fun clearStorage() {
isCleanBuild = true
wasCleared = true
isCheckRebuildRequired = false

View File

@@ -1,333 +1,57 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.bazel.jvm.jps
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.jetbrains.bazel.jvm.TestModules
import org.jetbrains.bazel.jvm.collectSources
import org.jetbrains.bazel.jvm.getTestWorkerPaths
import org.jetbrains.bazel.jvm.kotlin.JvmBuilderFlags
import org.jetbrains.bazel.jvm.kotlin.parseArgs
import org.jetbrains.bazel.jvm.logging.LogWriter
import org.jetbrains.bazel.jvm.performTestInvocation
import java.io.PrintStream
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.Path
import java.security.MessageDigest
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.io.path.walk
//private val dateTimeFormatter = DateTimeFormatter.ofPattern("yy-MM-dd_T-HH-mm-ss")
object TestJpsBuildWorker {
internal object TestJpsBuildWorker {
@OptIn(ExperimentalPathApi::class)
@JvmStatic
fun main(startupArgs: Array<String>) {
val userHomeDir = Path.of(System.getProperty("user.home"))
val out = StringWriter()
try {
@Suppress("SpellCheckingInspection")
val baseDir = Path.of("/private/var/tmp/_bazel_develar/c002af20f6ada3e2667e9e2ceaf2ceca/execroot/_main")
val testPaths = getTestWorkerPaths()
val baseDir = testPaths.baseDir
//val sources = collectSources(sourceDirPath = "platform/platform-impl/src", paths = testPaths)
//val sources = Files.newDirectoryStream(userHomeDir.resolve("projects/idea/community/platform/util/xmlDom/src")).use { it.toList() }
val ideaProjectDirName = if (Runtime.getRuntime().availableProcessors() >= 20) "idea-push" else "idea-second"
val communityDir = userHomeDir.resolve("projects/$ideaProjectDirName/community")
val sources = communityDir.resolve("platform/platform-impl/src")
.walk()
.filter {
val p = it.toString()
p.endsWith(".kt") || p.endsWith(".java")
}
.map { "../community+/" + communityDir.relativize(it).invariantSeparatorsPathString }
.sorted()
.map { baseDir.resolve(it).normalize() }
.toList()
val testModule = TestModules.PLATFORM_BOOTSTRAP
val sources = collectSources(sourceDirPath = testModule.sourcePath, paths = testPaths)
val testParams = testModule.getParams(baseDir)
require(sources.isNotEmpty())
performTestInvocation { out, coroutineScope ->
// IDEA console is bad and outdated, write to file and use modern tooling to view logs
// ${dateTimeFormatter.format(LocalDateTime.now())}
val logFile = testPaths.userHomeDir.resolve("kotlin-worker/log.ndjson")
configureGlobalJps(LogWriter(coroutineScope, PrintStream(Files.newOutputStream(logFile)), closeWriterOnShutdown = true))
runBlocking(Dispatchers.Default) {
// IDEA console is bad and outdated, write to file and use modern tooling to view logs
// ${dateTimeFormatter.format(LocalDateTime.now())}
val logFile = userHomeDir.resolve("kotlin-worker/log.ndjson")
configureGlobalJps(LogWriter(this, PrintStream(Files.newOutputStream(logFile)), closeWriterOnShutdown = true))
val args = parseArgs(testParams.trimStart().lines().toTypedArray())
val messageDigest = MessageDigest.getInstance("SHA-256")
buildUsingJps(
baseDir = baseDir,
args = args,
out = out,
sources = sources,
dependencyFileToDigest = args.optionalList(JvmBuilderFlags.CLASSPATH).associate {
val file = baseDir.resolve(it).normalize()
val digest = messageDigest.digest(Files.readAllBytes(file))
messageDigest.reset()
file to digest
},
isDebugEnabled = true,
)
}
}
finally {
System.out.append(out.toString())
val args = parseArgs(testParams.lines().toTypedArray())
val messageDigest = MessageDigest.getInstance("SHA-256")
buildUsingJps(
baseDir = baseDir,
args = args,
out = out,
sources = sources,
dependencyFileToDigest = args.optionalList(JvmBuilderFlags.CLASSPATH).associate {
val file = baseDir.resolve(it).normalize()
val digest = messageDigest.digest(Files.readAllBytes(file))
messageDigest.reset()
file to digest
},
sourceFileToDigest = sources.associate {
val file = baseDir.resolve(it).normalize()
val digest = messageDigest.digest(Files.readAllBytes(file))
messageDigest.reset()
file to digest
},
isDebugEnabled = true,
)
}
}
}
@Suppress("SpellCheckingInspection")
private const val testParams = """
--target_label
@community//platform/platform-impl:ide-impl
--rule_kind
kt_jvm_library
--kotlin_module_name
intellij.platform.ide.impl
--deps_artifacts
bazel-out/community+/darwin_arm64-fastbuild/bin/jps/model-impl/model-impl.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/observable/ide-observable-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/rd-platform-community/rd-community-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util-class-loader/util-classLoader.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/zip/zip.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/xmlDom/xmlDom-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/jdom/jdom.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/text-matching/text-matching.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-impl/rpc/ide-rpc-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/startUpPerformanceReporter/startUpPerformanceReporter-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/backend/observation/observation-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ml-api/ml-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/storages/io-storages.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/core-nio-fs/libcore-nio-fs.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/project/shared/project-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/jbr/jbr.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/util/core/fleet-util-core-kt.jdeps
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/buildData/buildData-kt.jdeps
--warn
off
--jvm-default
all
--jvm-target
17
--opt-in
com.intellij.openapi.util.IntellijInternalApi
org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
--classpath
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/eawtstub/lib+/eawtstub-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-api/ide.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/util.abi.jar
../lib++_repo_rules+annotations-24_0_0_http/file/annotations-24.0.0.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util-rt/util-rt-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/base/base.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/core-api/core.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/extensions/extensions.abi.jar
../lib++_repo_rules+kotlinx-coroutines-core-jvm-1_8_0-intellij-11_http/file/kotlinx-coroutines-core-jvm-1.8.0-intellij-11.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/forms_rt/java-guiForms-rt-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/projectModel-api/projectModel.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/jps/model-api/model-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/editor-ui-api/editor.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/credential-store/credentialStore.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/remote-core/remote-core.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ide-core/ide-core.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/progress/shared/ide-progress.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/oro_matcher/lib++_repo_rules+oro-2_0_8_http/file/oro-2.0.8-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/lang-api/lang.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/lang-core/lang-core.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/lvcs-api/lvcs.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/indexing-api/indexing.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/analysis-api/analysis.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/code-style-api/codeStyle.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/execution/execution.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/refactoring/refactoring.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jna-platform-5_14_0_http_import/lib++_repo_rules+jna-platform-5_14_0_http/file/jna-platform-5.14.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jna-5_14_0_http_import/lib++_repo_rules+jna-5_14_0_http/file/jna-5.14.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/winp/lib++_repo_rules+winp-1_30_1_http/file/winp-1.30.1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/swingx/lib++_repo_rules+swingx-core-1_6_2-2_http/file/swingx-core-1.6.2-2-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/core-impl/core-impl.abi.jar
../lib++_repo_rules+kotlin-stdlib-2_1_0_http/file/kotlin-stdlib-2.1.0.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/miglayout-swing-11_4_http_import/lib++_repo_rules+miglayout-swing-11_4_http/file/miglayout-swing-11.4-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/miglayout-core-11_4_http_import/lib++_repo_rules+miglayout-core-11_4_http/file/miglayout-core-11.4-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/projectModel-impl/projectModel-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/jps/model-serialization/model-serialization-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util-ex/util-ex.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/concurrency/concurrency.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/workspace/storage/storage.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/workspace/jps/jps.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/commons-imaging/lib++_repo_rules+commons-imaging-1_0-RC-1_http/file/commons-imaging-1.0-RC-1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/guava-33_3_1-jre_http_import/lib++_repo_rules+guava-33_3_1-jre_http/file/guava-33.3.1-jre-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/failureaccess-1_0_2_http_import/lib++_repo_rules+failureaccess-1_0_2_http/file/failureaccess-1.0.2-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/j2objc-annotations-3_0_0_http_import/lib++_repo_rules+j2objc-annotations-3_0_0_http/file/j2objc-annotations-3.0.0-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/jps/model-impl/model-impl-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/analysis-impl/analysis-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/editor-ui-ex/editor-ex.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/gson/lib++_repo_rules+gson-2_11_0_http/file/gson-2.11.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/httpmime-4_5_14_http_import/lib++_repo_rules+httpmime-4_5_14_http/file/httpmime-4.5.14-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/httpclient-4_5_14_http_import/lib++_repo_rules+httpclient-4_5_14_http/file/httpclient-4.5.14-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/httpcore-4_4_16_http_import/lib++_repo_rules+httpcore-4_4_16_http/file/httpcore-4.4.16-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diff-api/diff.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/diff/diff.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/imgscalr/lib++_repo_rules+imgscalr-lib-4_2_http/file/imgscalr-lib-4.2-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/built-in-server-api/builtInServer.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/observable/ide-observable.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/stream_ex/lib++_repo_rules+streamex-0_8_2_http/file/streamex-0.8.2-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-codec-http2-4_2_0_RC1_http_import/lib++_repo_rules+netty-codec-http2-4_2_0_RC1_http/file/netty-codec-http2-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-transport-4_2_0_RC1_http_import/lib++_repo_rules+netty-transport-4_2_0_RC1_http/file/netty-transport-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-resolver-4_2_0_RC1_http_import/lib++_repo_rules+netty-resolver-4_2_0_RC1_http/file/netty-resolver-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-codec-4_2_0_RC1_http_import/lib++_repo_rules+netty-codec-4_2_0_RC1_http/file/netty-codec-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-codec-base-4_2_0_RC1_http_import/lib++_repo_rules+netty-codec-base-4_2_0_RC1_http/file/netty-codec-base-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-handler-4_2_0_RC1_http_import/lib++_repo_rules+netty-handler-4_2_0_RC1_http/file/netty-handler-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-transport-native-unix-common-4_2_0_RC1_http_import/lib++_repo_rules+netty-transport-native-unix-common-4_2_0_RC1_http/file/netty-transport-native-unix-common-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-codec-http-4_2_0_RC1_http_import/lib++_repo_rules+netty-codec-http-4_2_0_RC1_http/file/netty-codec-http-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jackson/lib++_repo_rules+jackson-core-2_17_0_http/file/jackson-core-2.17.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/java_compatibility/lib++_repo_rules+java-compatibility-1_0_1_http/file/java-compatibility-1.0.1-ijar.jar
../lib++_repo_rules+kotlin-reflect-2_1_0_http/file/kotlin-reflect-2.1.0.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jackson-databind-2_17_2_http_import/lib++_repo_rules+jackson-databind-2_17_2_http/file/jackson-databind-2.17.2-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jackson-annotations-2_17_2_http_import/lib++_repo_rules+jackson-annotations-2_17_2_http/file/jackson-annotations-2.17.2-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/util-ui.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-util-io-impl/ide-util-io-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-util-io/ide-util-io.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/instanceContainer/instanceContainer.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/service-container/serviceContainer.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jcef/lib++_repo_rules+jcef-122_1_9-gd14e051-chromium-122_0_6261_94-api-1_17-251-b2_http/file/jcef-122.1.9-gd14e051-chromium-122.0.6261.94-api-1.17-251-b2-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/statistics/statistics.abi.jar
../lib++_repo_rules+ap-validation-76_http/file/ap-validation-76.jar
../lib++_repo_rules+model-76_http/file/model-76.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/asm/lib++_repo_rules+asm-all-9_6_1_http/file/asm-all-9.6.1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jsoup/lib++_repo_rules+jsoup-1_18_1_http/file/jsoup-1.18.1-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/rd-platform-community/rd-community.abi.jar
../lib++_repo_rules+rd-core-2024_3_1_http/file/rd-core-2024.3.1.jar
../lib++_repo_rules+rd-framework-2024_3_1_http/file/rd-framework-2024.3.1.jar
../lib++_repo_rules+rd-swing-2024_3_1_http/file/rd-swing-2024.3.1.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/fastutil-min/lib++_repo_rules+intellij-deps-fastutil-8_5_14-jb1_http/file/intellij-deps-fastutil-8.5.14-jb1-ijar.jar
../lib++_repo_rules+blockmap-1_0_7_http/file/blockmap-1.0.7.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util-class-loader/util-classLoader-hjar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-buffer-4_2_0_RC1_http_import/lib++_repo_rules+netty-buffer-4_2_0_RC1_http/file/netty-buffer-4.2.0.RC1-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/netty-common-4_2_0_RC1_http_import/lib++_repo_rules+netty-common-4_2_0_RC1_http/file/netty-common-4.2.0.RC1-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/progress/progress.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/core-ui/core-ui.abi.jar
../lib++_repo_rules+marketplace-zip-signer-0_1_24_http/file/marketplace-zip-signer-0.1.24.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/caffeine/lib++_repo_rules+caffeine-3_1_8_http/file/caffeine-3.1.8-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/classgraph/lib++_repo_rules+classgraph-4_8_174_http/file/classgraph-4.8.174-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/zip/zip-hjar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/icu4j/lib++_repo_rules+icu4j-73_2_http/file/icu4j-73.2-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/xmlDom/xmlDom.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ide-core-impl/ide-core-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ide-core/plugins/ide-core-plugins.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-util-netty/ide-util-netty.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/aalto-xml-1_3_3_http_import/lib++_repo_rules+aalto-xml-1_3_3_http/file/aalto-xml-1.3.3-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/stax2-api-4_2_2_http_import/lib++_repo_rules+stax2-api-4_2_2_http/file/stax2-api-4.2.2-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jbr-api/lib++_repo_rules+jbr-api-1_0_0_http/file/jbr-api-1.0.0-ijar.jar
../lib++_repo_rules+kotlinx-serialization-json-jvm-1_7_3_http/file/kotlinx-serialization-json-jvm-1.7.3.jar
../lib++_repo_rules+kotlinx-serialization-core-jvm-1_7_3_http/file/kotlinx-serialization-core-jvm-1.7.3.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/jdom/jdom-hjar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jvm-native-trusted-roots/lib++_repo_rules+jvm-native-trusted-roots-1_0_21_http/file/jvm-native-trusted-roots-1.0.21-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-sdk-1_45_0_http_import/lib++_repo_rules+opentelemetry-sdk-1_45_0_http/file/opentelemetry-sdk-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-api-1_45_0_http_import/lib++_repo_rules+opentelemetry-api-1_45_0_http/file/opentelemetry-api-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-context-1_45_0_http_import/lib++_repo_rules+opentelemetry-context-1_45_0_http/file/opentelemetry-context-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-sdk-common-1_45_0_http_import/lib++_repo_rules+opentelemetry-sdk-common-1_45_0_http/file/opentelemetry-sdk-common-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-sdk-trace-1_45_0_http_import/lib++_repo_rules+opentelemetry-sdk-trace-1_45_0_http/file/opentelemetry-sdk-trace-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-api-incubator-1_45_0-alpha_http_import/lib++_repo_rules+opentelemetry-api-incubator-1_45_0-alpha_http/file/opentelemetry-api-incubator-1.45.0-alpha-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-sdk-metrics-1_45_0_http_import/lib++_repo_rules+opentelemetry-sdk-metrics-1_45_0_http/file/opentelemetry-sdk-metrics-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-sdk-logs-1_45_0_http_import/lib++_repo_rules+opentelemetry-sdk-logs-1_45_0_http/file/opentelemetry-sdk-logs-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-semconv/lib++_repo_rules+opentelemetry-semconv-1_28_0-alpha_http/file/opentelemetry-semconv-1.28.0-alpha-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/telemetry/telemetry.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/diagnostic.abi.jar
../lib++_repo_rules+opentelemetry-extension-kotlin-1_45_0_http/file/opentelemetry-extension-kotlin-1.45.0.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/hdr_histogram/lib++_repo_rules+HdrHistogram-2_2_2_http/file/HdrHistogram-2.2.2-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/code-style-impl/codeStyle-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/text-matching/text-matching-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-impl/rpc/ide-rpc.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/hash4j/lib++_repo_rules+hash4j-0_19_0_http/file/hash4j-0.19.0-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/backend/workspace/workspace.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/startUpPerformanceReporter/startUpPerformanceReporter.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/telemetry-impl/telemetry-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/telemetry.exporters/telemetry-exporters.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/diagnostic/telemetry/rt/diagnostic-telemetry-rt-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ijent/ijent.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/eel/eel.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/backend/observation/observation.abi.jar
../lib++_repo_rules+pty4j-0_13_1_http/file/pty4j-0.13.1.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/settings/settings.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/coroutines/coroutines.abi.jar
../lib++_repo_rules+rwmutex-idea-0_0_7_http/file/rwmutex-idea-0.0.7.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/lz4-java/lib++_repo_rules+lz4-java-1_8_0_http/file/lz4-java-1.8.0-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ml-api/ml.abi.jar
../lib++_repo_rules+ml-feature-api-58_http/file/ml-feature-api-58.jar
../lib++_repo_rules+kotlinx-collections-immutable-jvm-0_3_8_http/file/kotlinx-collections-immutable-jvm-0.3.8.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/storages/io-storages-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/core-nio-fs/libcore-nio-fs-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ijent/impl/community-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ijent/buildConstants/community-buildConstants.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/kernel/shared/kernel.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/kernel/rpc/rpc.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/rpc/rpc.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/kernel/kernel.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/rhizomedb/rhizomedb.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/util/core/fleet-util-core.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/util/logging/api/fleet-util-logging-api.abi.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/slf4j-api/lib++_repo_rules+slf4j-api-2_0_13_http/file/slf4j-api-2.0.13-ijar.jar
../lib++_repo_rules+kotlinx-coroutines-slf4j-1_8_0-intellij-11_http/file/kotlinx-coroutines-slf4j-1.8.0-intellij-11.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/project/shared/project.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/jbr/jbr-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/ui.jcef/ui-jcef.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/eel-provider/eel-provider.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-impl/ui/ide-ui.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/buildData/buildData.abi.jar
../lib++_repo_rules+annotations-java5-24_0_0_http/file/annotations-java5-24.0.0.jar
../lib++_repo_rules+kotlinx-coroutines-debug-1_8_0-intellij-11_http/file/kotlinx-coroutines-debug-1.8.0-intellij-11.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jaxen/lib++_repo_rules+jaxen-1_2_0_http/file/jaxen-1.2.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/log4_j/lib++_repo_rules+log4j-over-slf4j-1_7_36_http/file/log4j-over-slf4j-1.7.36-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/rt-java8/rt-java8-hjar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/commons-compress/lib++_repo_rules+commons-compress-1_26_1_http/file/commons-compress-1.26.1-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/troveCompileOnly/troveCompileOnly-hjar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/kryo5/lib++_repo_rules+kryo5-5_6_0_http/file/kryo5-5.6.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jcip/lib++_repo_rules+jcip-annotations-1_0_http/file/jcip-annotations-1.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/mvstore/lib++_repo_rules+h2-mvstore-2_3_232_http/file/h2-mvstore-2.3.232-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/jsvg/lib++_repo_rules+jsvg-1_3_0-jb_8_http/file/jsvg-1.3.0-jb.8-ijar.jar
../lib++_repo_rules+kotlinx-serialization-protobuf-jvm-1_7_3_http/file/kotlinx-serialization-protobuf-jvm-1.7.3.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/automaton/lib++_repo_rules+automaton-1_12-4_http/file/automaton-1.12-4-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/reporting/api/fleet-reporting-api.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/preferences/preferences.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/fleet/util/os/fleet-util-os-hjar.jar
../lib++_repo_rules+expects-compiler-plugin-2_1_0-0_3_http/file/expects-compiler-plugin-2.1.0-0.3.jar
../lib++_repo_rules+rpc-compiler-plugin-2_1_0-0_4_http/file/rpc-compiler-plugin-2.1.0-0.4.jar
../lib++_repo_rules+kotlinx-datetime-jvm-0_6_1_http/file/kotlinx-datetime-jvm-0.6.1.jar
../lib++_repo_rules+rhizomedb-compiler-plugin-2_1_0-0_2_http/file/rhizomedb-compiler-plugin-2.1.0-0.2.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/usageView/usageView.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/statistics/uploader/uploader.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/statistics/config/config.abi.jar
../lib++_repo_rules+jackson-module-kotlin-2_17_2_http/file/jackson-module-kotlin-2.17.2.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/runtime/product/product.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/runtime/repository/repository-hjar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/indexing-impl/indexing-impl.abi.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/nanoxml/nanoxml-hjar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/proxy-vole-1_1_6_http_import/lib++_repo_rules+proxy-vole-1_1_6_http/file/proxy-vole-1.1.6-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/delight-rhino-sandbox-0_0_17_http_import/lib++_repo_rules+delight-rhino-sandbox-0_0_17_http/file/delight-rhino-sandbox-0.0.17-ijar.jar
../lib++_repo_rules+rd-text-2024_3_1_http/file/rd-text-2024.3.1.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-exporter-otlp-common-1_45_0_http_import/lib++_repo_rules+opentelemetry-exporter-otlp-common-1_45_0_http/file/opentelemetry-exporter-otlp-common-1.45.0-ijar.jar
bazel-out/lib+/darwin_arm64-fastbuild/bin/_ijar/opentelemetry-exporter-common-1_45_0_http_import/lib++_repo_rules+opentelemetry-exporter-common-1_45_0_http/file/opentelemetry-exporter-common-1.45.0-ijar.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/util/http/http.abi.jar
../lib++_repo_rules+ktor-client-core-jvm-2_3_13_http/file/ktor-client-core-jvm-2.3.13.jar
../lib++_repo_rules+ktor-http-jvm-2_3_13_http/file/ktor-http-jvm-2.3.13.jar
../lib++_repo_rules+ktor-utils-jvm-2_3_13_http/file/ktor-utils-jvm-2.3.13.jar
../lib++_repo_rules+ktor-io-jvm-2_3_13_http/file/ktor-io-jvm-2.3.13.jar
../lib++_repo_rules+ktor-events-jvm-2_3_13_http/file/ktor-events-jvm-2.3.13.jar
../lib++_repo_rules+ktor-websocket-serialization-jvm-2_3_13_http/file/ktor-websocket-serialization-jvm-2.3.13.jar
../lib++_repo_rules+ktor-serialization-jvm-2_3_13_http/file/ktor-serialization-jvm-2.3.13.jar
../lib++_repo_rules+ktor-websockets-jvm-2_3_13_http/file/ktor-websockets-jvm-2.3.13.jar
../lib++_repo_rules+ktor-client-java-jvm-2_3_13_http/file/ktor-client-java-jvm-2.3.13.jar
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/eel-impl/eel-impl.abi.jar
--plugin-id
org.jetbrains.kotlin.kotlin-serialization-compiler-plugin
--plugin-classpath
--out
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-impl/ide-impl.jar
--abi-out
bazel-out/community+/darwin_arm64-fastbuild/bin/platform/platform-impl/ide-impl.abi.jar
--add-export
java.desktop/sun.awt=ALL-UNNAMED
java.desktop/sun.font=ALL-UNNAMED
java.desktop/java.awt.peer=ALL-UNNAMED
jdk.attach/sun.tools.attach=ALL-UNNAMED
java.desktop/sun.awt.image=ALL-UNNAMED
java.desktop/sun.awt.datatransfer=ALL-UNNAMED
java.desktop/sun.swing=ALL-UNNAMED
java.base/sun.nio.fs=ALL-UNNAMED
"""
}

View File

@@ -0,0 +1,219 @@
@file:Suppress("UnstableApiUsage", "ReplaceGetOrSet", "ReplaceJavaStaticMethodWithKotlinAnalog")
package org.jetbrains.bazel.jvm.jps.impl
import org.jetbrains.bazel.jvm.jps.SourceDescriptor
import org.jetbrains.jps.builders.BuildTarget
import org.jetbrains.jps.builders.storage.SourceToOutputMapping
import org.jetbrains.jps.incremental.storage.*
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes
internal class BazelBuildDataProvider(
@JvmField val relativizer: PathTypeAwareRelativizer,
actualDigestMap: Map<Path, ByteArray>,
private val sourceToDescriptor: HashMap<Path, SourceDescriptor>,
@JvmField val storeFile: Path,
) : BuildDataProvider {
private val stamp = BazelStampStorage(actualDigestMap = actualDigestMap, map = sourceToDescriptor)
@JvmField val sourceToOutputMapping = BazelSourceToOutputMapping(map = sourceToDescriptor, relativizer = relativizer)
private val sourceToForm = object : OneToManyPathMapping {
override fun getOutputs(path: String): Collection<String>? = sourceToOutputMapping.getOutputs(path)
override fun getOutputs(file: Path): Collection<Path>? = sourceToOutputMapping.getOutputs(file)
override fun setOutputs(path: Path, outPaths: List<Path>) {
sourceToOutputMapping.setOutputs(path, outPaths)
}
override fun remove(key: Path) {
sourceToOutputMapping.remove(key)
}
}
fun getFinalList(): Array<SourceDescriptor> {
val list = synchronized(sourceToDescriptor) {
sourceToDescriptor.values.toTypedArray()
}
list.sortBy { it.sourceFile }
return list
}
override fun clearCache() {
}
override fun removeStaleTarget(targetId: String, targetTypeId: String) {
}
override fun getFileStampStorage(target: BuildTarget<*>): BazelStampStorage = stamp
override fun getSourceToOutputMapping(target: BuildTarget<*>): BazelSourceToOutputMapping = sourceToOutputMapping
override fun getOutputToTargetMapping(): OutputToTargetMapping = throw UnsupportedOperationException("Must not be used")
override fun getSourceToForm(target: BuildTarget<*>): OneToManyPathMapping = sourceToForm
override fun closeTargetMaps(target: BuildTarget<*>) {
}
override fun removeAllMaps() {
}
override fun commit() {
}
override fun close() {
}
}
internal class BazelStampStorage(
private val actualDigestMap: Map<Path, ByteArray>,
private val map: HashMap<Path, SourceDescriptor>,
) : StampsStorage<ByteArray> {
override fun updateStamp(sourceFile: Path, buildTarget: BuildTarget<*>?, currentFileTimestamp: Long) {
val actualDigest = requireNotNull(actualDigestMap.get(sourceFile)) {
"No digest is provided for $sourceFile"
}
synchronized(map) {
map.computeIfAbsent(sourceFile) { SourceDescriptor(sourceFile = it) }.digest = actualDigest
}
}
override fun removeStamp(sourceFile: Path, buildTarget: BuildTarget<*>?) {
//synchronized(map) {
// map.get(sourceFile)?.digest = null
//}
throw UnsupportedOperationException("Must not be used")
}
override fun getCurrentStampIfUpToDate(file: Path, buildTarget: BuildTarget<*>?, attrs: BasicFileAttributes?): ByteArray? {
//val key = createKey(file)
//val storedDigest = synchronized(map) { map.get(key)?.digest } ?: return null
//val actualDigest = actualDigestMap.get(file) ?: return null
//return actualDigest.takeIf { storedDigest.contentEquals(actualDigest) }
throw UnsupportedOperationException("Must not be used")
}
fun isDirty(sourceFile: Path): Boolean {
val storedDigest = synchronized(map) { map.get(sourceFile)?.digest } ?: return true
val actualDigest = actualDigestMap.get(sourceFile) ?: return true
return !storedDigest.contentEquals(actualDigest)
}
}
internal class BazelSourceToOutputMapping(
private val map: HashMap<Path, SourceDescriptor>,
private val relativizer: PathTypeAwareRelativizer,
) : SourceToOutputMapping {
override fun setOutputs(sourceFile: Path, outputPaths: List<Path>) {
val relativeOutputPaths = if (outputPaths.isEmpty()) null else outputPaths.map { relativizer.toRelative(it, RelativePathType.OUTPUT) }
synchronized(map) {
val value = if (relativeOutputPaths == null) {
map.get(sourceFile) ?: return
}
else {
map.computeIfAbsent(sourceFile) { SourceDescriptor(sourceFile = it) }
}
value.outputs = relativeOutputPaths
}
}
override fun appendOutput(sourcePath: String, outputPath: String) {
val sourceFile = Path.of(sourcePath)
val relativeOutputPath = relativizer.toRelative(outputPath, RelativePathType.OUTPUT)
synchronized(map) {
val sourceInfo = map.computeIfAbsent(sourceFile) { SourceDescriptor(sourceFile = it) }
val existingOutputs = sourceInfo.outputs
if (existingOutputs == null) {
sourceInfo.outputs = java.util.List.of(relativeOutputPath)
}
else if (!existingOutputs.contains(relativeOutputPath)) {
sourceInfo.outputs = existingOutputs + relativeOutputPath
}
}
}
override fun remove(sourceFile: Path) {
synchronized(map) {
map.get(sourceFile)?.outputs = null
}
}
override fun removeOutput(sourcePath: String, outputPath: String) {
val sourceFile = Path.of(sourcePath)
val relativeOutputPath = relativizer.toRelative(outputPath, RelativePathType.OUTPUT)
synchronized(map) {
val sourceInfo = map.get(sourceFile) ?: return
val existingOutputs = sourceInfo.outputs ?: return
if (existingOutputs.contains(relativeOutputPath)) {
if (existingOutputs.size == 1) {
sourceInfo.outputs = null
}
else {
sourceInfo.outputs = existingOutputs - relativeOutputPath
}
}
}
}
override fun getOutputs(sourcePath: String): Collection<String>? {
val sourceFile = Path.of(sourcePath)
return synchronized(map) { map.get(sourceFile)?.outputs }?.map { relativizer.toAbsolute(it, RelativePathType.OUTPUT) }
}
override fun getOutputs(sourceFile: Path): Collection<Path>? {
return synchronized(map) { map.get(sourceFile)?.outputs }?.map { relativizer.toAbsoluteFile(it, RelativePathType.OUTPUT) }
}
fun getDescriptor(sourceFile: Path): SourceDescriptor? {
return synchronized(map) { map.get(sourceFile) }
}
fun findAffectedSources(affectedSources: List<List<String>>): List<Path> {
val result = ArrayList<Path>(affectedSources.size)
synchronized(map) {
for (descriptor in map.values) {
val outputs = descriptor.outputs ?: continue
for (output in outputs) {
if (affectedSources.any { it.contains(output) }) {
result.add(descriptor.sourceFile)
break
}
}
}
}
return result
}
fun findDeletedAndUnsetDigest(known: Map<Path, *>): List<Path> {
synchronized(map) {
return map.values.asSequence()
.filter { !known.containsKey(it.sourceFile) }
.onEach { it.digest = null }
.map { it.sourceFile }
.toList()
}
}
override fun getSourceFileIterator(): Iterator<Path> {
throw UnsupportedOperationException("Must not be used")
//return synchronized(map) { map.keys.toTypedArray() }.asSequence()
// .map { relativizer.toAbsoluteFile(it, RelativePathType.SOURCE) }
// .iterator()
}
override fun getSourcesIterator(): Iterator<String> {
throw UnsupportedOperationException("Must not be used")
//return synchronized(map) { map.keys.toTypedArray() }.asSequence()
// .map { relativizer.toAbsolute(it, RelativePathType.SOURCE) }
// .iterator()
}
override fun cursor(): SourceToOutputMappingCursor {
throw UnsupportedOperationException("Must not be used")
}
}

View File

@@ -1,7 +1,7 @@
@file:Suppress("HardCodedStringLiteral", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "DialogTitleCapitalization", "UnstableApiUsage", "ReplaceGetOrSet")
package org.jetbrains.bazel.jvm.jps.impl
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.bazel.jvm.jps.RequestLog
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.DirtyFilesHolder
import org.jetbrains.jps.builders.FileProcessor
@@ -13,24 +13,18 @@ import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.build.GeneratedJvmClass
import org.jetbrains.kotlin.build.report.ICReporter
import org.jetbrains.kotlin.build.report.ICReporter.ReportSeverity
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.debug
import org.jetbrains.kotlin.build.report.metrics.JpsBuildTime
import org.jetbrains.kotlin.build.report.statistics.StatTag
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.*
import org.jetbrains.kotlin.jps.KotlinJpsBundle
import org.jetbrains.kotlin.jps.build.*
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageManager
import org.jetbrains.kotlin.jps.statistic.JpsBuilderMetricReporter
import org.jetbrains.kotlin.jps.statistic.JpsStatisticsReportService
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.deserialization.MetadataVersion
@@ -38,44 +32,44 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.preloading.ClassCondition
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import kotlin.system.measureTimeMillis
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.jps.build.KotlinBuilder")
private val classesToLoadByParent: ClassCondition = ClassCondition { className ->
val prefixes = listOf(
private val classesToLoadByParent = ClassCondition { className ->
for (it in arrayOf(
"org.apache.log4j.", // For logging from compiler
"org.jetbrains.kotlin.incremental.components.",
"org.jetbrains.kotlin.incremental.js",
"org.jetbrains.kotlin.load.kotlin.incremental.components."
)
val classes = listOf(
)) {
if (className.startsWith(it)) {
return@ClassCondition true
}
}
for (it in arrayOf(
"org.jetbrains.kotlin.config.Services",
"org.jetbrains.kotlin.progress.CompilationCanceledStatus",
"org.jetbrains.kotlin.progress.CompilationCanceledException",
"org.jetbrains.kotlin.modules.TargetId",
"org.jetbrains.kotlin.cli.common.ExitCode"
)
prefixes.forEach { if (className.startsWith(it)) return@ClassCondition true }
classes.forEach { if (className == it) return@ClassCondition true }
)) {
if (className == it) {
return@ClassCondition true
}
}
return@ClassCondition false
}
internal class BazelKotlinBuilder(
private val isKotlinBuilderInDumbMode: Boolean = false,
private val isKotlinBuilderInDumbMode: Boolean,
private val enableLookupStorageFillingInDumbMode: Boolean = false,
private val log: RequestLog,
) : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
companion object {
const val JPS_KOTLIN_HOME_PROPERTY = "jps.kotlin.home"
}
private val statisticsLogger = TeamcityStatisticsLogger()
override fun getPresentableName() = "Kotlin Builder"
override fun getCompilableFileExtensions() = arrayListOf("kt")
@@ -115,7 +109,7 @@ internal class BazelKotlinBuilder(
kotlinContext.reportUnsupportedTargets()
}
LOG.info("Total Kotlin global compile context initialization time: $time ms")
log.info("Total Kotlin global compile context initialization time: $time ms")
return kotlinContext
}
@@ -131,8 +125,6 @@ internal class BazelKotlinBuilder(
if (kotlinCompileContext != null) {
kotlinCompileContext.dispose()
context.putUserData(kotlinCompileContextKey, null)
statisticsLogger.reportTotal()
}
}
}
@@ -141,12 +133,6 @@ internal class BazelKotlinBuilder(
override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
super.chunkBuildStarted(context, chunk)
if (chunk.isDummy(context)) {
return
}
val kotlinContext = ensureKotlinContextInitialized(context)
val buildLogger = context.testingContext?.buildLogger
buildLogger?.chunkBuildStarted(context, chunk)
@@ -155,6 +141,7 @@ internal class BazelKotlinBuilder(
}
val targets = chunk.targets
val kotlinContext = ensureKotlinContextInitialized(context)
if (targets.none { kotlinContext.hasKotlinMarker[it] == true }) {
return
}
@@ -191,7 +178,7 @@ internal class BazelKotlinBuilder(
}
}
)
val fsOperations = FSOperationsHelper(context, chunk, dirtyFilesHolder, LOG)
val fsOperations = BazelKotlinFsOperationsHelper(context, chunk, dirtyFilesHolder, log)
val representativeTarget = kotlinContext.targetsBinding[chunk.representativeTarget()] ?: return
@@ -229,10 +216,17 @@ internal class BazelKotlinBuilder(
}
val changesCollector = ChangesCollector()
removedClasses.forEach { changesCollector.collectSignature(FqName(it), areSubclassesAffected = true) }
val affectedByRemovedClasses = changesCollector.getDirtyFiles(incrementalCaches.values, kotlinContext.lookupStorageManager)
for (it in removedClasses) {
changesCollector.collectSignature(FqName(it), areSubclassesAffected = true)
}
val affectedByRemovedClasses = getDirtyFiles(
changeCollector = changesCollector,
caches = incrementalCaches.values,
lookupStorageManager = kotlinContext.lookupStorageManager,
reporter = BazelJpsICReporter(log),
)
fsOperations.markFilesForCurrentRound(affectedByRemovedClasses.dirtyFiles + affectedByRemovedClasses.forceRecompileTogether)
fsOperations.markFilesForCurrentRound(affectedByRemovedClasses.dirtyFiles.asSequence() + affectedByRemovedClasses.forceRecompileTogether)
}
override fun build(
@@ -241,48 +235,28 @@ internal class BazelKotlinBuilder(
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
outputConsumer: OutputConsumer
): ExitCode {
val reportService = JpsStatisticsReportService.getFromContext(context)
reportService.moduleBuildStarted(chunk)
return doBuild(context = context, chunk = chunk, dirtyFilesHolder = dirtyFilesHolder, outputConsumer = outputConsumer).also { result ->
reportService.moduleBuildFinished(chunk, context, result)
}
}
private fun doBuild(
context: CompileContext,
chunk: ModuleChunk,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
outputConsumer: OutputConsumer
): ExitCode {
if (chunk.isDummy(context)) {
return NOTHING_DONE
}
val kotlinTarget = context.kotlin.targetsBinding.get(chunk.representativeTarget()) ?: return OK
val messageCollector = MessageCollectorAdapter(context, kotlinTarget)
val kotlinDirtyFilesHolder = KotlinDirtySourceFilesHolder(chunk, context, dirtyFilesHolder)
val fsOperations = FSOperationsHelper(context, chunk, kotlinDirtyFilesHolder, LOG)
val reportService = JpsStatisticsReportService.getFromContext(context)
reportService.reportDirtyFiles(kotlinDirtyFilesHolder)
return reportService.reportMetrics(chunk, JpsBuildTime.JPS_ITERATION) {
val proposedExitCode = doBuild(
chunk = chunk,
representativeTarget = kotlinTarget,
context = context,
kotlinDirtyFilesHolder = kotlinDirtyFilesHolder,
messageCollector = messageCollector,
outputConsumer = outputConsumer,
fsOperations = fsOperations
)
val fsOperations = BazelKotlinFsOperationsHelper(
context = context,
chunk = chunk,
dirtyFilesHolder = kotlinDirtyFilesHolder,
log = log,
)
val proposedExitCode = doBuild(
chunk = chunk,
representativeTarget = kotlinTarget,
context = context,
kotlinDirtyFilesHolder = kotlinDirtyFilesHolder,
messageCollector = MessageCollectorAdapter(context, kotlinTarget),
outputConsumer = outputConsumer,
fsOperations = fsOperations
)
val actualExitCode = if (proposedExitCode == OK && fsOperations.hasMarkedDirty) ADDITIONAL_PASS_REQUIRED else proposedExitCode
LOG.debug("Build result: $actualExitCode")
context.testingContext?.buildLogger?.buildFinished(actualExitCode)
actualExitCode
}
val actualExitCode = if (proposedExitCode == OK && fsOperations.hasMarkedDirty) ADDITIONAL_PASS_REQUIRED else proposedExitCode
log.debug("build result: $actualExitCode")
context.testingContext?.buildLogger?.buildFinished(actualExitCode)
return actualExitCode
}
private fun doBuild(
@@ -292,27 +266,18 @@ internal class BazelKotlinBuilder(
kotlinDirtyFilesHolder: KotlinDirtySourceFilesHolder,
messageCollector: MessageCollectorAdapter,
outputConsumer: OutputConsumer,
fsOperations: FSOperationsHelper,
fsOperations: BazelKotlinFsOperationsHelper,
): ExitCode {
val kotlinContext = context.kotlin
val kotlinChunk = chunk.toKotlinChunk(context)!!
val kotlinChunk = context.kotlin.getChunk(chunk)!!
if (!kotlinChunk.haveSameCompiler) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
KotlinJpsBundle.message(
"error.text.cyclically.dependent.modules.0.should.have.same.compiler",
kotlinChunk.presentableModulesToCompilersList
)
)
return ABORT
throw RuntimeException("Cyclically dependent modules ${kotlinChunk.presentableModulesToCompilersList} should have same compiler.")
}
if (!kotlinChunk.isEnabled) {
return NOTHING_DONE
}
val projectDescriptor = context.projectDescriptor
val targets = chunk.targets
val isChunkRebuilding = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) ||
@@ -320,8 +285,8 @@ internal class BazelKotlinBuilder(
if (!kotlinDirtyFilesHolder.hasDirtyOrRemovedFiles) {
if (isChunkRebuilding) {
targets.forEach {
kotlinContext.hasKotlinMarker[it] = false
for (target in targets) {
kotlinContext.hasKotlinMarker[target] = false
}
}
@@ -341,14 +306,10 @@ internal class BazelKotlinBuilder(
val targetsWithoutOutputDir = targets.filter { it.outputDir == null }
if (targetsWithoutOutputDir.isNotEmpty()) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
KotlinJpsBundle.message("error.text.output.directory.not.specified.for.0", targetsWithoutOutputDir.joinToString())
)
return ABORT
throw RuntimeException("Output directory not specified for ${targetsWithoutOutputDir.joinToString()}")
}
val project = projectDescriptor.project
val project = context.projectDescriptor.project
val lookupTracker = getLookupTracker(project, representativeTarget)
val exceptActualTracker = ExpectActualTrackerImpl()
val incrementalCaches = kotlinChunk.loadCaches()
@@ -374,9 +335,6 @@ internal class BazelKotlinBuilder(
kotlinDirtyFilesHolder.allRemovedFilesFiles
)
val reportService = JpsStatisticsReportService.getFromContext(context)
reportService.reportCompilerArguments(chunk, kotlinChunk)
val start = System.nanoTime()
val outputItemCollector = doCompileModuleChunk(
kotlinChunk = kotlinChunk,
representativeTarget = representativeTarget,
@@ -386,16 +344,13 @@ internal class BazelKotlinBuilder(
fsOperations = fsOperations,
environment = environment,
incrementalCaches = incrementalCaches,
buildMetricReporter = reportService.getMetricReporter(chunk)
)
statisticsLogger.registerStatistic(chunk, System.nanoTime() - start)
if (outputItemCollector == null) {
return NOTHING_DONE
}
val compilationErrors = Utils.ERRORS_DETECTED_KEY[context, false]
val compilationErrors = Utils.ERRORS_DETECTED_KEY.get(context, false)
if (compilationErrors) {
JavaBuilderUtil.registerFilesWithErrors(context, messageCollector.filesWithErrors.map(::File))
return ABORT
@@ -417,22 +372,22 @@ internal class BazelKotlinBuilder(
val kotlinTargets = kotlinContext.targetsBinding
for ((target, outputItems) in generatedFiles) {
val kotlinTarget = kotlinTargets[target] ?: error("Could not find Kotlin target for JPS target $target")
val kotlinTarget = kotlinTargets.get(target) ?: error("Could not find Kotlin target for JPS target $target")
kotlinTarget.registerOutputItems(outputConsumer, outputItems)
}
kotlinChunk.saveVersions()
if (targets.any { kotlinContext.hasKotlinMarker[it] == null }) {
fsOperations.markChunk(recursively = false, kotlinOnly = true, excludeFiles = kotlinDirtyFilesHolder.allDirtyFiles)
if (targets.any { kotlinContext.hasKotlinMarker.get(it) == null }) {
fsOperations.markChunk(excludeFiles = kotlinDirtyFilesHolder.allDirtyFiles)
}
for (target in targets) {
kotlinContext.hasKotlinMarker[target] = true
kotlinContext.hasKotlinMarker.set(target, true)
kotlinContext.rebuildAfterCacheVersionChanged.clean(target)
}
kotlinChunk.targets.forEach {
it.doAfterBuild()
for (target in kotlinChunk.targets) {
target.doAfterBuild()
}
representativeTarget.updateChunkMappings(
@@ -459,10 +414,10 @@ internal class BazelKotlinBuilder(
val kotlinModuleBuilderTarget = kotlinContext.targetsBinding[target]!!
kotlinModuleBuilderTarget.updateCaches(
dirtyFilesHolder = kotlinDirtyFilesHolder,
jpsIncrementalCache = incrementalCaches[kotlinModuleBuilderTarget]!!,
jpsIncrementalCache = incrementalCaches.get(kotlinModuleBuilderTarget)!!,
files = files,
changesCollector = changesCollector,
environment = environment
environment = environment,
)
}
@@ -476,7 +431,8 @@ internal class BazelKotlinBuilder(
compiledFiles = kotlinDirtyFilesHolder.allDirtyFiles,
lookupStorageManager = kotlinContext.lookupStorageManager,
fsOperations = fsOperations,
caches = incrementalCaches.values
caches = incrementalCaches.values,
reporter = BazelJpsICReporter(log),
)
}
}
@@ -492,17 +448,15 @@ internal class BazelKotlinBuilder(
commonArguments: CommonCompilerArguments,
context: CompileContext,
dirtyFilesHolder: KotlinDirtySourceFilesHolder,
fsOperations: FSOperationsHelper,
fsOperations: BazelKotlinFsOperationsHelper,
environment: JpsCompilerEnvironment,
incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
buildMetricReporter: JpsBuilderMetricReporter?,
): OutputItemsCollector? {
kotlinChunk.targets.forEach {
it.nextRound(context)
}
if (representativeTarget.isIncrementalCompilationEnabled) {
buildMetricReporter?.addTag(StatTag.INCREMENTAL)
for (target in kotlinChunk.targets) {
val cache = incrementalCaches[target]
val jpsTarget = target.jpsModuleBuildTarget
@@ -519,8 +473,7 @@ internal class BazelKotlinBuilder(
}
registerFilesToCompile(dirtyFilesHolder, context)
val isDoneSomething = representativeTarget.compileModuleChunk(commonArguments, dirtyFilesHolder, environment, buildMetricReporter)
val isDoneSomething = representativeTarget.compileModuleChunk(commonArguments, dirtyFilesHolder, environment, null)
return if (isDoneSomething) environment.outputItemsCollector else null
}
@@ -529,8 +482,8 @@ internal class BazelKotlinBuilder(
context: CompileContext,
) {
val allDirtyFiles = dirtyFilesHolder.allDirtyFiles
if (LOG.isDebugEnabled) {
LOG.debug("Compiling files: $allDirtyFiles")
if (log.isDebugEnabled) {
log.debug("compiling files: $allDirtyFiles")
}
JavaBuilderUtil.registerFilesToCompile(context, allDirtyFiles)
}
@@ -561,7 +514,7 @@ internal class BazelKotlinBuilder(
}
return JpsCompilerEnvironment(
kotlinPaths = computeKotlinPathsForJpsPlugin(messageCollector) ?: return null,
kotlinPaths = computeKotlinPathsForJpsPlugin() ?: return null,
services = compilerServices,
classesToLoadByParent = classesToLoadByParent,
messageCollector = messageCollector,
@@ -570,25 +523,14 @@ internal class BazelKotlinBuilder(
)
}
// When JPS is run on TeamCity, it can not rely on Kotlin plugin layout,
// so the path to Kotlin is specified in a system property
private fun computeKotlinPathsForJpsPlugin(messageCollector: MessageCollectorAdapter): KotlinPaths? {
private fun computeKotlinPathsForJpsPlugin(): KotlinPaths? {
val jpsKotlinHome = System.getProperty(JPS_KOTLIN_HOME_PROPERTY)?.let { File(it) }
if (System.getProperty("kotlin.jps.tests").equals("true", ignoreCase = true) && jpsKotlinHome == null) {
return PathUtil.kotlinPathsForDistDirectory
?: throw RuntimeException("Make sure that '$JPS_KOTLIN_HOME_PROPERTY' system property is set in JPS process")
if (jpsKotlinHome.exists()) {
return KotlinPathsFromHomeDir(jpsKotlinHome)
}
return when {
jpsKotlinHome == null -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "Make sure that '$JPS_KOTLIN_HOME_PROPERTY' system property is set in JPS process")
null
}
jpsKotlinHome.exists() -> KotlinPathsFromHomeDir(jpsKotlinHome)
else -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "Cannot find kotlinc home at $jpsKotlinHome")
null
}
else {
throw RuntimeException("Cannot find kotlinc home at $jpsKotlinHome")
}
}
@@ -641,7 +583,7 @@ internal class BazelKotlinBuilder(
generatedFiles: Map<ModuleBuildTarget, List<GeneratedFile>>,
kotlinContext: KotlinCompileContext,
incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
fsOperations: FSOperationsHelper
fsOperations: BazelKotlinFsOperationsHelper,
) {
for ((target, files) in generatedFiles) {
val kotlinModuleBuilderTarget = kotlinContext.targetsBinding[target] ?: continue
@@ -653,21 +595,21 @@ internal class BazelKotlinBuilder(
val actualParts = generated.filter { it.outputClass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART }
.map { it.outputClass.className.toString() }
if (!actualParts.containsAll(expectedAllParts)) {
fsOperations.markFiles(expectedAllParts.flatMap { cache.sourcesByInternalName(it) }
+ multifileClasses.flatMap { it.sourceFiles })
fsOperations.markFiles(expectedAllParts.asSequence().flatMap { cache.sourcesByInternalName(it) }
+ multifileClasses.asSequence().flatMap { it.sourceFiles })
}
}
}
}
private class JpsICReporter : ICReporterBase() {
private class BazelJpsICReporter(private val log: RequestLog) : ICReporterBase() {
override fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {
}
override fun report(message: () -> String, severity: ReportSeverity) {
// Currently, all severity levels are mapped to debug
if (KotlinBuilder.LOG.isDebugEnabled) {
KotlinBuilder.LOG.debug(message())
if (log.isDebugEnabled) {
log.debug(message())
}
}
}
@@ -676,16 +618,19 @@ private fun processChangesUsingLookups(
collector: ChangesCollector,
compiledFiles: Set<File>,
lookupStorageManager: JpsLookupStorageManager,
fsOperations: FSOperationsHelper,
caches: Iterable<JpsIncrementalCache>
fsOperations: BazelKotlinFsOperationsHelper,
caches: Iterable<JpsIncrementalCache>,
reporter: ICReporter,
) {
val allCaches = caches.flatMap { it.thisWithDependentCaches }
val reporter = JpsICReporter()
reporter.debug { "Start processing changes" }
val dirtyFiles = collector.getDirtyFiles(allCaches, lookupStorageManager)
// if list of inheritors of sealed class has changed it should be recompiled with all the inheritors
val dirtyFiles = getDirtyFiles(
changeCollector = collector,
caches = allCaches,
lookupStorageManager = lookupStorageManager,
reporter = reporter,
)
// if a list of inheritors of sealed class has changed it should be recompiled with all the inheritors
// Here we have a small optimization. Do not recompile the bunch if ALL these files were recompiled during the previous round.
val excludeFiles = if (compiledFiles.containsAll(dirtyFiles.forceRecompileTogether)) {
compiledFiles
@@ -694,27 +639,26 @@ private fun processChangesUsingLookups(
compiledFiles.minus(dirtyFiles.forceRecompileTogether)
}
fsOperations.markInChunkOrDependents(
(dirtyFiles.dirtyFiles + dirtyFiles.forceRecompileTogether).asIterable(),
excludeFiles = excludeFiles
files = (dirtyFiles.dirtyFiles.asSequence() + dirtyFiles.forceRecompileTogether),
excludeFiles = excludeFiles,
)
reporter.debug { "End of processing changes" }
}
private data class FilesToRecompile(@JvmField val dirtyFiles: Set<File>, @JvmField val forceRecompileTogether: Set<File>)
private fun ChangesCollector.getDirtyFiles(
private fun getDirtyFiles(
changeCollector: ChangesCollector,
caches: Iterable<IncrementalCacheCommon>,
lookupStorageManager: JpsLookupStorageManager
lookupStorageManager: JpsLookupStorageManager,
reporter: ICReporter,
): FilesToRecompile {
val reporter = JpsICReporter()
val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = getChangedAndImpactedSymbols(caches, reporter)
val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = changeCollector.changes().getChangedAndImpactedSymbols(caches, reporter)
val dirtyFilesFromLookups = lookupStorageManager.withLookupStorage {
mapLookupSymbolsToFiles(lookupStorage = it, lookupSymbols = dirtyLookupSymbols, reporter = reporter)
}
return FilesToRecompile(
dirtyFilesFromLookups + mapClassesFqNamesToFiles(caches, dirtyClassFqNames, reporter),
mapClassesFqNamesToFiles(caches, forceRecompile, reporter)
dirtyFiles = dirtyFilesFromLookups + mapClassesFqNamesToFiles(caches, dirtyClassFqNames, reporter),
forceRecompileTogether = mapClassesFqNamesToFiles(caches, forceRecompile, reporter)
)
}

View File

@@ -0,0 +1,148 @@
@file:Suppress("HardCodedStringLiteral", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "DialogTitleCapitalization", "UnstableApiUsage", "ReplaceGetOrSet")
package org.jetbrains.bazel.jvm.jps.impl
import com.intellij.openapi.util.io.FileUtilRt
import org.jetbrains.bazel.jvm.jps.RequestLog
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.BuildRootDescriptor
import org.jetbrains.jps.builders.FileProcessor
import org.jetbrains.jps.builders.impl.DirtyFilesHolderBase
import org.jetbrains.jps.builders.java.JavaBuilderUtil
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
import org.jetbrains.jps.incremental.BuildOperations
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.FSOperations
import org.jetbrains.jps.incremental.FSOperations.addCompletelyMarkedDirtyTarget
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder
import java.io.File
import java.nio.file.Path
internal class BazelKotlinFsOperationsHelper(
private val context: CompileContext,
private val chunk: ModuleChunk,
private val dirtyFilesHolder: KotlinDirtySourceFilesHolder,
private val log: RequestLog,
) {
internal var hasMarkedDirty = false
private set
fun markChunk(excludeFiles: Set<File>) {
val target = chunk.targets.single()
val filter = { file: Path ->
val filePath = file.toString()
when {
!(FileUtilRt.extensionEquals(filePath, "kt") || FileUtilRt.extensionEquals(filePath, "kts")) -> {
false
}
excludeFiles.contains(file.toFile()) -> {
false
}
else -> {
hasMarkedDirty = true
true
}
}
}
var completelyMarkedDirty = true
val stampStorage = context.projectDescriptor.dataManager.getFileStampStorage(target)
for (rootDescriptor in (context.projectDescriptor.buildRootIndex as BazelBuildRootIndex).descriptors) {
val file = rootDescriptor.rootFile
if (!filter(file)) {
completelyMarkedDirty = false
continue
}
// if it is a full project rebuild, all storages are already completely cleared;
// so passing null because there is no need to access the storage to clear non-existing data
val marker = if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) null else stampStorage
context.projectDescriptor.fsState.markDirty(context, CompilationRound.NEXT, file, rootDescriptor, marker, false)
}
if (completelyMarkedDirty) {
addCompletelyMarkedDirtyTarget(context, target)
}
}
internal fun markFilesForCurrentRound(files: Sequence<File>) {
val buildRootIndex = context.projectDescriptor.buildRootIndex as BazelBuildRootIndex
for (file in files) {
val root = buildRootIndex.fileToDescriptors.get(file.toPath())
if (root != null) {
dirtyFilesHolder.byTarget[root.target]?._markDirty(file, root)
}
}
markFilesImpl(files, currentRound = true) { it.exists() }
}
/**
* Marks given [files] as dirty for current round and given [target] of [chunk].
*/
fun markFilesForCurrentRound(target: ModuleBuildTarget, files: Collection<File>) {
require(target in chunk.targets)
val targetDirtyFiles = dirtyFilesHolder.byTarget.getValue(target)
val dirtyFileToRoot = HashMap<File, JavaSourceRootDescriptor>()
for (file in files) {
val root = context.projectDescriptor.buildRootIndex
.findAllParentDescriptors<BuildRootDescriptor>(file, context)
.single { sourceRoot -> sourceRoot.target == target }
targetDirtyFiles._markDirty(file, root as JavaSourceRootDescriptor)
dirtyFileToRoot[file] = root
}
markFilesImpl(files.asSequence(), currentRound = true) { it.exists() }
cleanOutputsForNewDirtyFilesInCurrentRound(target, dirtyFileToRoot)
}
private fun cleanOutputsForNewDirtyFilesInCurrentRound(target: ModuleBuildTarget, dirtyFiles: Map<File, JavaSourceRootDescriptor>) {
val dirtyFilesHolder = object : DirtyFilesHolderBase<JavaSourceRootDescriptor, ModuleBuildTarget>(context) {
override fun processDirtyFiles(processor: FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>) {
for ((file, root) in dirtyFiles) {
processor.apply(target, file, root)
}
}
override fun hasDirtyFiles(): Boolean = dirtyFiles.isNotEmpty()
}
BuildOperations.cleanOutputsCorrespondingToChangedFiles(context, dirtyFilesHolder)
}
fun markFiles(files: Sequence<File>) {
markFilesImpl(files, currentRound = false) { it.exists() }
}
fun markInChunkOrDependents(files: Sequence<File>, excludeFiles: Set<File>) {
markFilesImpl(files, currentRound = false) {
!excludeFiles.contains(it) && it.exists()
}
}
private inline fun markFilesImpl(
files: Sequence<File>,
currentRound: Boolean,
shouldMark: (File) -> Boolean
) {
val filesToMark = files.filterTo(HashSet(), shouldMark)
if (filesToMark.isEmpty()) {
return
}
val compilationRound = if (currentRound) {
CompilationRound.CURRENT
}
else {
hasMarkedDirty = true
CompilationRound.NEXT
}
for (fileToMark in filesToMark) {
FSOperations.markDirty(context, compilationRound, fileToMark)
}
log.debug("Mark dirty: $filesToMark ($compilationRound)")
}
}

View File

@@ -4,22 +4,18 @@
package org.jetbrains.bazel.jvm.jps.impl
import com.intellij.tracing.Tracer
import com.intellij.util.containers.FileHashStrategy
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import org.jetbrains.bazel.jvm.jps.ConsoleMessageHandler
import org.jetbrains.bazel.jvm.jps.RequestLog
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.BuildTarget
import org.jetbrains.jps.builders.FileProcessor
import org.jetbrains.jps.builders.impl.BuildOutputConsumerImpl
import org.jetbrains.jps.builders.impl.BuildTargetChunk
import org.jetbrains.jps.builders.impl.DirtyFilesHolderBase
import org.jetbrains.jps.builders.java.JavaBuilderUtil
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException
import org.jetbrains.jps.builders.storage.SourceToOutputMapping
import org.jetbrains.jps.incremental.*
import org.jetbrains.jps.incremental.ModuleLevelBuilder.OutputConsumer
import org.jetbrains.jps.incremental.fs.BuildFSState
import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.jps.incremental.messages.*
import org.jetbrains.jps.incremental.storage.BuildTargetConfiguration
@@ -34,7 +30,6 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
import java.util.function.Function
import java.util.function.Predicate
import kotlin.concurrent.Volatile
private val TARGET_WITH_CLEARED_OUTPUT = GlobalContextKey.create<MutableSet<BuildTarget<*>>>("_targets_with_cleared_output_")
@@ -47,34 +42,34 @@ private const val CLASSPATH_INDEX_FILE_NAME = "classpath.index"
private const val UNMODIFIED_MARK_FILE_NAME = ".unmodified"
internal class JpsProjectBuilder(
private val builderRegistry: BuilderRegistry,
private val messageHandler: ConsoleMessageHandler,
private val log: RequestLog,
private val isCleanBuild: Boolean,
private val dataManager: BazelBuildDataProvider,
) {
private val totalModuleLevelBuilderCount = builderRegistry.moduleLevelBuilderCount
private val elapsedTimeNanosByBuilder = ConcurrentHashMap<Builder, AtomicLong>()
private val numberOfSourcesProcessedByBuilder = ConcurrentHashMap<Builder, AtomicInteger>()
fun build(context: CompileContextImpl, moduleTarget: BazelModuleBuildTarget) {
fun build(context: CompileContextImpl, moduleTarget: BazelModuleBuildTarget, builders: Array<ModuleLevelBuilder>): Int {
try {
val buildSpan = Tracer.start("IncProjectBuilder.runBuild")
runBuild(context, moduleTarget = moduleTarget)
runBuild(context, moduleTarget = moduleTarget, builders = builders)
buildSpan.complete()
}
catch (e: StopBuildException) {
// some builder decided to stop the build - report optional progress message if any
e.message?.takeIf { it.isNotEmpty() }?.let {
messageHandler.processMessage(ProgressMessage(it))
log.processMessage(ProgressMessage(it))
}
return if (log.hasErrors()) 1 else 0
}
catch (e: BuildDataCorruptedException) {
messageHandler.warn("Internal caches are corrupted or have outdated format, forcing project rebuild: $e")
log.warn("Internal caches are corrupted or have outdated format, forcing project rebuild: $e")
throw RebuildRequestedException(e)
}
catch (e: ProjectBuildException) {
val cause = e.cause
if (cause is IOException || cause is BuildDataCorruptedException || (cause is RuntimeException && cause.cause is IOException)) {
messageHandler.warn("Internal caches are corrupted or have outdated format, forcing project rebuild: $e")
log.warn("Internal caches are corrupted or have outdated format, forcing project rebuild: $e")
throw RebuildRequestedException(cause)
}
else {
@@ -82,9 +77,10 @@ internal class JpsProjectBuilder(
throw e
}
}
return 0
}
private fun runBuild(context: CompileContextImpl, moduleTarget: BazelModuleBuildTarget) {
private fun runBuild(context: CompileContextImpl, moduleTarget: BazelModuleBuildTarget, builders: Array<ModuleLevelBuilder>) {
context.setDone(0.0f)
context.addBuildListener(ChainedTargetsBuildListener(context))
@@ -118,26 +114,13 @@ internal class JpsProjectBuilder(
}
}
})
require(builderRegistry.targetBuilders.isEmpty())
val allModuleLevelBuildersBuildStartedSpan = Tracer.start("All ModuleLevelBuilder.buildStarted")
for (builder in builderRegistry.moduleLevelBuilders) {
for (builder in builders) {
builder.buildStarted(context)
}
allModuleLevelBuildersBuildStartedSpan.complete()
val projectDescriptor = context.projectDescriptor
var buildProgress: BuildProgress? = null
try {
val sortedTargetChunks = projectDescriptor.buildTargetIndex.getSortedTargetChunks(context)
buildProgress = BuildProgress(
projectDescriptor.dataManager,
projectDescriptor.buildTargetIndex,
sortedTargetChunks,
Predicate { context.scope.isAffected(moduleTarget) }
)
require(builderRegistry.beforeTasks.isEmpty())
val checkingSourcesSpan = Tracer.start("Building targets")
// We don't call closeSourceToOutputStorages as we only built a single target and close the database after the build.
// (In any case, for the new storage, it only involves removing the cached map with no actual IO close operation or using MVStore API)
@@ -145,22 +128,14 @@ internal class JpsProjectBuilder(
val affected = context.scope.isAffected(moduleTarget)
isAffectedSpan.complete()
if (affected) {
buildTargetChunk(context = context, buildProgress = buildProgress, moduleTarget = moduleTarget)
buildTargetChunk(context = context, target = moduleTarget, builders = builders)
}
checkingSourcesSpan.complete()
require(builderRegistry.afterTasks.isEmpty())
sendElapsedTimeMessages(context)
}
finally {
if (buildProgress != null) {
buildProgress.updateExpectedAverageTime()
if (isCleanBuild && !Utils.errorsDetected(context) && !context.cancelStatus.isCanceled) {
projectDescriptor.dataManager.targetStateManager.setLastSuccessfulRebuildDuration(buildProgress.absoluteBuildTime)
}
}
for (builder in builderRegistry.moduleLevelBuilders) {
for (builder in builders) {
builder.buildFinished(context)
}
}
@@ -178,77 +153,71 @@ internal class JpsProjectBuilder(
.forEach { context.processMessage(it) }
}
private fun deleteAndCollectDeleted(file: Path, deletedPaths: MutableList<Path>, parentDirs: MutableSet<Path>): Boolean {
val deleted = Files.deleteIfExists(file)
if (deleted) {
deletedPaths.add(file)
file.parent?.let {
parentDirs.add(it)
}
}
return deleted
}
private fun processDeletedPaths(context: CompileContext, target: ModuleBuildTarget): Boolean {
var doneSomething = false
// cleanup outputs
val targetToRemovedSources = HashMap<BuildTarget<*>, MutableCollection<String>>()
val dirsToDelete = HashSet<Path>()
for (target in arrayOf(target)) {
val deletedPaths = context.projectDescriptor.fsState.getAndClearDeletedPaths(target)
if (deletedPaths.isEmpty()) {
continue
}
val deletedPaths = context.projectDescriptor.fsState.getAndClearDeletedPaths(target).asSequence().map { Path.of(it) }.sorted().toList()
if (deletedPaths.isEmpty()) {
return false
}
targetToRemovedSources.put(target, deletedPaths)
if (isTargetOutputCleared(context, target)) {
continue
}
if (isTargetOutputCleared(context, target)) {
return false
}
val dataManager = context.projectDescriptor.dataManager
val buildTargetId = dataManager.targetStateManager.getBuildTargetId(target)
val sourceToOutputStorage = dataManager.getSourceToOutputMap(target)
val logger = context.loggingManager.projectBuilderLogger
// actually delete outputs associated with removed paths
for (deletedSource in deletedPaths.sorted()) {
// deleting outputs corresponding to a non-existing source
val outputs = sourceToOutputStorage.getOutputs(deletedSource)
if (outputs != null && !outputs.isEmpty()) {
val deletedOutputPaths = ArrayList<String>()
val outputToSourceRegistry = dataManager.outputToTargetMapping
for (output in outputToSourceRegistry.removeTargetAndGetSafeToDeleteOutputs(outputs, buildTargetId, sourceToOutputStorage)) {
val deleted = BuildOperations.deleteRecursivelyAndCollectDeleted(Path.of(output), deletedOutputPaths, dirsToDelete)
if (deleted) {
doneSomething = true
}
}
if (!deletedOutputPaths.isEmpty()) {
if (logger.isEnabled) {
logger.logDeletedFiles(deletedOutputPaths)
}
context.processMessage(FileDeletedEvent(deletedOutputPaths))
var doneSomething = false
val dataManager = context.projectDescriptor.dataManager
val sourceToOutputStorage = dataManager.getSourceToOutputMap(target)
// actually delete outputs associated with removed paths
for (deletedFile in deletedPaths) {
// deleting outputs corresponding to a non-existing source
val outputs = sourceToOutputStorage.getOutputs(deletedFile)
if (outputs != null && !outputs.isEmpty()) {
val deletedOutputFiles = ArrayList<Path>()
for (output in outputs) {
val deleted = deleteAndCollectDeleted(output, deletedOutputFiles, dirsToDelete)
if (deleted) {
doneSomething = true
}
}
// check if the deleted source was associated with a form
val sourceToFormMap = dataManager.getSourceToFormMap(target)
val boundForms = sourceToFormMap.getOutputs(deletedSource)
if (boundForms != null) {
for (formPath in boundForms) {
val formFile = Path.of(formPath)
if (Files.exists(formFile)) {
FSOperations.markDirty(context, CompilationRound.CURRENT, formFile.toFile())
}
}
sourceToFormMap.remove(deletedSource)
if (!deletedOutputFiles.isEmpty()) {
log.info("Deleted files: $deletedOutputFiles")
context.processMessage(FileDeletedEvent(deletedOutputFiles.map { it.toString() }))
}
}
// check if the deleted source was associated with a form
val sourceToFormMap = dataManager.getSourceToFormMap(target)
val boundForms = sourceToFormMap.getOutputs(deletedFile)
if (boundForms != null) {
for (formFile in boundForms) {
if (Files.exists(formFile)) {
FSOperations.markDirty(context, CompilationRound.CURRENT, formFile.toFile())
}
}
sourceToFormMap.remove(deletedFile)
}
}
if (!targetToRemovedSources.isEmpty()) {
val existing = Utils.REMOVED_SOURCES_KEY.get(context)
if (existing != null) {
for (entry in existing.entries) {
val paths = targetToRemovedSources.get(entry.key)
if (paths == null) {
targetToRemovedSources.put(entry.key, entry.value)
}
else {
paths.addAll(entry.value)
}
}
}
Utils.REMOVED_SOURCES_KEY.set(context, targetToRemovedSources)
val existing = Utils.REMOVED_SOURCES_KEY.get(context).get(target)
if (existing == null) {
Utils.REMOVED_SOURCES_KEY.set(context, java.util.Map.of(target, deletedPaths as Collection<Path>))
}
else {
val set = LinkedHashSet<Path>()
set.addAll(existing)
set.addAll(deletedPaths)
Utils.REMOVED_SOURCES_KEY.set(context, java.util.Map.of(target, set as Collection<Path>))
}
FSOperations.pruneEmptyDirs(context, dirsToDelete)
@@ -256,19 +225,21 @@ internal class JpsProjectBuilder(
}
// return true if changed something, false otherwise
private fun runModuleLevelBuilders(context: CompileContext, moduleTarget: BazelModuleBuildTarget, buildProgress: BuildProgress): Boolean {
val chunk = ModuleChunk(setOf(moduleTarget))
for (category in BuilderCategory.entries) {
for (builder in builderRegistry.getBuilders(category)) {
builder.chunkBuildStarted(context, chunk)
}
private fun runModuleLevelBuilders(
context: CompileContext,
moduleTarget: BazelModuleBuildTarget,
builders: Array<ModuleLevelBuilder>,
): Boolean {
val chunk = ModuleChunk(java.util.Set.of<ModuleBuildTarget>(moduleTarget))
for (builder in builders) {
builder.chunkBuildStarted(context, chunk)
}
completeRecompiledSourcesSet(context, moduleTarget)
if (!isCleanBuild) {
completeRecompiledSourcesSet(context, moduleTarget)
}
var doneSomething = false
var rebuildFromScratchRequested = false
var nextPassRequired: Boolean
val outputConsumer = ChunkBuildOutputConsumerImpl(context)
try {
val fsState = context.projectDescriptor.fsState
@@ -280,11 +251,13 @@ internal class JpsProjectBuilder(
}
}
var rebuildFromScratchRequested = false
var nextPassRequired: Boolean
do {
nextPassRequired = false
fsState.beforeNextRoundStart(context, chunk)
if (!JavaBuilderUtil.isForcedRecompilationAllJavaModules(context.scope)) {
if (!isCleanBuild) {
val cleanedSources = BuildOperations.cleanOutputsCorrespondingToChangedFiles(context, dirtyFilesHolder)
for (entry in cleanedSources.entries) {
val files = entry.value.keys
@@ -293,11 +266,11 @@ internal class JpsProjectBuilder(
}
val mapping = context.projectDescriptor.dataManager.getSourceToOutputMap(entry.key)
for (srcFile in files) {
val outputs = entry.value.get(srcFile)!!
mapping.setOutputs(srcFile.path, outputs)
for (sourceFile in files) {
val outputs = entry.value.get(sourceFile)!!
mapping.setOutputs(sourceFile, outputs)
if (!outputs.isEmpty()) {
messageHandler.info("Some outputs were not removed for ${srcFile.path} source file: $outputs")
log.info("Some outputs were not removed for $sourceFile source file: $outputs")
}
}
}
@@ -305,73 +278,58 @@ internal class JpsProjectBuilder(
try {
var buildersPassed = 0
BUILDER_CATEGORY_LOOP@ for (category in BuilderCategory.entries) {
val builders = builderRegistry.getBuilders(category)
if (category == BuilderCategory.CLASS_POST_PROCESSOR) {
var instrumentedClassesSaved = false
for (builder in builders) {
if (builder.category == BuilderCategory.CLASS_POST_PROCESSOR && !instrumentedClassesSaved) {
instrumentedClassesSaved = true
// ensure changes from instruments are visible to class post-processors
saveInstrumentedClasses(outputConsumer)
}
if (builders.isEmpty()) {
continue
outputConsumer.setCurrentBuilderName(builder.presentableName)
processDeletedPaths(context, moduleTarget)
val start = System.nanoTime()
val processedSourcesBefore = outputConsumer.getNumberOfProcessedSources()
val buildResult = builder.build(context, chunk, dirtyFilesHolder, outputConsumer)
storeBuilderStatistics(
builder = builder,
elapsedTime = System.nanoTime() - start,
processedFiles = outputConsumer.getNumberOfProcessedSources() - processedSourcesBefore,
)
doneSomething = doneSomething or (buildResult != ModuleLevelBuilder.ExitCode.NOTHING_DONE)
if (buildResult == ModuleLevelBuilder.ExitCode.ABORT) {
throw StopBuildException("Builder ${builder.presentableName} requested build stop")
}
try {
for (builder in builders) {
outputConsumer.setCurrentBuilderName(builder.presentableName)
processDeletedPaths(context, moduleTarget)
val start = System.nanoTime()
val processedSourcesBefore = outputConsumer.getNumberOfProcessedSources()
val buildResult = builder.build(context, chunk, dirtyFilesHolder, outputConsumer)
storeBuilderStatistics(
builder = builder,
elapsedTime = System.nanoTime() - start,
processedFiles = outputConsumer.getNumberOfProcessedSources() - processedSourcesBefore,
)
doneSomething = doneSomething or (buildResult != ModuleLevelBuilder.ExitCode.NOTHING_DONE)
if (buildResult == ModuleLevelBuilder.ExitCode.ABORT) {
throw StopBuildException("Builder ${builder.presentableName} requested build stop")
}
context.checkCanceled()
if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) {
nextPassRequired = true
}
else if (buildResult == ModuleLevelBuilder.ExitCode.CHUNK_REBUILD_REQUIRED) {
if (!rebuildFromScratchRequested && !JavaBuilderUtil.isForcedRecompilationAllJavaModules(context.scope)) {
notifyChunkRebuildRequested(context, chunk, builder)
// allow rebuild from scratch only once per chunk
rebuildFromScratchRequested = true
try {
// forcibly mark all files in the chunk dirty
fsState.clearContextRoundData(context)
FSOperations.markDirty(context, CompilationRound.NEXT, chunk, null)
// reverting to the beginning
nextPassRequired = true
outputConsumer.clear()
break@BUILDER_CATEGORY_LOOP
}
catch (e: Exception) {
throw ProjectBuildException(e)
}
}
else {
messageHandler.debug("Builder ${builder.presentableName} requested second chunk rebuild")
}
}
buildersPassed++
for (target in chunk.targets) {
buildProgress.updateProgress(target, (buildersPassed.toDouble()) / totalModuleLevelBuilderCount, context)
}
context.checkCanceled()
if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) {
nextPassRequired = true
}
else if (buildResult == ModuleLevelBuilder.ExitCode.CHUNK_REBUILD_REQUIRED) {
if (!rebuildFromScratchRequested && !isCleanBuild) {
notifyChunkRebuildRequested(context, chunk, builder)
// allow rebuild from scratch only once per chunk
rebuildFromScratchRequested = true
// forcibly mark all files in the chunk dirty
fsState.clearContextRoundData(context)
FSOperations.markDirty(context, CompilationRound.NEXT, chunk, null)
// reverting to the beginning
nextPassRequired = true
outputConsumer.clear()
break
}
else {
log.debug("Builder ${builder.presentableName} requested second chunk rebuild")
}
}
finally {
outputConsumer.setCurrentBuilderName(null)
}
buildersPassed++
}
}
finally {
outputConsumer.setCurrentBuilderName(null)
val moreToCompile = JavaBuilderUtil.updateMappingsOnRoundCompletion(context, dirtyFilesHolder, chunk)
if (moreToCompile) {
nextPassRequired = true
@@ -384,10 +342,8 @@ internal class JpsProjectBuilder(
saveInstrumentedClasses(outputConsumer)
outputConsumer.fireFileGeneratedEvents()
outputConsumer.clear()
for (category in BuilderCategory.entries) {
for (builder in builderRegistry.getBuilders(category)) {
builder.chunkBuildFinished(context, chunk)
}
for (builder in builders) {
builder.chunkBuildFinished(context, chunk)
}
if (Utils.errorsDetected(context)) {
context.processMessage(CompilerMessage("", BuildMessage.Kind.JPS_INFO, "Errors occurred while compiling module ${chunk.presentableShortName}"))
@@ -397,59 +353,59 @@ internal class JpsProjectBuilder(
return doneSomething
}
private fun ensureFsStateInitialized(context: CompileContext, target: BuildTarget<*>) {
private fun initFsStateForCleanBuild(context: CompileContext, target: BuildTarget<*>) {
val fsState = context.projectDescriptor.fsState
if (isCleanBuild) {
val targetRoots = (context.projectDescriptor.buildRootIndex as BazelBuildRootIndex).descriptors
fsState.getDelta(target).clearRecompile(targetRoots)
for (rootDescriptor in targetRoots) {
// if it is a full project rebuild, all storages are already completely cleared;
// so passing null as stampStorage because there is no need to access the storage to clear non-existing data
fsState.markDirty(
/* context = */ context,
/* round = */ CompilationRound.CURRENT,
/* file = */ rootDescriptor.file,
/* buildRootDescriptor = */ rootDescriptor,
/* stampStorage = */ null,
/* saveEventStamp = */ false,
)
}
FSOperations.addCompletelyMarkedDirtyTarget(context, target)
fsState.markInitialScanPerformed(target)
}
else {
require(!fsState.isInitialScanPerformed(target))
initTargetFsStateForNonInitialBuild(context, target, messageHandler)
val targetRoots = (context.projectDescriptor.buildRootIndex as BazelBuildRootIndex).descriptors
fsState.getDelta(target).clearRecompile(targetRoots)
for (rootDescriptor in targetRoots) {
// if it is a full project rebuild, all storages are already completely cleared;
// so passing null as stampStorage because there is no need to access the storage to clear non-existing data
fsState.markDirty(
/* context = */ context,
/* round = */ CompilationRound.CURRENT,
/* file = */ rootDescriptor.file,
/* buildRootDescriptor = */ rootDescriptor,
/* stampStorage = */ null,
/* saveEventStamp = */ false,
)
}
FSOperations.addCompletelyMarkedDirtyTarget(context, target)
fsState.markInitialScanPerformed(target)
}
private fun buildTargetChunk(context: CompileContext, buildProgress: BuildProgress, moduleTarget: BazelModuleBuildTarget) {
val buildSpan = Tracer.start { "Building ${moduleTarget.presentableName}" }
private fun buildTargetChunk(context: CompileContext, target: BazelModuleBuildTarget, builders: Array<ModuleLevelBuilder>) {
val buildSpan = Tracer.start { "Building ${target.presentableName}" }
val fsState = context.projectDescriptor.fsState
var doneSomething: Boolean
val targets = java.util.Set.of<BuildTarget<*>>(moduleTarget)
val targets = java.util.Set.of<BuildTarget<*>>(target)
try {
context.setCompilationStartStamp(targets, System.currentTimeMillis())
sendBuildingTargetMessages(targets, BuildingTargetProgressMessage.Event.STARTED)
Utils.ERRORS_DETECTED_KEY.set(context, false)
ensureFsStateInitialized(context = context, target = moduleTarget)
val fsState = context.projectDescriptor.fsState
if (isCleanBuild) {
initFsStateForCleanBuild(context = context, target = target)
}
else {
require(!fsState.isInitialScanPerformed(target))
initTargetFsStateForNonInitialBuild(context = context, target = target, log = log, dataManager = dataManager)
}
doneSomething = processDeletedPaths(context, moduleTarget)
doneSomething = processDeletedPaths(context, target)
val chunk = BuildTargetChunk(targets)
fsState.beforeChunkBuildStart(context, targets)
val runBuildersSpan = Tracer.start { "runBuilders " + moduleTarget.presentableName }
doneSomething = doneSomething or runModuleLevelBuilders(context, moduleTarget, buildProgress)
val runBuildersSpan = Tracer.start { "runBuilders " + target.presentableName }
doneSomething = doneSomething or runModuleLevelBuilders(context, target, builders)
runBuildersSpan.complete()
fsState.clearContextRoundData(context)
fsState.clearContextChunk(context)
if (doneSomething) {
BuildOperations.markTargetsUpToDate(context, chunk)
BuildOperations.markTargetsUpToDate(context, targets)
}
}
catch (e: BuildDataCorruptedException) {
@@ -460,21 +416,20 @@ internal class JpsProjectBuilder(
}
catch (e: Throwable) {
val message = StringBuilder()
message.append(moduleTarget.presentableName).append(": ").append(e.javaClass.getName())
message.append(target.presentableName).append(": ").append(e.javaClass.getName())
e.message?.let {
message.append(": ").append(it)
}
throw ProjectBuildException(message.toString(), e)
}
finally {
buildProgress.onTargetChunkFinished(targets, context)
try {
// restore deleted paths that were not processed by 'integrate'
val map = Utils.REMOVED_SOURCES_KEY.get(context)
if (map != null) {
for (entry in map.entries) {
for (path in entry.value) {
fsState.registerDeleted(context, entry.key, Path.of(path), null)
for (file in entry.value) {
fsState.registerDeleted(context, entry.key, file, null)
}
}
}
@@ -488,7 +443,7 @@ internal class JpsProjectBuilder(
}
private fun sendBuildingTargetMessages(targets: Set<BuildTarget<*>>, event: BuildingTargetProgressMessage.Event) {
messageHandler.processMessage(BuildingTargetProgressMessage(targets, event))
log.processMessage(BuildingTargetProgressMessage(targets, event))
}
private fun storeBuilderStatistics(builder: Builder, elapsedTime: Long, processedFiles: Int) {
@@ -501,7 +456,7 @@ private class ChunkBuildOutputConsumerImpl(private val context: CompileContext)
private val targetToConsumer = HashMap<BuildTarget<*>, BuildOutputConsumerImpl>()
private val classes = HashMap<String, CompiledClass>()
private val targetToClassesMap = HashMap<BuildTarget<*>, MutableCollection<CompiledClass>>()
private val outputToBuilderNameMap = Object2ObjectMaps.synchronize(Object2ObjectOpenCustomHashMap<File, String>(FileHashStrategy))
private val outputToBuilderNameMap = Collections.synchronizedMap(HashMap<File, String>())
@Volatile
private var currentBuilderName: String? = null
@@ -634,66 +589,74 @@ private fun isTargetOutputCleared(context: CompileContext, target: BuildTarget<*
}
}
/**
* if an output file is generated from multiple sources, make sure all of them are added for recompilation
*/
@Suppress("SpellCheckingInspection")
private fun completeRecompiledSourcesSet(context: CompileContext, moduleBuildTarget: BazelModuleBuildTarget) {
private inline fun processFilesToRecompile(
context: CompileContext,
target: BazelModuleBuildTarget,
fsState: BuildFSState,
processor: (Path, JavaSourceRootDescriptor) -> Boolean,
): Boolean {
val scope = context.scope
if (scope.isBuildForced(moduleBuildTarget)) {
// assuming build is either forced for all targets in a chunk or for none of them
return
}
val projectDescriptor = context.projectDescriptor
val affectedOutputs = HashSet<String>()
val affectedSources = HashSet<String>()
val mappings = ArrayList<SourceToOutputMapping>()
projectDescriptor.fsState.processFilesToRecompile(context, moduleBuildTarget, object : FileProcessor<JavaSourceRootDescriptor, BazelModuleBuildTarget> {
private var srcToOut: SourceToOutputMapping? = null
override fun apply(target: BazelModuleBuildTarget, file: File, root: JavaSourceRootDescriptor): Boolean {
val src = file.invariantSeparatorsPath
if (!affectedSources.add(src)) {
return true
}
// lazy init
var srcToOut = this.srcToOut
if (srcToOut == null) {
srcToOut = projectDescriptor.dataManager.getSourceToOutputMap(target)
mappings.add(srcToOut)
this.srcToOut = srcToOut
}
val outs = srcToOut.getOutputs(src) ?: return true
// Temporary hack for KTIJ-197
// Change of only one input of *.kotlin_module files didn't trigger recompilation of all inputs in old behavior.
// Now it does. It isn't yet obvious whether it is right or wrong behavior. Let's leave old behavior for a
// while for safety and keeping kotlin incremental JPS tests green
outs.filterTo(affectedOutputs) { "kotlin_module" != it.substringAfterLast('.') }
return true
}
})
if (affectedOutputs.isEmpty()) {
return
}
for (srcToOut in mappings) {
val cursor = srcToOut.cursor()
while (cursor.hasNext()) {
val src = cursor.next()
if (!affectedSources.contains(src)) {
for (out in cursor.outputPaths) {
if (affectedOutputs.contains(out)) {
FSOperations.markDirtyIfNotDeleted(context, CompilationRound.CURRENT, Path.of(src))
break
}
val delta = fsState.getEffectiveFilesDelta(context, target)
delta.lockData()
try {
for (entry in delta.sourceMapToRecompile.entries) {
val root = entry.key as JavaSourceRootDescriptor
for (file in entry.value) {
if (!scope.isAffected(target, file)) {
continue
}
if (!processor(file, root)) {
return false
}
}
}
return true
}
finally {
delta.unlockData()
}
}
/**
* if an output file is generated from multiple sources, make sure all of them are added for recompilation
*/
private fun completeRecompiledSourcesSet(context: CompileContext, target: BazelModuleBuildTarget) {
val projectDescriptor = context.projectDescriptor
val affected = mutableListOf<List<String>>()
val affectedSources = HashSet<Path>()
val sourceToOut = projectDescriptor.dataManager.getSourceToOutputMap(target) as BazelSourceToOutputMapping
processFilesToRecompile(context = context, target = target, fsState = projectDescriptor.fsState) { file, root ->
if (!affectedSources.add(file)) {
sourceToOut.getDescriptor(file)?.outputs?.let {
affected.add(it)
}
}
true
}
if (affected.isEmpty()) {
return
}
// one output can be produced by different sources, so, we find intersection by outputs
val affectedSourceFiles = sourceToOut.findAffectedSources(affected)
if (affectedSourceFiles.isEmpty()) {
return
}
val fileToDescriptors = (context.projectDescriptor.buildRootIndex as BazelBuildRootIndex).fileToDescriptors
val stampStorage = projectDescriptor.dataManager.getFileStampStorage(target)
for (file in affectedSourceFiles) {
val rootDescriptor = fileToDescriptors.get(file) ?: continue
context.projectDescriptor.fsState.markDirtyIfNotDeleted(
context,
CompilationRound.CURRENT,
file,
rootDescriptor,
stampStorage
)
}
}

View File

@@ -2,18 +2,21 @@
package org.jetbrains.bazel.jvm.jps.impl
import org.jetbrains.bazel.jvm.jps.ConsoleMessageHandler
import org.jetbrains.bazel.jvm.jps.RequestLog
import org.jetbrains.jps.builders.BuildTarget
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.FSOperations
import org.jetbrains.jps.incremental.fs.BuildFSState
import org.jetbrains.jps.incremental.fs.FilesDelta
import org.jetbrains.jps.incremental.storage.RelativePathType
import org.jetbrains.jps.incremental.storage.StampsStorage
internal fun initTargetFsStateForNonInitialBuild(context: CompileContext, target: BuildTarget<*>, messageHandler: ConsoleMessageHandler) {
internal fun initTargetFsStateForNonInitialBuild(
context: CompileContext,
target: BuildTarget<*>,
log: RequestLog,
dataManager: BazelBuildDataProvider,
) {
val projectDescriptor = context.projectDescriptor
val dataManager = projectDescriptor.dataManager
val buildRootIndex = projectDescriptor.buildRootIndex as BazelBuildRootIndex
val fsState = projectDescriptor.fsState
@@ -21,7 +24,7 @@ internal fun initTargetFsStateForNonInitialBuild(context: CompileContext, target
val nextDelta = context.getUserData(BuildFSState.NEXT_ROUND_DELTA_KEY)
val filesDelta = fsState.getDelta(target)
val stampStorage = dataManager.getFileStampStorage(target)!!
val stampStorage = dataManager.getFileStampStorage(target)
markDirtyFiles(
context = context,
target = target,
@@ -29,19 +32,13 @@ internal fun initTargetFsStateForNonInitialBuild(context: CompileContext, target
buildRootIndex = buildRootIndex,
fsState = fsState,
filesDelta = filesDelta,
messageHandler = messageHandler,
messageHandler = log,
)
val fileToDescriptors = buildRootIndex.fileToDescriptors
// handle deleted paths
val sourceToOutputMap = dataManager.getSourceToOutputMap(target)
for (file in sourceToOutputMap.sourceFileIterator) {
if (fileToDescriptors.contains(file)) {
continue
}
for (file in dataManager.sourceToOutputMapping.findDeletedAndUnsetDigest(fileToDescriptors)) {
currentDelta?.addDeleted(file)
nextDelta?.addDeleted(file)
filesDelta.addDeleted(file)
@@ -53,18 +50,18 @@ internal fun initTargetFsStateForNonInitialBuild(context: CompileContext, target
private fun markDirtyFiles(
context: CompileContext,
target: BuildTarget<*>,
stampStorage: StampsStorage<*>,
stampStorage: BazelStampStorage,
buildRootIndex: BazelBuildRootIndex,
fsState: BuildFSState,
filesDelta: FilesDelta,
messageHandler: ConsoleMessageHandler,
messageHandler: RequestLog,
) {
var completelyMarkedDirty = true
for (rootDescriptor in buildRootIndex.descriptors) {
fsState.clearRecompile(rootDescriptor)
val file = rootDescriptor.file
val markDirty = stampStorage.getCurrentStampIfUpToDate(file, rootDescriptor.target, null) == null
val markDirty = stampStorage.isDirty(file)
if (!markDirty) {
completelyMarkedDirty = false
continue

View File

@@ -30,6 +30,7 @@ internal fun loadJpsProject(
jpsModel: JpsModel,
moduleTarget: BazelModuleBuildTarget,
relativizer: PathRelativizerService,
buildDataProvider: BazelBuildDataProvider,
): ProjectDescriptor {
val dataPaths = BuildDataPathsImpl(dataStorageRoot)
val dataManager = BuildDataManager.createSingleDb(
@@ -37,7 +38,7 @@ internal fun loadJpsProject(
/* targetStateManager = */ BazelBuildTargetStateManager(loadTargetState(storageManager)),
/* relativizer = */ relativizer,
/* versionManager = */ NoopBuildDataVersionManager,
/* storageManager = */ storageManager,
/* buildDataProvider = */ buildDataProvider,
)
return ProjectDescriptor(
/* model = */ jpsModel,

View File

@@ -27,7 +27,7 @@ internal fun createPathRelativizer(baseDir: Path, classOutDir: Path): PathRelati
return when {
p.startsWith(baseDirPrefix) -> p.substring(baseDirPrefix.length)
p.startsWith(parentOfBaseDirPrefix) -> "../" + p.substring(parentOfBaseDirPrefix.length)
else -> error("Unexpected path: $p")
else -> error("Unexpected path: $p (baseDirPrefix=$baseDirPrefix, parentOfBaseDirPrefix=$parentOfBaseDirPrefix)")
}
}
RelativePathType.OUTPUT -> {

View File

@@ -15,6 +15,7 @@ kt_jvm_library(
"//zip:build-zip",
"//src/jps-builder:jps-standalone",
"//:kotlin-metadata",
"//src/abi"
],
visibility = ["//visibility:public"],
)

View File

@@ -7,7 +7,7 @@ import org.jetbrains.jps.incremental.MessageHandler
import org.jetbrains.jps.incremental.messages.BuildMessage
import org.jetbrains.jps.incremental.messages.CompilerMessage
class ConsoleMessageHandler(
class RequestLog(
@PublishedApi @JvmField internal val out: Appendable,
@JvmField val isDebugEnabled: Boolean,
) : MessageHandler {
@@ -26,6 +26,10 @@ class ConsoleMessageHandler(
out.appendLine("ERROR: $message")
}
fun error(message: String, error: Throwable) {
out.appendLine("ERROR: $message\n${error.stackTraceToString().prependIndent(" ")}")
}
fun info(message: String) {
out.appendLine("INFO: $message")
}

View File

@@ -6,33 +6,35 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.bazel.jvm.abi.writeAbi
import org.jetbrains.intellij.build.io.*
import org.jetbrains.jps.incremental.storage.ExperimentalSourceToOutputMapping
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassWriter
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.nio.file.*
import java.nio.file.attribute.DosFileAttributeView
import java.nio.file.attribute.PosixFileAttributeView
import java.nio.file.attribute.PosixFilePermission
import java.util.zip.ZipEntry
import java.nio.file.NoSuchFileException
import java.nio.file.Path
class SourceDescriptor(
// absolute and normalized
@JvmField var sourceFile: Path,
@JvmField var digest: ByteArray? = null,
@JvmField var outputs: List<String>? = null,
) {
//fun isEmpty(): Boolean = digest == null && outputs.isNullOrEmpty()
}
suspend fun packageToJar(
outJar: Path,
abiJar: Path?,
sourceToOutputMap: ExperimentalSourceToOutputMapping,
sourceDescriptors: Array<SourceDescriptor>,
classOutDir: Path,
messageHandler: ConsoleMessageHandler
log: RequestLog
) {
if (abiJar == null) {
withContext(Dispatchers.IO) {
createJar(
outJar = outJar,
sourceToOutputMap = sourceToOutputMap,
sourceDescriptors = sourceDescriptors,
classOutDir = classOutDir,
abiChannel = null,
messageHandler = messageHandler,
messageHandler = log,
)
}
return
@@ -43,132 +45,59 @@ suspend fun packageToJar(
launch {
createJar(
outJar = outJar,
sourceToOutputMap = sourceToOutputMap,
sourceDescriptors = sourceDescriptors,
classOutDir = classOutDir,
abiChannel = classChannel,
messageHandler = messageHandler,
messageHandler = log,
)
classChannel.close()
}
writeZipUsingTempFile(abiJar, indexWriter = null) { stream ->
val classesToBeDeleted = HashSet<String>()
for ((name, classData) in classChannel) {
val classWriter = ClassWriter(0)
val abiClassVisitor = AbiClassVisitor(classVisitor = classWriter, classesToBeDeleted = classesToBeDeleted)
ClassReader(classData).accept(abiClassVisitor, 0)
if (!abiClassVisitor.isApiClass) {
continue
}
val abiData = classWriter.toByteArray()
stream.writeDataRawEntry(ByteBuffer.wrap(abiData), name, abiData.size, abiData.size, ZipEntry.STORED, 0)
}
if (classesToBeDeleted.isNotEmpty()) {
messageHandler.debug("Non-abi classes to be deleted: ${classesToBeDeleted.size}")
}
}
writeAbi(abiJar, classChannel)
//if (classesToBeDeleted.isNotEmpty()) {
// messageHandler.debug("Non-abi classes to be deleted: ${classesToBeDeleted.size}")
//}
}
}
private suspend fun createJar(
outJar: Path,
sourceToOutputMap: ExperimentalSourceToOutputMapping,
sourceDescriptors: Array<SourceDescriptor>,
classOutDir: Path,
abiChannel: Channel<Pair<ByteArray, ByteArray>>?,
messageHandler: ConsoleMessageHandler,
messageHandler: RequestLog,
) {
val packageIndexBuilder = PackageIndexBuilder()
writeZipUsingTempFile(outJar, packageIndexBuilder.indexWriter) { stream ->
// MVStore like a TreeMap, keys already sorted
//val all = sourceToOutputMap.outputs().toList()
//val unique = LinkedHashSet(all)
//if (unique.size != all.size) {
// messageHandler.out.appendLine("Duplicated outputs: ${all.groupingBy { it }
// .eachCount()
// .filter { it.value > 1 }
// .keys
// }")
//}
// output file maybe associated with more than one output file
val uniqueGuard = HashSet<String>(sourceDescriptors.size + 10)
for (path in sourceToOutputMap.outputs().toList()) {
// duplicated - ignore it
if (path.endsWith(".kotlin_module")) {
continue
}
for (sourceDescriptor in sourceDescriptors) {
for (path in sourceDescriptor.outputs ?: continue) {
// duplicated - ignore it
if (!uniqueGuard.add(path)) {
continue
}
packageIndexBuilder.addFile(name = path, addClassDir = false)
try {
val file = classOutDir.resolve(path)
if (abiChannel != null && path.endsWith(".class")) {
val name = path.toByteArray()
val classData = stream.fileAndGetData(name, file)
abiChannel.send(name to classData)
packageIndexBuilder.addFile(name = path, addClassDir = false)
try {
val file = classOutDir.resolve(path)
if (abiChannel != null && path.endsWith(".class")) {
val name = path.toByteArray()
val classData = stream.fileAndGetData(name, file)
abiChannel.send(name to classData)
}
else {
stream.file(nameString = path, file = file)
}
}
else {
stream.file(nameString = path, file = file)
catch (_: NoSuchFileException) {
messageHandler.warn("output file exists in src-to-output mapping, but not found on disk: $path (classOutDir=$classOutDir)")
}
}
catch (_: NoSuchFileException) {
messageHandler.warn("output file exists in src-to-output mapping, but not found on disk: $path (classOutDir=$classOutDir)")
}
}
packageIndexBuilder.writePackageIndex(stream = stream, addDirEntriesMode = AddDirEntriesMode.RESOURCE_ONLY)
}
}
private inline fun writeZipUsingTempFile(file: Path, indexWriter: IkvIndexBuilder?, task: (ZipArchiveOutputStream) -> Unit) {
val tempFile = Files.createTempFile(file.parent, file.fileName.toString(), ".tmp")
var moved = false
try {
ZipArchiveOutputStream(
channel = FileChannel.open(tempFile, WRITE),
zipIndexWriter = ZipIndexWriter(indexWriter),
).use {
task(it)
}
try {
moveAtomic(tempFile, file)
}
catch (e: AccessDeniedException) {
makeFileWritable(file, e)
moveAtomic(tempFile, file)
}
moved = true
}
finally {
if (!moved) {
Files.deleteIfExists(tempFile)
}
}
}
private fun moveAtomic(from: Path, to: Path) {
try {
Files.move(from, to, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE)
}
catch (_: AtomicMoveNotSupportedException) {
Files.move(from, to, StandardCopyOption.REPLACE_EXISTING)
}
}
private fun makeFileWritable(file: Path, cause: Throwable) {
val posixView = Files.getFileAttributeView<PosixFileAttributeView?>(file, PosixFileAttributeView::class.java)
if (posixView != null) {
val permissions = posixView.readAttributes().permissions()
permissions.add(PosixFilePermission.OWNER_WRITE)
posixView.setPermissions(permissions)
}
val dosView = Files.getFileAttributeView<DosFileAttributeView?>(file, DosFileAttributeView::class.java)
@Suppress("IfThenToSafeAccess")
if (dosView != null) {
dosView.setReadOnly(false)
}
throw UnsupportedOperationException("Unable to modify file attributes. Unsupported platform.", cause)
}

View File

@@ -0,0 +1,16 @@
def get_jvm_flags(flags):
return [
# "-XX:+UseZGC",
# "-XX:+ZGenerational",
"-Xms2g",
"-Xmx8g",
"-XX:ReservedCodeCacheSize=512m",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
"-Didea.io.use.nio2=true",
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED",
"--add-opens=java.base/java.nio=ALL-UNNAMED",
"-Dkotlin.environment.keepalive=true",
] + flags

View File

@@ -1,6 +1,7 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_jvm//:jvm.bzl", "jvm_import")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
load("//:src/jvm-args.bzl", "get_jvm_flags")
kt_jvm_library(
name = "worker-lib",
@@ -24,19 +25,19 @@ kt_jvm_library(
visibility = ["//visibility:public"],
)
java_binary(
name = "worker-jvm",
runtime_deps = [":worker-lib"],
main_class = "org.jetbrains.bazel.jvm.kotlin.KotlinBuildWorker",
jvm_flags = [
"-Xms1000m",
"-Xmx6000m",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
"-Dkotlin.environment.keepalive=true",
# Kotlin Compiler sets it
"-Didea.io.use.nio2=true",
"-Dzip.handler.uses.crc.instead.of.timestamp=true",
],
jvm_flags = get_jvm_flags([]),
visibility = ["//visibility:public"],
)
java_binary(
name = "test-worker",
runtime_deps = [":worker-lib"],
main_class = "org.jetbrains.bazel.jvm.kotlin.TestKotlinBuildWorker",
jvm_flags = get_jvm_flags([]),
)

View File

@@ -30,7 +30,6 @@ import java.util.*
import java.util.jar.Attributes
import java.util.jar.JarFile
import java.util.jar.Manifest
import java.util.zip.ZipEntry
private val MANIFEST_NAME_BYTES = JarFile.MANIFEST_NAME.toByteArray()
@@ -121,15 +120,7 @@ class JarCreator(
val content = manifestContentImpl(existingFile = manifestFile)
val size = content.size
out.writeDataRawEntry(
data = ByteBuffer.wrap(content),
name = MANIFEST_NAME_BYTES,
size = size,
compressedSize = size,
method = ZipEntry.STORED,
crc = 0,
)
out.writeDataRawEntryWithoutCrc(data = ByteBuffer.wrap(content), name = MANIFEST_NAME_BYTES)
}
override fun close() {

View File

@@ -25,7 +25,7 @@ object KotlinBuildWorker : WorkRequestExecutor {
// Important issues:
// https://youtrack.jetbrains.com/issue/KT-71680/Report-a-warning-when-Serializable-public-class-has-private-serializer
private suspend fun buildKotlin(
internal suspend fun buildKotlin(
workingDir: Path,
out: Writer,
args: ArgMap<JvmBuilderFlags>,
@@ -42,7 +42,7 @@ private suspend fun buildKotlin(
Platform.JVM -> {
compileContext.execute("compile classes") {
val code = compileContext.execute("kotlinc") {
compileKotlinForJvm(args = args, context = compileContext, sources = sources, out = out, workingDir = workingDir, info = info)
compileKotlinForJvm(args = args, context = compileContext, sources = sources, out = out, baseDir = workingDir, info = info)
}
if (code != 0) {
return code
@@ -56,23 +56,21 @@ private suspend fun buildKotlin(
return 0
}
private fun createBuildInfo(argMap: ArgMap<JvmBuilderFlags>): CompilationTaskInfo {
val ruleKind = argMap.mandatorySingle(JvmBuilderFlags.RULE_KIND).split('_')
private fun createBuildInfo(args: ArgMap<JvmBuilderFlags>): CompilationTaskInfo {
val ruleKind = args.mandatorySingle(JvmBuilderFlags.RULE_KIND).split('_')
check(ruleKind.size == 3 && ruleKind[0] == "kt") {
"invalid rule kind $ruleKind"
}
return CompilationTaskInfo(
label = argMap.mandatorySingle(JvmBuilderFlags.TARGET_LABEL),
label = args.mandatorySingle(JvmBuilderFlags.TARGET_LABEL),
ruleKind = checkNotNull(RuleKind.valueOf(ruleKind[2].uppercase())) {
"unrecognized rule kind ${ruleKind[2]}"
},
platform = checkNotNull(Platform.valueOf(ruleKind[1].uppercase())) {
"unrecognized platform ${ruleKind[1]}"
},
moduleName = argMap.mandatorySingle(JvmBuilderFlags.KOTLIN_MODULE_NAME).also {
check(it.isNotBlank()) { "--kotlin_module_name should not be blank" }
},
moduleName = args.mandatorySingle(JvmBuilderFlags.KOTLIN_MODULE_NAME),
)
}

View File

@@ -4,18 +4,27 @@ package org.jetbrains.bazel.jvm.kotlin
import com.google.devtools.build.lib.view.proto.Deps
import com.google.devtools.build.lib.view.proto.Deps.Dependencies
import com.intellij.openapi.util.Disposer
import com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE
import kotlinx.coroutines.ensureActive
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder
import org.jetbrains.kotlin.cli.common.setupCommonArguments
import org.jetbrains.kotlin.cli.jvm.compiler.configurePlugins
import org.jetbrains.kotlin.cli.jvm.compiler.k2jvm
import org.jetbrains.kotlin.cli.jvm.compiler.loadPlugins
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.setupJvmSpecificArguments
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.messageCollector
import org.jetbrains.kotlin.metadata.deserialization.MetadataVersion
import org.jetbrains.kotlin.modules.JavaRootPath
import java.io.BufferedInputStream
import java.io.File
import java.io.Writer
@@ -28,18 +37,21 @@ internal suspend fun compileKotlinForJvm(
context: TraceHelper,
sources: List<Path>,
out: Writer,
workingDir: Path,
baseDir: Path,
info: CompilationTaskInfo,
): Int {
val kotlinArgs = K2JVMCompilerArguments()
configureCommonCompilerArgs(kotlinArgs, args, workingDir)
configureCommonCompilerArgs(kotlinArgs, args, baseDir)
kotlinArgs.classpath = createClasspath(args, workingDir)
val classPath = createClasspath(args, baseDir)
kotlinArgs.classpath = classPath.joinToString(File.pathSeparator) { it.toString() }
kotlinArgs.moduleName = info.moduleName
kotlinArgs.destination = workingDir.resolve(args.mandatorySingle(JvmBuilderFlags.OUT)).toString()
val outFile = baseDir.resolve(args.mandatorySingle(JvmBuilderFlags.OUT))
val outFilePath = outFile.toString()
kotlinArgs.destination = outFilePath
val pluginConfigurations = configurePlugins(args = args, workingDir = workingDir, label = info.label)
val pluginConfigurations = configurePlugins(args = args, workingDir = baseDir, label = info.label)
fun printOptions() {
wrapOutput(out, info.label, classifier = "K2JVM Compiler Arguments") {
@@ -67,12 +79,63 @@ internal suspend fun compileKotlinForJvm(
config.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, true)
}
config.put(JVMConfigurationKeys.OUTPUT_JAR, outFile.toFile())
coroutineContext.ensureActive()
val rootDisposable = Disposer.newDisposable("Disposable for Bazel Kotlin Compiler")
var code = try {
loadPlugins(configuration = config, pluginConfigurations = pluginConfigurations)
k2jvm(args = kotlinArgs, config = config, rootDisposable = rootDisposable).code
val moduleName = info.moduleName
config.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
val module = ModuleBuilder(moduleName, outFilePath, "java-production")
args.optional(JvmBuilderFlags.FRIENDS)?.let { value ->
for (path in value) {
module.addFriendDir(baseDir.resolve(path).normalize().toString())
}
}
var isJava9Module = false
val moduleInfoNameSuffix = File.separatorChar + MODULE_INFO_FILE
for (source in sources) {
val path = source.toString()
if (path.endsWith(".java")) {
module.addJavaSourceRoot(JavaRootPath(path, null))
config.addJavaSourceRoot(source.toFile(), null)
if (!isJava9Module) {
isJava9Module = path.endsWith(moduleInfoNameSuffix)
}
}
else {
module.addSourceFiles(path)
config.addKotlinSourceRoot(path = path, isCommon = false, hmppModuleName = null)
}
}
for (path in classPath) {
module.addClasspathEntry(path.toString())
}
for (file in classPath) {
val ioFile = file.toFile()
if (isJava9Module) {
config.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(ioFile))
}
config.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(ioFile))
}
config.addAll(JVMConfigurationKeys.MODULES, listOf(module))
k2jvm(
config = config,
rootDisposable = rootDisposable,
module = module,
messageCollector = messageCollector,
).code
}
finally {
Disposer.dispose(rootDisposable)
@@ -92,7 +155,7 @@ internal suspend fun compileKotlinForJvm(
val renderer = object : PlainTextMessageRenderer(/* colorEnabled = */ true) {
override fun getPath(location: CompilerMessageSourceLocation): String {
return workingDir.relativize(Path.of(location.path)).toString()
return baseDir.relativize(Path.of(location.path)).toString()
}
override fun getName(): String = "RelativePath"
@@ -111,14 +174,14 @@ private inline fun wrapOutput(out: Writer, label: String, classifier: String, ta
out.appendLine("\u001B[1m=============== END of $classifier ($label) ===============\u001B[0m")
}
private fun createClasspath(args: ArgMap<JvmBuilderFlags>, baseDir: Path): String {
private fun createClasspath(args: ArgMap<JvmBuilderFlags>, baseDir: Path): List<Path> {
if (!args.boolFlag(JvmBuilderFlags.REDUCED_CLASSPATH_MODE)) {
return args.mandatory(JvmBuilderFlags.CLASSPATH).joinToString(File.pathSeparator) { baseDir.resolve(it).normalize().toString() }
return args.mandatory(JvmBuilderFlags.CLASSPATH).map { baseDir.resolve(it).normalize() }
}
val directDependencies = args.mandatory(JvmBuilderFlags.DIRECT_DEPENDENCIES)
val depsArtifacts = args.optional(JvmBuilderFlags.DEPS_ARTIFACTS) ?: return directDependencies.joinToString(File.pathSeparator)
val depsArtifacts = args.optional(JvmBuilderFlags.DEPS_ARTIFACTS) ?: return directDependencies.map { baseDir.resolve(it).normalize() }
val transitiveDepsForCompile = LinkedHashSet<String>()
for (jdepsPath in depsArtifacts) {
BufferedInputStream(Files.newInputStream(Path.of(jdepsPath))).use {
@@ -131,7 +194,7 @@ private fun createClasspath(args: ArgMap<JvmBuilderFlags>, baseDir: Path): Strin
}
}
return (directDependencies.asSequence() + transitiveDepsForCompile).joinToString(File.pathSeparator)
return (directDependencies.asSequence() + transitiveDepsForCompile).map { baseDir.resolve(it).normalize() }.toList()
}
private data class LogMessage(

View File

@@ -0,0 +1,28 @@
package org.jetbrains.bazel.jvm.kotlin
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.jetbrains.bazel.jvm.TestModules
import org.jetbrains.bazel.jvm.collectSources
import org.jetbrains.bazel.jvm.getTestWorkerPaths
import org.jetbrains.bazel.jvm.performTestInvocation
import kotlin.io.path.ExperimentalPathApi
internal object TestKotlinBuildWorker {
@OptIn(ExperimentalPathApi::class)
@JvmStatic
fun main(startupArgs: Array<String>) {
val testPaths = getTestWorkerPaths()
val baseDir = testPaths.baseDir
runBlocking(Dispatchers.Default) {
val testModule = TestModules.PLATFORM_BOOTSTRAP
val sources = collectSources(sourceDirPath = testModule.sourcePath, paths = testPaths)
val testParams = testModule.getParams(baseDir)
val args = parseArgs(testParams.trimStart().lines().toTypedArray())
performTestInvocation { out, coroutineScope ->
buildKotlin(workingDir = baseDir, out = out, args = args, sources = sources)
}
}
}
}

View File

@@ -9,11 +9,11 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.createPhaseConfig
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder
import org.jetbrains.kotlin.cli.jvm.compiler.pipeline.compileModulesUsingFrontendIrAndLightTree
import org.jetbrains.kotlin.cli.jvm.compiler.pipeline.createProjectEnvironment
import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
import org.jetbrains.kotlin.cli.jvm.configureAdvancedJvmOptions
import org.jetbrains.kotlin.cli.jvm.configureModuleChunk
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser.RegisteredPluginInfo
import org.jetbrains.kotlin.cli.plugins.processCompilerPluginOptions
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.messageCollector
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.util.ServiceLoaderLite
import java.io.File
import java.net.URLClassLoader
@@ -52,19 +51,20 @@ private fun createCompilerConfigurationTemplate(): CompilerConfiguration {
internal val configTemplate = createCompilerConfigurationTemplate()
internal fun k2jvm(
args: K2JVMCompilerArguments,
config: CompilerConfiguration,
rootDisposable: Disposable,
module: ModuleBuilder,
messageCollector: MessageCollector,
): ExitCode {
val messageCollector = config.getNotNull(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val moduleBuilders = listOf(module)
val modularJdkRoot = module.modularJdkRoot
if (modularJdkRoot != null) {
// We use the SDK of the first module in the chunk, which is not always correct because some other module in the chunk
// might depend on a different SDK
config.put(JVMConfigurationKeys.JDK_HOME, File(modularJdkRoot))
}
val moduleName = args.moduleName ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME
config.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
val chunk = config.configureModuleChunk(arguments = args, buildFile = null).modules
config.configureSourceRoots(chunk = chunk, buildFile = null)
val targetDescription = chunk
.map { input -> input.getModuleName() + "-" + input.getModuleType() }
.let { names -> names.singleOrNull() ?: names.joinToString() }
require(config.getBoolean(CommonConfigurationKeys.USE_LIGHT_TREE))
require(config.getBoolean(CommonConfigurationKeys.USE_FIR))
if (messageCollector.hasErrors()) {
@@ -85,19 +85,19 @@ internal fun k2jvm(
return ExitCode.COMPILATION_ERROR
}
if (!compileModulesUsingFrontendIrAndLightTree(
if (compileModulesUsingFrontendIrAndLightTree(
projectEnvironment = projectEnvironment,
compilerConfiguration = config,
messageCollector = messageCollector,
buildFile = null,
chunk = chunk,
targetDescription = targetDescription,
chunk = moduleBuilders,
targetDescription = module.getModuleName() + "-" + module.getModuleType(),
checkSourceFiles = false,
isPrintingVersion = false,
)) {
return ExitCode.COMPILATION_ERROR
return ExitCode.OK
}
return ExitCode.OK
return ExitCode.COMPILATION_ERROR
}
@OptIn(ExperimentalCompilerApi::class)

View File

@@ -0,0 +1,92 @@
package org.jetbrains.bazel.jvm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.io.StringWriter
import java.io.Writer
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.io.path.walk
import kotlin.system.exitProcess
enum class TestModules(@JvmField val sourcePath: String, private val paramsPath: String) {
PLATFORM_IMPL("platform/platform-impl/src", "platform/platform-impl/ide-impl.jar-0.params"),
PLATFORM_BOOTSTRAP("platform/platform-impl/bootstrap/src", "platform/platform-impl/bootstrap/ide-bootstrap-kt.jar-0.params");
fun getParams(baseDir: Path): String {
@Suppress("SpellCheckingInspection")
return Files.readString(baseDir.resolve("bazel-out/community+/darwin_arm64-fastbuild/bin").resolve(paramsPath)).trim()
}
}
data class TestWorkerPaths(
@JvmField val baseDir: Path,
@JvmField val communityDir: Path,
val userHomeDir: Path,
)
fun getTestWorkerPaths(): TestWorkerPaths {
val userHomeDir = Path.of(System.getProperty("user.home"))
val ideaProjectDirName = if (Runtime.getRuntime().availableProcessors() >= 20) "idea-push" else "idea-second"
val projectDir = userHomeDir.resolve("projects/$ideaProjectDirName")
val communityDir = projectDir.resolve("community")
val baseDir = getBazelExecRoot(projectDir)
return TestWorkerPaths(
baseDir = baseDir,
communityDir = communityDir,
userHomeDir = Path.of(System.getProperty("user.home")),
)
}
private fun getBazelExecRoot(currentWorkingDir: Path): Path {
val process = ProcessBuilder("bazelisk", "info", "execution_root")
.redirectErrorStream(true)
.directory(currentWorkingDir.toFile())
.start()
val output = process.inputStream.readAllBytes().toString(Charsets.UTF_8).trim()
val exitCode = process.waitFor()
if (exitCode == 0) {
val result = Path.of(output)
require(Files.isDirectory(result)) {
"Not a directory: $result (currentWorkingDir=$currentWorkingDir"
}
return result
}
throw RuntimeException("Failed to retrieve exec root (output=$output, exitCode=$exitCode)")
}
@OptIn(ExperimentalPathApi::class)
fun collectSources(sourceDirPath: String, paths: TestWorkerPaths): List<Path> {
val result = paths.communityDir.resolve(sourceDirPath)
.walk()
.filter {
val p = it.toString()
p.endsWith(".kt") || p.endsWith(".java")
}
.map { "../community+/" + paths.communityDir.relativize(it).invariantSeparatorsPathString }
.sorted()
.map { paths.baseDir.resolve(it).normalize() }
.toList()
require(result.isNotEmpty())
return result
}
fun performTestInvocation(execute: suspend (out: Writer, coroutineScope: CoroutineScope) -> Int) {
val out = StringWriter()
val exitCode: Int
try {
exitCode = runBlocking(Dispatchers.Default) {
execute(out, this)
}
}
finally {
System.out.append(out.toString())
}
exitProcess(exitCode)
}

View File

@@ -77,6 +77,11 @@ class ZipArchiveOutputStream(
)
}
fun writeDataRawEntryWithoutCrc(data: ByteBuffer, name: ByteArray) {
val size = data.remaining()
writeDataRawEntry(data = data, name = name, size = size, compressedSize = size, method = ZipEntry.STORED, crc = 0)
}
// data contains only data - zip local file header will be generated
fun writeDataRawEntry(
data: ByteBuffer,

View File

@@ -22,7 +22,8 @@ import java.util.zip.ZipEntry
import kotlin.math.min
val W_CREATE_NEW: EnumSet<StandardOpenOption> = EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
val WRITE: EnumSet<StandardOpenOption> = EnumSet.of(StandardOpenOption.WRITE)
@PublishedApi
internal val WRITE: EnumSet<StandardOpenOption> = EnumSet.of(StandardOpenOption.WRITE)
private val READ = EnumSet.of(StandardOpenOption.READ)
// 1 MB

View File

@@ -4,6 +4,9 @@ package org.jetbrains.intellij.build.io
import java.io.File
import java.nio.channels.FileChannel
import java.nio.file.*
import java.nio.file.attribute.DosFileAttributeView
import java.nio.file.attribute.PosixFileAttributeView
import java.nio.file.attribute.PosixFilePermission
import java.util.*
import java.util.zip.Deflater
@@ -198,4 +201,65 @@ inline fun archiveDir(startDir: Path, addFile: (file: Path) -> Unit, excludes: L
}
}
}
}
inline fun writeZipUsingTempFile(file: Path, indexWriter: IkvIndexBuilder?, task: (ZipArchiveOutputStream) -> Unit) {
writeFileUsingTempFile(file) { tempFile ->
ZipArchiveOutputStream(
channel = FileChannel.open(tempFile, WRITE),
zipIndexWriter = ZipIndexWriter(indexWriter),
).use {
task(it)
}
}
}
inline fun writeFileUsingTempFile(file: Path, task: (tempFile: Path) -> Unit) {
val tempFile = Files.createTempFile(file.parent, file.fileName.toString(), ".tmp")
var moved = false
try {
task(tempFile)
try {
moveAtomic(tempFile, file)
}
catch (e: AccessDeniedException) {
makeFileWritable(file, e)
moveAtomic(tempFile, file)
}
moved = true
}
finally {
if (!moved) {
Files.deleteIfExists(tempFile)
}
}
}
@PublishedApi
internal fun moveAtomic(from: Path, to: Path) {
try {
Files.move(from, to, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE)
}
catch (_: AtomicMoveNotSupportedException) {
Files.move(from, to, StandardCopyOption.REPLACE_EXISTING)
}
}
@PublishedApi
internal fun makeFileWritable(file: Path, cause: Throwable) {
val posixView = Files.getFileAttributeView<PosixFileAttributeView?>(file, PosixFileAttributeView::class.java)
if (posixView != null) {
val permissions = posixView.readAttributes().permissions()
permissions.add(PosixFilePermission.OWNER_WRITE)
posixView.setPermissions(permissions)
}
val dosView = Files.getFileAttributeView<DosFileAttributeView?>(file, DosFileAttributeView::class.java)
@Suppress("IfThenToSafeAccess")
if (dosView != null) {
dosView.setReadOnly(false)
}
throw UnsupportedOperationException("Unable to modify file attributes. Unsupported platform.", cause)
}