[kotlinp] Support rendering KLIB metadata using "kotlinp" format
This commit includes: 1. New subproject :tools:kotlinp-klib that contains the facade for rendering the metadata in "kotlinp" format given just KlibModuleMetadata 2. A tool for computing (external) IR signatures that are not directly available in metadata: KlibKotlinpExternalSignatureComputer ^KT-62340
This commit is contained in:
committed by
Space Team
parent
ed30221f42
commit
83cc0d75a7
@@ -345,6 +345,7 @@
|
||||
/libraries/tools/kotlin-tooling-metadata/ "Kotlin Build Tools"
|
||||
/libraries/tools/kotlinp/ "Kotlin JVM" "Kotlin Common Backend"
|
||||
/libraries/tools/kotlinp/jvm/ "Kotlin JVM"
|
||||
/libraries/tools/kotlinp/klib/ "Kotlin Common Backend"
|
||||
/libraries/tools/maven-archetypes/ "Kotlin Build Tools"
|
||||
/libraries/tools/mutability-annotations-compat/ "Kotlin Libraries"
|
||||
/libraries/tools/script-runtime/ "Kotlin Compiler Core"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":kotlinx-metadata"))
|
||||
compileOnly(project(":kotlinx-metadata-klib"))
|
||||
|
||||
api(project(":tools:kotlinp"))
|
||||
|
||||
testApi(intellijCore())
|
||||
|
||||
testCompileOnly(project(":kotlinx-metadata"))
|
||||
testCompileOnly(project(":kotlinx-metadata-klib"))
|
||||
|
||||
testImplementation(libs.junit4)
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(projectTests(":generators:test-generator"))
|
||||
|
||||
testRuntimeOnly(project(":kotlinx-metadata-klib"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kotlinp.klib
|
||||
|
||||
sealed interface DeclarationId
|
||||
sealed interface ClassifierId
|
||||
|
||||
/**
|
||||
* Represents a class or a type alias.
|
||||
*
|
||||
* @param qualifiedName the fully qualified name. Examples: `Outer`, `Outer.Nested`, `org/sample/Outer.Nested`.
|
||||
*/
|
||||
data class ClassOrTypeAliasId(val qualifiedName: String) : DeclarationId, ClassifierId, Comparable<ClassOrTypeAliasId> {
|
||||
override fun compareTo(other: ClassOrTypeAliasId) = qualifiedName.compareTo(other.qualifiedName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a class constructor.
|
||||
*
|
||||
* @param qualifiedName the fully qualified name. Examples: `Outer.<init>`, `Outer.Nested.<init>`, `org/sample/Outer.Nested.<init>`.
|
||||
* @param parameters value parameters.
|
||||
*/
|
||||
data class ConstructorId(val qualifiedName: String, val parameters: List<ParameterId>) : DeclarationId, Comparable<ConstructorId> {
|
||||
override fun compareTo(other: ConstructorId) = COMPARATOR.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val COMPARATOR = compareBy(ConstructorId::qualifiedName)
|
||||
.thenByList(ConstructorId::parameters)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a function or a property accessor.
|
||||
*
|
||||
* @param qualifiedName the fully qualified name. Examples: `foo`, `org/sample/foo`, `Outer.foo`, `Outer.Nested.foo`,
|
||||
* `org/sample/Outer.Nested.foo`, `<get-bar>`, `org/sample/<get-bar>`, `Outer.<get-bar>`, `Outer.Nested.<get-bar>`,
|
||||
* `org/sample/Outer.Nested.<get-bar>`.
|
||||
* @param contextReceivers context receivers.
|
||||
* @param extensionReceiver extension receiver.
|
||||
* @param parameters value parameters.
|
||||
*/
|
||||
data class FunctionId(
|
||||
val qualifiedName: String,
|
||||
val contextReceivers: List<TypeId>,
|
||||
val extensionReceiver: TypeId?,
|
||||
val parameters: List<ParameterId>,
|
||||
val returnType: TypeId,
|
||||
) : DeclarationId, Comparable<FunctionId> {
|
||||
override fun compareTo(other: FunctionId) = COMPARATOR.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val COMPARATOR = compareBy(FunctionId::qualifiedName)
|
||||
.thenByList(FunctionId::contextReceivers)
|
||||
.thenBy(FunctionId::extensionReceiver)
|
||||
.thenByList(FunctionId::parameters)
|
||||
.thenBy(FunctionId::returnType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a property.
|
||||
*
|
||||
* @param qualifiedName the fully qualified name. Examples: `foo`, `org/sample/foo`, `Outer.foo`, `Outer.Nested.foo`,
|
||||
* `org/sample/Outer.Nested.foo`.
|
||||
* @param contextReceivers context receivers.
|
||||
* @param extensionReceiver extension receiver.
|
||||
*/
|
||||
data class PropertyId(
|
||||
val qualifiedName: String,
|
||||
val contextReceivers: List<TypeId>,
|
||||
val extensionReceiver: TypeId?,
|
||||
val returnType: TypeId,
|
||||
) : DeclarationId, Comparable<PropertyId> {
|
||||
override fun compareTo(other: PropertyId) = COMPARATOR.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val COMPARATOR = compareBy(PropertyId::qualifiedName)
|
||||
.thenByList(PropertyId::contextReceivers)
|
||||
.thenBy(PropertyId::extensionReceiver)
|
||||
.thenBy(PropertyId::returnType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a value parameter.
|
||||
*
|
||||
* @param name value parameter name.
|
||||
* @param type value parameter type.
|
||||
* @param isVararg if it is a vararg.
|
||||
*/
|
||||
data class ParameterId(val name: String, val type: TypeId, val isVararg: Boolean) : Comparable<ParameterId> {
|
||||
constructor(type: TypeId, isVararg: Boolean) : this(IGNORED_NAME, type, isVararg)
|
||||
|
||||
override fun compareTo(other: ParameterId) = COMPARATOR.compare(this, other)
|
||||
|
||||
companion object {
|
||||
const val IGNORED_NAME = "?"
|
||||
|
||||
private val COMPARATOR = compareBy(
|
||||
ParameterId::type,
|
||||
ParameterId::isVararg,
|
||||
ParameterId::name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a type parameter.
|
||||
*
|
||||
* @param index the type parameter index.
|
||||
*/
|
||||
data class TypeParameterId(val index: Int) : ClassifierId, Comparable<TypeParameterId> {
|
||||
override fun compareTo(other: TypeParameterId) = index.compareTo(other.index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a type.
|
||||
*
|
||||
* @param classifier the classifier.
|
||||
* @param arguments type arguments.
|
||||
*/
|
||||
data class TypeId(val classifier: ClassifierId, val arguments: List<TypeArgumentId>) : Comparable<TypeId> {
|
||||
override fun compareTo(other: TypeId) = COMPARATOR.compare(this, other)
|
||||
|
||||
companion object {
|
||||
val UNIT = TypeId(ClassOrTypeAliasId("kotlin/Unit"), emptyList())
|
||||
|
||||
private val COMPARATOR = compareBy<TypeId>(
|
||||
{ it.classifier as? ClassOrTypeAliasId },
|
||||
{ it.classifier as? TypeParameterId }
|
||||
).thenByList(TypeId::arguments)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a type argument.
|
||||
*/
|
||||
sealed class TypeArgumentId : Comparable<TypeArgumentId> {
|
||||
final override fun compareTo(other: TypeArgumentId) = COMPARATOR.compare(this, other)
|
||||
|
||||
data object Star : TypeArgumentId()
|
||||
data class Regular(val type: TypeId, val variance: VarianceId) : TypeArgumentId()
|
||||
|
||||
enum class VarianceId { INVARIANT, IN, OUT }
|
||||
|
||||
companion object {
|
||||
private val COMPARATOR = Comparator<TypeArgumentId> { left, right ->
|
||||
when {
|
||||
left is Regular && right is Regular -> REGULAR_ARGUMENT_COMPARATOR.compare(left, right)
|
||||
left is Regular -> 1
|
||||
right is Regular -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
private val REGULAR_ARGUMENT_COMPARATOR = compareBy(Regular::type, Regular::variance)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T, R : Comparable<R>> Comparator<T>.thenByList(
|
||||
crossinline selector: (T) -> List<R>,
|
||||
): Comparator<T> = Comparator { left, right ->
|
||||
compare(left, right).let { if (it != 0) return@Comparator it }
|
||||
|
||||
val leftList = selector(left)
|
||||
val rightList = selector(right)
|
||||
|
||||
leftList.size.compareTo(rightList.size).let { if (it != 0) return@Comparator it }
|
||||
|
||||
for (index in leftList.indices) {
|
||||
leftList[index].compareTo(rightList[index]).let { if (it != 0) return@Comparator it }
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kotlinp.klib
|
||||
|
||||
import kotlinx.metadata.*
|
||||
import kotlinx.metadata.klib.KlibEnumEntry
|
||||
import org.jetbrains.kotlin.kotlinp.klib.TypeArgumentId.VarianceId
|
||||
|
||||
private fun KmClassifier.classifierId(): ClassifierId = when (this) {
|
||||
is KmClassifier.Class -> ClassOrTypeAliasId(name)
|
||||
is KmClassifier.TypeAlias -> ClassOrTypeAliasId(name)
|
||||
is KmClassifier.TypeParameter -> TypeParameterId(id)
|
||||
}
|
||||
|
||||
private fun KmVariance.varianceId(): VarianceId = when (this) {
|
||||
KmVariance.INVARIANT -> VarianceId.INVARIANT
|
||||
KmVariance.IN -> VarianceId.IN
|
||||
KmVariance.OUT -> VarianceId.OUT
|
||||
}
|
||||
|
||||
private fun KmTypeProjection.typeArgumentId(): TypeArgumentId =
|
||||
if (this == KmTypeProjection.STAR) {
|
||||
TypeArgumentId.Star
|
||||
} else {
|
||||
val (variance, type) = this
|
||||
check(variance != null && type != null) { "Variance and type should not be null" }
|
||||
TypeArgumentId.Regular(type.typeId(), variance.varianceId())
|
||||
}
|
||||
|
||||
private fun KmType.typeId(): TypeId = TypeId(classifier.classifierId(), arguments.map { it.typeArgumentId() })
|
||||
|
||||
private fun KmValueParameter.valueParameterId(): ParameterId = ParameterId(name, type.typeId(), varargElementType != null)
|
||||
|
||||
internal fun KmClass.classId(): ClassOrTypeAliasId = ClassOrTypeAliasId(name)
|
||||
internal fun KmClass.enumEntryId(enumEntry: KlibEnumEntry): ClassOrTypeAliasId = ClassOrTypeAliasId("$name.${enumEntry.name}")
|
||||
internal fun KmTypeAlias.typeAliasId(containerNamePrefix: String): ClassOrTypeAliasId = ClassOrTypeAliasId("$containerNamePrefix$name")
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
fun KmFunction.functionId(containerNamePrefix: String): FunctionId = FunctionId(
|
||||
qualifiedName = "$containerNamePrefix$name",
|
||||
contextReceivers = contextReceiverTypes.map { it.typeId() },
|
||||
extensionReceiver = receiverParameterType?.typeId(),
|
||||
parameters = valueParameters.map { it.valueParameterId() },
|
||||
returnType = returnType.typeId()
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
fun KmProperty.propertyId(containerNamePrefix: String): PropertyId = PropertyId(
|
||||
qualifiedName = "$containerNamePrefix$name",
|
||||
contextReceivers = contextReceiverTypes.map { it.typeId() },
|
||||
extensionReceiver = receiverParameterType?.typeId(),
|
||||
returnType = returnType.typeId()
|
||||
)
|
||||
|
||||
internal fun KmProperty.getterId(propertyId: PropertyId): FunctionId = FunctionId(
|
||||
qualifiedName = "${propertyId.qualifiedName.dropLast(name.length)}<get-$name>",
|
||||
contextReceivers = propertyId.contextReceivers,
|
||||
extensionReceiver = propertyId.extensionReceiver,
|
||||
parameters = emptyList(),
|
||||
returnType = propertyId.returnType
|
||||
)
|
||||
|
||||
internal fun KmProperty.setterId(propertyId: PropertyId, setterParameter: KmValueParameter?): FunctionId = FunctionId(
|
||||
qualifiedName = "${propertyId.qualifiedName.dropLast(name.length)}<set-$name>",
|
||||
contextReceivers = propertyId.contextReceivers,
|
||||
extensionReceiver = propertyId.extensionReceiver,
|
||||
parameters = listOf(ParameterId((setterParameter?.type ?: returnType).typeId(), false)),
|
||||
returnType = TypeId.UNIT
|
||||
)
|
||||
|
||||
fun KmConstructor.constructorId(containerNamePrefix: String): ConstructorId = ConstructorId(
|
||||
qualifiedName = "$containerNamePrefix<init>",
|
||||
parameters = valueParameters.map { it.valueParameterId() }
|
||||
)
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kotlinp.klib
|
||||
|
||||
import kotlinx.metadata.*
|
||||
import kotlinx.metadata.klib.KlibEnumEntry
|
||||
import kotlinx.metadata.klib.KlibModuleMetadata
|
||||
import kotlinx.metadata.klib.fqName
|
||||
import kotlinx.metadata.klib.klibEnumEntries
|
||||
import java.util.IdentityHashMap
|
||||
|
||||
/**
|
||||
* An auxiliary tool that allows computing and caching IR signatures for the metadata declarations rendered
|
||||
* by [KlibKotlinp]. Such signatures are not available directly in the metadata, but they can be computed by
|
||||
* descriptors (K1) or Lazy IR built over FIR (K2) and then properly mapped to the corresponding `Km*` entities.
|
||||
*/
|
||||
class ExternalSignatureComputer(module: KlibModuleMetadata, getExternallyComputedSignature: (DeclarationId) -> String?) {
|
||||
/**
|
||||
* Note on [IdentityHashMap]: It's presence here might look strange. But in fact this is an approach that allows memorizing
|
||||
* externally computed signatures and then looking up them using `Km*` entities as keys. I.e. no context needs to be maintained
|
||||
* while traversing the `Km*`-tree (like: what is the containing declaration of a particular [KmFunction]?).
|
||||
*/
|
||||
private val signatures = IdentityHashMap<Any, String>()
|
||||
private val getterSignatures = IdentityHashMap<KmProperty, String>()
|
||||
private val setterSignatures = IdentityHashMap<KmProperty, String>()
|
||||
|
||||
init {
|
||||
fun visitDeclarationContainer(containerNamePrefix: String, container: KmDeclarationContainer) {
|
||||
container.functions.forEach { function ->
|
||||
signatures[function] = getExternallyComputedSignature(function.functionId(containerNamePrefix))
|
||||
}
|
||||
container.properties.forEach { property ->
|
||||
val propertyId = property.propertyId(containerNamePrefix)
|
||||
signatures[property] = getExternallyComputedSignature(propertyId)
|
||||
getterSignatures[property] = getExternallyComputedSignature(property.getterId(propertyId))
|
||||
if (property.setter != null) {
|
||||
setterSignatures[property] = getExternallyComputedSignature(property.setterId(propertyId, property.setterParameter))
|
||||
}
|
||||
}
|
||||
container.typeAliases.forEach { typeAlias ->
|
||||
signatures[typeAlias] = getExternallyComputedSignature(typeAlias.typeAliasId(containerNamePrefix))
|
||||
}
|
||||
}
|
||||
|
||||
module.fragments.forEach { fragment ->
|
||||
fragment.classes.forEach { clazz ->
|
||||
signatures[clazz] = getExternallyComputedSignature(clazz.classId())
|
||||
val classNamePrefix = "${clazz.name}."
|
||||
clazz.constructors.forEach { constructor ->
|
||||
signatures[constructor] = getExternallyComputedSignature(constructor.constructorId(classNamePrefix))
|
||||
}
|
||||
visitDeclarationContainer(classNamePrefix, clazz)
|
||||
clazz.klibEnumEntries.forEach { enumEntry ->
|
||||
signatures[enumEntry] = getExternallyComputedSignature(clazz.enumEntryId(enumEntry))
|
||||
}
|
||||
}
|
||||
fragment.pkg?.let {
|
||||
val packageName = fragment.fqName?.replace('.', '/').orEmpty()
|
||||
visitDeclarationContainer(if (packageName.isNotEmpty()) "$packageName/" else "", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun classSignature(clazz: KmClass): String? = signatures[clazz]
|
||||
fun enumEntrySignature(enumEntry: KlibEnumEntry): String? = signatures[enumEntry]
|
||||
fun constructorSignature(constructor: KmConstructor): String? = signatures[constructor]
|
||||
fun functionSignature(function: KmFunction): String? = signatures[function]
|
||||
fun propertySignature(property: KmProperty): String? = signatures[property]
|
||||
fun propertyGetterSignature(property: KmProperty): String? = getterSignatures[property]
|
||||
fun propertySetterSignature(property: KmProperty): String? = setterSignatures[property]
|
||||
fun typeAliasSignature(typeAlias: KmTypeAlias): String? = signatures[typeAlias]
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kotlinp.klib
|
||||
|
||||
import kotlinx.metadata.*
|
||||
import kotlinx.metadata.internal.common.KmModuleFragment
|
||||
import kotlinx.metadata.klib.*
|
||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.Settings
|
||||
import org.jetbrains.kotlin.kotlinp.Printer
|
||||
|
||||
class KlibKotlinp(
|
||||
settings: Settings,
|
||||
private val signatureComputer: ExternalSignatureComputer?,
|
||||
) : Kotlinp(settings) {
|
||||
fun renderModule(module: KlibModuleMetadata, printer: Printer): Unit = with(printer) {
|
||||
appendLine("library {")
|
||||
withIndent {
|
||||
appendCommentedLine("module name: ", module.name)
|
||||
module.fragments.forEach { renderModuleFragment(it, printer) }
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
private fun renderModuleFragment(moduleFragment: KmModuleFragment, printer: Printer): Unit = with(printer) {
|
||||
appendLine()
|
||||
appendLine("library fragment {")
|
||||
withIndent {
|
||||
moduleFragment.fqName?.let {
|
||||
appendCommentedLine("package name: ", it.ifEmpty { "<root>" })
|
||||
}
|
||||
if (moduleFragment.className.isNotEmpty()) {
|
||||
appendLine()
|
||||
moduleFragment.className.sortIfNeeded { it }.forEach {
|
||||
appendCommentedLine("class name: ", it)
|
||||
}
|
||||
}
|
||||
moduleFragment.classes.forEach { appendLine(); renderClass(it, printer) }
|
||||
moduleFragment.pkg?.let { appendLine(); renderPackage(it, printer) }
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
override fun getAnnotations(clazz: KmClass) = clazz.annotations
|
||||
override fun getAnnotations(constructor: KmConstructor) = constructor.annotations
|
||||
override fun getAnnotations(function: KmFunction) = function.annotations
|
||||
override fun getAnnotations(property: KmProperty) = property.annotations
|
||||
override fun getGetterAnnotations(property: KmProperty) = property.getterAnnotations
|
||||
override fun getSetterAnnotations(property: KmProperty) = property.setterAnnotations
|
||||
override fun getAnnotations(typeParameter: KmTypeParameter) = typeParameter.annotations
|
||||
override fun getAnnotations(type: KmType) = type.annotations
|
||||
override fun getAnnotations(valueParameter: KmValueParameter) = valueParameter.annotations
|
||||
|
||||
override fun Printer.appendSignatures(clazz: KmClass) = appendSignature { classSignature(clazz) }
|
||||
override fun Printer.appendSignatures(constructor: KmConstructor) = appendSignature { constructorSignature(constructor) }
|
||||
override fun Printer.appendSignatures(function: KmFunction) = appendSignature { functionSignature(function) }
|
||||
override fun Printer.appendSignatures(property: KmProperty) = appendSignature { propertySignature(property) }
|
||||
override fun Printer.appendGetterSignatures(property: KmProperty) = appendSignature { propertyGetterSignature(property) }
|
||||
override fun Printer.appendSetterSignatures(property: KmProperty) = appendSignature { propertySetterSignature(property) }
|
||||
override fun Printer.appendSignatures(typeAlias: KmTypeAlias) = appendSignature { typeAliasSignature(typeAlias) }
|
||||
|
||||
private inline fun Printer.appendSignature(extractSignature: ExternalSignatureComputer.() -> String?) {
|
||||
val signature = signatureComputer?.let(extractSignature) ?: return
|
||||
appendCommentedLine("signature: ", signature)
|
||||
}
|
||||
|
||||
override fun Printer.appendEnumEntries(clazz: KmClass) {
|
||||
clazz.klibEnumEntries.forEach { enumEntry ->
|
||||
appendLine()
|
||||
appendSignature { enumEntrySignature(enumEntry) }
|
||||
appendAnnotations(hasAnnotations = null, enumEntry.annotations)
|
||||
appendLine(enumEntry.name, ",")
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendCompileTimeConstant(property: KmProperty): Printer {
|
||||
val compileTimeValue = property.compileTimeValue
|
||||
if (compileTimeValue != null) {
|
||||
renderAnnotationArgument(compileTimeValue, this)
|
||||
return this
|
||||
} else {
|
||||
return append("...")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,6 +207,7 @@ include ":kotlin-imports-dumper-compiler-plugin",
|
||||
":generators:tree-generator-common",
|
||||
":tools:kotlinp",
|
||||
":tools:kotlinp-jvm",
|
||||
":tools:kotlinp-klib",
|
||||
":kotlin-build-tools-enum-compat",
|
||||
":kotlin-gradle-compiler-types",
|
||||
":kotlin-gradle-plugin-api",
|
||||
@@ -768,6 +769,7 @@ project(':kotlin-assignment-compiler-plugin.embeddable').projectDir = "$rootDir/
|
||||
|
||||
project(':tools:kotlinp').projectDir = "$rootDir/libraries/tools/kotlinp" as File
|
||||
project(':tools:kotlinp-jvm').projectDir = "$rootDir/libraries/tools/kotlinp/jvm" as File
|
||||
project(':tools:kotlinp-klib').projectDir = "$rootDir/libraries/tools/kotlinp/klib" as File
|
||||
project(":kotlin-build-tools-enum-compat").projectDir = "$rootDir/libraries/tools/kotlin-build-tools-enum-compat" as File
|
||||
project(':kotlin-gradle-compiler-types').projectDir = "$rootDir/libraries/tools/kotlin-gradle-compiler-types" as File
|
||||
project(':kotlin-gradle-plugin-api').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-api" as File
|
||||
|
||||
Reference in New Issue
Block a user