KT-63748: Pack Swift Export Frontend into compiler plugin
Co-authored-by: Sergej Jaskiewicz <jaskiewiczs@icloud.com> Merge-request: KT-MR-13351 Merged-by: Artem Olkov <artem.olkov@jetbrains.com>
This commit is contained in:
@@ -379,6 +379,7 @@
|
||||
/plugins/power-assert/ "Kotlin Compiler Core"
|
||||
/plugins/sam-with-receiver/ "Kotlin Compiler Core"
|
||||
/plugins/scripting/ "Kotlin Compiler Core"
|
||||
/plugins/swift-export/ "Kotlin Native"
|
||||
|
||||
/prepare/ "Kotlin Build Infrastructure"
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ sourceSets {
|
||||
dependencies {
|
||||
implementation(projectTests(":native:swift:sir-analysis-api"))
|
||||
implementation(projectTests(":native:swift:sir-compiler-bridge"))
|
||||
implementation(projectTests(":kotlin-swift-export-compiler-plugin"))
|
||||
implementation(projectTests(":generators:test-generator"))
|
||||
runtimeOnly(projectTests(":analysis:analysis-test-framework"))
|
||||
runtimeOnly(libs.junit.jupiter.api)
|
||||
|
||||
+12
-1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.generators.tests.native.swift.sir
|
||||
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
|
||||
import org.jetbrains.kotlin.sir.analysisapi.AbstractKotlinSirContextTest
|
||||
import org.jetbrains.kotlin.sir.bridge.AbstractKotlinSirBridgeTest
|
||||
import org.jetbrains.kotlin.swiftexport.AbstractSwiftExportContextTest
|
||||
|
||||
|
||||
fun main() {
|
||||
@@ -20,7 +21,7 @@ fun main() {
|
||||
testClass<AbstractKotlinSirContextTest>(
|
||||
suiteTestClassName = "SirAnalysisGeneratedTests"
|
||||
) {
|
||||
model("", pattern = "^([^_](.+)).kt$", recursive = false)
|
||||
model("", recursive = false)
|
||||
}
|
||||
}
|
||||
testGroup(
|
||||
@@ -33,5 +34,15 @@ fun main() {
|
||||
model("", extension = null, recursive = false)
|
||||
}
|
||||
}
|
||||
testGroup(
|
||||
"plugins/swift-export/tests-gen",
|
||||
"plugins/swift-export/testData"
|
||||
) {
|
||||
testClass<AbstractSwiftExportContextTest>(
|
||||
suiteTestClassName = "SwiftExportCompilerPluginTest"
|
||||
) {
|
||||
model("", excludedPattern = ".*\\.golden\\.kt$", recursive = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import java.util.regex.Pattern;
|
||||
public class SirAnalysisGeneratedTests extends AbstractKotlinSirContextTest {
|
||||
@Test
|
||||
public void testAllFilesPresentInTestData() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/swift/sir-analysis-api/testData"), Pattern.compile("^([^_](.+)).kt$"), null, false);
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/swift/sir-analysis-api/testData"), Pattern.compile("^(.+)\\.kt$"), null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.sir.visitors.SirTransformer
|
||||
* Pass that for every occurring declaration in package x.y.z generates a mirroring type scope and puts it there.
|
||||
* Right now, enums without cases are used for namespace simulation.
|
||||
*/
|
||||
public class SirInflatePackagesPass : SirPass<SirModule, Unit, SirModule> {
|
||||
public class SirInflatePackagesPass : SirModulePass {
|
||||
private data class Namespace(
|
||||
val elements: MutableList<SirDeclaration> = mutableListOf(),
|
||||
val children: MutableMap<String, Namespace> = mutableMapOf(),
|
||||
@@ -79,7 +79,7 @@ public class SirInflatePackagesPass : SirPass<SirModule, Unit, SirModule> {
|
||||
}.also(SirDeclarationContainer::fixParents)
|
||||
}
|
||||
|
||||
public override fun run(element: SirModule, data: Unit): SirModule = element.transform(Transformer, Context())
|
||||
public override fun run(element: SirModule, data: Nothing?): SirModule = element.transform(Transformer, Context())
|
||||
}
|
||||
|
||||
private fun SirDeclarationContainer.fixParents() = declarations
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
package org.jetbrains.sir.passes
|
||||
|
||||
import org.jetbrains.kotlin.sir.SirElement
|
||||
import org.jetbrains.kotlin.sir.SirModule
|
||||
|
||||
public typealias SirModulePass = SirPass<SirModule, Nothing?, SirModule>
|
||||
|
||||
/**
|
||||
* Swift IR is supposed to be transformed by a series of passes.
|
||||
@@ -23,4 +26,4 @@ public interface SirPass<in E : SirElement, in T, out R> {
|
||||
public fun run(element: E, data: T): R
|
||||
}
|
||||
|
||||
public fun <E : SirElement, R> SirPass<E, Unit, R>.run(element: E): R = this.run(element, Unit)
|
||||
public fun <E : SirElement, R> SirPass<E, Nothing?, R>.run(element: E): R = this.run(element, null)
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ import java.lang.IllegalStateException
|
||||
* or `element` does not contain origin of type `SirOrigin.KotlinEntity.Function`,
|
||||
* returns original element.
|
||||
*/
|
||||
public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothing?, SirElement> {
|
||||
public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothing?, SirDeclaration> {
|
||||
|
||||
private class Transformer : SirTransformerVoid() {
|
||||
override fun <E : SirElement> transformElement(element: E): E {
|
||||
@@ -45,7 +45,7 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
|
||||
}
|
||||
}
|
||||
|
||||
override fun run(element: SirElement, data: Nothing?): SirElement = element.transform(Transformer())
|
||||
override fun run(element: SirElement, data: Nothing?): SirDeclaration = element.transform(Transformer())
|
||||
}
|
||||
|
||||
private fun SirKotlinOrigin.Parameter.toSir(): SirParameter = SirParameter(
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.sir.mock.MockParameter
|
||||
import org.jetbrains.kotlin.sir.passes.asserts.assertSirFunctionsEquals
|
||||
import org.jetbrains.kotlin.sir.passes.mocks.MockSirFunction
|
||||
import org.jetbrains.kotlin.sir.util.SirSwiftModule
|
||||
import org.jetbrains.sir.passes.run
|
||||
import org.jetbrains.sir.passes.translation.ForeignIntoSwiftFunctionTranslationPass
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNotNull
|
||||
@@ -39,7 +40,7 @@ class SirPassTests {
|
||||
mySirElement.parent = module
|
||||
|
||||
val myPass = ForeignIntoSwiftFunctionTranslationPass()
|
||||
val result = myPass.run(mySirElement, null) as? SirFunction
|
||||
val result = myPass.run(mySirElement) as? SirFunction
|
||||
assertNotNull(result, "SirFunction should be produced")
|
||||
val exp = MockSirFunction(
|
||||
name = "foo",
|
||||
@@ -95,7 +96,7 @@ class SirPassTests {
|
||||
mySirElement.parent = module
|
||||
|
||||
val myPass = ForeignIntoSwiftFunctionTranslationPass()
|
||||
val result = myPass.run(mySirElement, null) as? SirFunction
|
||||
val result = myPass.run(mySirElement) as? SirFunction
|
||||
assertNotNull(result, "SirFunction should be produced")
|
||||
val exp = MockSirFunction(
|
||||
name = "foo",
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.sir.printer
|
||||
import org.jetbrains.kotlin.sir.*
|
||||
import org.jetbrains.kotlin.sir.visitors.SirVisitorVoid
|
||||
import org.jetbrains.kotlin.utils.SmartPrinter
|
||||
import org.jetbrains.kotlin.utils.withIndent
|
||||
|
||||
private const val DEFAULT_INDENT: String = " "
|
||||
|
||||
@@ -46,6 +47,14 @@ public class SirAsSwiftSourcesPrinter(private val printer: SmartPrinter) : SirVi
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitEnum(enum: SirEnum): Unit = with(printer) {
|
||||
println("enum ${enum.name.swiftIdentifier} {")
|
||||
withIndent {
|
||||
enum.acceptChildren(SirAsSwiftSourcesPrinter(printer))
|
||||
}
|
||||
println("}")
|
||||
}
|
||||
|
||||
override fun visitForeignFunction(function: SirForeignFunction) {} // we do not write foreign nodes
|
||||
|
||||
override fun visitElement(element: SirElement): Unit = with(printer) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Swift Export Compiler Plugin
|
||||
|
||||
## Description
|
||||
|
||||
This module represents an entry point for the SwiftExport-Frontend functionality. It is implemented as a compiler plugin, without a direct CLI interface.
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### How to build
|
||||
|
||||
```bash
|
||||
./gradlew kotlin-swift-export-compiler-plugin:build
|
||||
```
|
||||
|
||||
This will produce the resulting jar (with all dependency embedded) at `%PATH_TO_KOTLIN_REPO%/plugins/swift-export/build/libs/kotlin-swift-export-compiler-plugin-%VERSION%.jar`
|
||||
|
||||
### Example usage
|
||||
|
||||
Given that:
|
||||
1. there is a `kotlinc` installed at `$PATH`
|
||||
2. sources are located in the `app.kt` file
|
||||
3. the `$PLUGIN_PATH` variable contains the path to the jar received from the ["How to build"](#How-to-build) section
|
||||
|
||||
```bash
|
||||
kotlinc -language-version 2.0 app.kt -Xplugin=$PLUGIN_PATH -P plugin:org.jetbrains.kotlin.swiftexport:output_dir="~/my_awesome_directory/"
|
||||
```
|
||||
|
||||
This will produce the following file tree:
|
||||
```
|
||||
~/my_awesome_directory/
|
||||
|_result.swift
|
||||
|_result.kt
|
||||
|_result.h
|
||||
```
|
||||
|
||||
`result.kt` - contains the kotlin bridge for the resulting module. It should be compiled against the `.klib` produced by kotlinc-native in order to receive the final binary.
|
||||
`result.h` - contains C-header for final binary. The `result.swift` uses this header to talk to this compiled binary.
|
||||
|
||||
`result.swift` - contains the resulting Swift API for the compiled Kotlin/Native library.
|
||||
|
||||
### Supported Options
|
||||
|
||||
#### Output Directory
|
||||
|
||||
`output_dir` — Required parameter. Determines where the resulting artifacts will be placed.
|
||||
|
||||
#### Named
|
||||
|
||||
`named` — Optional parameter. Determines the names of the resulting files. By default — `result`.
|
||||
|
||||
## Dev guide
|
||||
|
||||
### How to generate tests:
|
||||
```bash
|
||||
./gradlew :generators:sir-tests-generator:generateTests
|
||||
```
|
||||
this will generate tests from the input files. The input files can be found and should be placed here: `plugins/swift-export/testData`
|
||||
|
||||
The test expects to find the `.golden.swift`, `.golden.kt` and `.golden.h` files that contain the resulting bridges. The name of the `.golden.*` file should be the same as the name of the corresponding `.kt` file.
|
||||
|
||||
The project for the generator can be found here — `generators/sir-tests-generator/build.gradle.kts`
|
||||
|
||||
### How to run the tests:
|
||||
```bash
|
||||
./gradlew :kotlin-swift-export-compiler-plugin:test
|
||||
```
|
||||
OR just open `SwiftExportCompilerPluginTest` in IDEA and start them from gutter.
|
||||
@@ -0,0 +1,59 @@
|
||||
description = "Swift Export Compiler Plugin"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
embedded(project(":kotlin-swift-export-compiler-plugin.backend")) { isTransitive = false }
|
||||
embedded(project(":kotlin-swift-export-compiler-plugin.cli")) { isTransitive = false }
|
||||
|
||||
embedded(project(":native:swift:sir")) { isTransitive = false }
|
||||
embedded(project(":native:swift:sir-analysis-api")) { isTransitive = false }
|
||||
embedded(project(":native:swift:sir-compiler-bridge")) { isTransitive = false }
|
||||
embedded(project(":native:swift:sir-passes")) { isTransitive = false }
|
||||
embedded(project(":native:swift:sir-printer")) { isTransitive = false }
|
||||
|
||||
testApi(project(":kotlin-swift-export-compiler-plugin.cli"))
|
||||
|
||||
testApi(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter.api)
|
||||
testRuntimeOnly(libs.junit.jupiter.engine)
|
||||
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(projectTests(":compiler:tests-common-new"))
|
||||
|
||||
|
||||
testApi(intellijCore())
|
||||
}
|
||||
|
||||
optInToExperimentalCompilerApi()
|
||||
|
||||
sourceSets {
|
||||
"main" { none() }
|
||||
"test" {
|
||||
projectDefault()
|
||||
generatedTestDir()
|
||||
}
|
||||
}
|
||||
|
||||
optInToExperimentalCompilerApi()
|
||||
|
||||
if (project.hasProperty("kotlin-native.swift-export.enabled")) {
|
||||
// todo: is you are removing this check - don't forget to run tests in repo/artifacts-tests/src/test/kotlin/org/jetbrains/kotlin/code/ArtifactsTest.kt
|
||||
publish()
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
testsJar()
|
||||
|
||||
val testDataDir = projectDir.resolve("testData")
|
||||
|
||||
projectTest(parallel = true, jUnitMode = JUnitMode.JUnit5) {
|
||||
workingDir = rootDir
|
||||
dependsOn(":dist")
|
||||
inputs.dir(testDataDir)
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
description = "Swift Compiler Plugin (Backend)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:backend"))
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(intellijCore())
|
||||
|
||||
implementation(project(":native:swift:sir"))
|
||||
implementation(project(":native:swift:sir-analysis-api"))
|
||||
implementation(project(":native:swift:sir-compiler-bridge"))
|
||||
implementation(project(":native:swift:sir-passes"))
|
||||
implementation(project(":native:swift:sir-printer"))
|
||||
|
||||
implementation(project(":analysis:analysis-api"))
|
||||
implementation(project(":analysis:analysis-api-standalone"))
|
||||
|
||||
compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
|
||||
implementation(kotlinStdlib())
|
||||
}
|
||||
|
||||
optInToUnsafeDuringIrConstructionAPI()
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider
|
||||
import org.jetbrains.kotlin.analysis.api.standalone.KtAlwaysAccessibleLifetimeTokenProvider
|
||||
import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.fir.extensions.FirAnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.sir.*
|
||||
import org.jetbrains.kotlin.sir.analysisapi.SirGenerator
|
||||
import org.jetbrains.kotlin.sir.bridge.BridgeRequest
|
||||
import org.jetbrains.kotlin.sir.bridge.createBridgeGenerator
|
||||
import org.jetbrains.kotlin.sir.bridge.createCBridgePrinter
|
||||
import org.jetbrains.kotlin.sir.bridge.createKotlinBridgePrinter
|
||||
import org.jetbrains.kotlin.sir.builder.buildModule
|
||||
import org.jetbrains.kotlin.sir.visitors.SirVisitor
|
||||
import org.jetbrains.sir.passes.SirInflatePackagesPass
|
||||
import org.jetbrains.sir.passes.SirModulePass
|
||||
import org.jetbrains.sir.passes.SirPass
|
||||
import org.jetbrains.sir.passes.run
|
||||
import org.jetbrains.sir.passes.translation.ForeignIntoSwiftFunctionTranslationPass
|
||||
import org.jetbrains.sir.printer.SirAsSwiftSourcesPrinter
|
||||
import java.io.File
|
||||
|
||||
class SwiftExportExtension(
|
||||
private val destination: File,
|
||||
private val outputFileName: String,
|
||||
) : FirAnalysisHandlerExtension() {
|
||||
override fun isApplicable(configuration: CompilerConfiguration): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun doAnalysis(configuration: CompilerConfiguration): Boolean {
|
||||
buildSwiftModule(configuration)
|
||||
.transformToSwift()
|
||||
.dumpResultToFiles()
|
||||
return true
|
||||
}
|
||||
|
||||
@OptIn(KtAnalysisApiInternals::class)
|
||||
private fun buildSwiftModule(configuration: CompilerConfiguration): SirModule {
|
||||
val standaloneAnalysisAPISession =
|
||||
buildStandaloneAnalysisAPISession(classLoader = SwiftExportExtension::class.java.classLoader) {
|
||||
@Suppress("DEPRECATION") // TODO: KT-61319 Kapt: remove usages of deprecated buildKtModuleProviderByCompilerConfiguration
|
||||
buildKtModuleProviderByCompilerConfiguration(configuration)
|
||||
|
||||
registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider())
|
||||
}
|
||||
|
||||
val (sourceModule, rawFiles) = standaloneAnalysisAPISession.modulesWithFiles.entries.single()
|
||||
|
||||
val ktFiles = rawFiles.filterIsInstance<KtFile>()
|
||||
|
||||
return buildModule {
|
||||
name = sourceModule.moduleName
|
||||
val sirFactory = SirGenerator()
|
||||
ktFiles.forEach { file ->
|
||||
declarations += sirFactory.build(file)
|
||||
}
|
||||
}.apply {
|
||||
declarations.forEach { it.parent = this }
|
||||
}
|
||||
}
|
||||
|
||||
private fun SirModule.transformToSwift(): SirModule {
|
||||
return SirPassesConfiguration.passes.fold(this) { module, pass ->
|
||||
pass.run(module)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SirModule.dumpResultToFiles() {
|
||||
val destinationPath = destination.absolutePath
|
||||
|
||||
val cHeaderFile = File("${destinationPath}/${outputFileName}.h")
|
||||
val ktBridgeFile = File("${destinationPath}/${outputFileName}.kt")
|
||||
val swiftFile = File("${destinationPath}/${outputFileName}.swift")
|
||||
|
||||
val bridges = generateBridgeSources()
|
||||
val swiftSrc = generateSwiftSrc()
|
||||
|
||||
dumpTextAtFile(bridges.ktSrc, ktBridgeFile)
|
||||
dumpTextAtFile(bridges.cSrc, cHeaderFile)
|
||||
dumpTextAtFile(sequenceOf(swiftSrc), swiftFile)
|
||||
}
|
||||
|
||||
private fun SirModule.generateSwiftSrc(): String {
|
||||
return SirAsSwiftSourcesPrinter().print(this)
|
||||
}
|
||||
|
||||
private fun SirModule.generateBridgeSources(): BridgeSources {
|
||||
val requests = BridgeRequestsBuilder.build(this)
|
||||
|
||||
val generator = createBridgeGenerator()
|
||||
val kotlinBridgePrinter = createKotlinBridgePrinter()
|
||||
val cBridgePrinter = createCBridgePrinter()
|
||||
|
||||
requests.forEach { request ->
|
||||
val bridge = generator.generate(request)
|
||||
kotlinBridgePrinter.add(bridge)
|
||||
cBridgePrinter.add(bridge)
|
||||
}
|
||||
|
||||
val actualKotlinSrc = kotlinBridgePrinter.print()
|
||||
val actualCHeader = cBridgePrinter.print()
|
||||
|
||||
return BridgeSources(ktSrc = actualKotlinSrc, cSrc = actualCHeader)
|
||||
}
|
||||
|
||||
private fun dumpTextAtFile(text: Sequence<String>, file: File) {
|
||||
if (!file.exists()) {
|
||||
file.parentFile.mkdirs()
|
||||
file.createNewFile()
|
||||
}
|
||||
val writer = file.printWriter()
|
||||
for (t in text) {
|
||||
writer.println(t)
|
||||
}
|
||||
writer.close()
|
||||
}
|
||||
|
||||
private data class BridgeSources(val ktSrc: Sequence<String>, val cSrc: Sequence<String>)
|
||||
|
||||
private object SirPassesConfiguration {
|
||||
val passes: List<SirModulePass> = listOf(
|
||||
SirInflatePackagesPass(),
|
||||
WholeModuleTranslationByElementPass(ForeignIntoSwiftFunctionTranslationPass()),
|
||||
)
|
||||
|
||||
class WholeModuleTranslationByElementPass(
|
||||
val pass: SirPass<SirElement, Nothing?, SirDeclaration>
|
||||
) : SirModulePass {
|
||||
override fun run(element: SirModule, data: Nothing?): SirModule {
|
||||
return buildModule {
|
||||
name = element.name
|
||||
element.declarations.forEach {
|
||||
val newDecl = pass.run(it)
|
||||
declarations.add(newDecl)
|
||||
}
|
||||
}.apply {
|
||||
declarations.forEach { it.parent = this }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object BridgeRequestsBuilder : SirVisitor<Unit, MutableList<BridgeRequest>>() {
|
||||
fun build(from: SirModule): List<BridgeRequest> {
|
||||
val result = mutableListOf<BridgeRequest>()
|
||||
from.accept(this, result)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun visitFunction(function: SirFunction, data: MutableList<BridgeRequest>) {
|
||||
val fqName = (function.origin as? SirKotlinOrigin.Function)?.fqName ?: return
|
||||
|
||||
data.add(
|
||||
BridgeRequest(
|
||||
function,
|
||||
fqName.joinToString("_"),
|
||||
fqName
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitElement(element: SirElement, data: MutableList<BridgeRequest>) {
|
||||
element.acceptChildren(this, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
object SwiftExportPluginNames {
|
||||
const val PLUGIN_ID = "org.jetbrains.kotlin.swiftexport"
|
||||
const val OUTPUT_DIR_KEY = "output_dir"
|
||||
const val RESULT_NAME_KEY = "named"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
description = "Swift Compiler Plugin (CLI)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":kotlin-swift-export-compiler-plugin.backend"))
|
||||
|
||||
compileOnly(project(":compiler:plugin-api"))
|
||||
compileOnly(project(":compiler:fir:entrypoint"))
|
||||
|
||||
embedded(project(":analysis:analysis-api-standalone")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-api")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-api-fir")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-api-impl-base")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-api-impl-barebone")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-api-standalone:analysis-api-standalone-base")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-api-standalone:analysis-api-fir-standalone-base")) { isTransitive = false }
|
||||
embedded(project(":analysis:analysis-internal-utils")) { isTransitive = false }
|
||||
embedded(project(":analysis:low-level-api-fir")) { isTransitive = false }
|
||||
embedded(project(":analysis:symbol-light-classes")) { isTransitive = false }
|
||||
}
|
||||
|
||||
optInToExperimentalCompilerApi()
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.swiftexport.SwiftExportCommandLineProcessor
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.swiftexport.SwiftExportComponentRegistrar
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
import org.jetbrains.kotlin.compiler.plugin.*
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.fir.extensions.FirAnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.swiftexport.SwiftExportPluginNames.OUTPUT_DIR_KEY
|
||||
import org.jetbrains.kotlin.swiftexport.SwiftExportPluginNames.PLUGIN_ID
|
||||
import org.jetbrains.kotlin.swiftexport.SwiftExportPluginNames.RESULT_NAME_KEY
|
||||
import java.io.File
|
||||
|
||||
object SwiftExportConfigurationKeys {
|
||||
val OUTPUT_DIR: CompilerConfigurationKey<String> = CompilerConfigurationKey.create(
|
||||
"Destination directory for swift-export resulted files"
|
||||
)
|
||||
val RESULT_NAME: CompilerConfigurationKey<String> = CompilerConfigurationKey.create(
|
||||
"Filenames for resulted files. There will be 3 files - %RESULT_NAME%.swift, %RESULT_NAME%.h and %RESULT_NAME%.kt."
|
||||
)
|
||||
}
|
||||
|
||||
class SwiftExportCommandLineProcessor : CommandLineProcessor {
|
||||
companion object {
|
||||
val OUTPUT_DIR = CliOption(
|
||||
OUTPUT_DIR_KEY, "output destination",
|
||||
"Destination directory for swift-export resulted files",
|
||||
required = true, allowMultipleOccurrences = false
|
||||
)
|
||||
val RESULT_NAME = CliOption(
|
||||
RESULT_NAME_KEY, "Filenames for resulted files",
|
||||
"Filenames for resulted files. By default - \"result\"",
|
||||
required = false, allowMultipleOccurrences = false
|
||||
)
|
||||
}
|
||||
|
||||
override val pluginId = PLUGIN_ID
|
||||
override val pluginOptions = listOf(
|
||||
OUTPUT_DIR, RESULT_NAME
|
||||
)
|
||||
|
||||
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) = when (option) {
|
||||
OUTPUT_DIR -> configuration.put(SwiftExportConfigurationKeys.OUTPUT_DIR, value)
|
||||
RESULT_NAME -> configuration.put(SwiftExportConfigurationKeys.RESULT_NAME, value)
|
||||
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
|
||||
}
|
||||
}
|
||||
|
||||
class SwiftExportComponentRegistrar : CompilerPluginRegistrar() {
|
||||
override val supportsK2: Boolean
|
||||
get() = true
|
||||
|
||||
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
|
||||
val dir = File(
|
||||
configuration.get(SwiftExportConfigurationKeys.OUTPUT_DIR)
|
||||
?: throw IllegalArgumentException("output_dir is a required argument for org.jetbrains.kotlin.swiftexport")
|
||||
)
|
||||
|
||||
val named = configuration.get(SwiftExportConfigurationKeys.RESULT_NAME)
|
||||
?: "result"
|
||||
FirAnalysisHandlerExtension.registerExtension(
|
||||
SwiftExportExtension(dir, named)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
embedded(project(":kotlin-swift-export-compiler-plugin")) { isTransitive = false }
|
||||
}
|
||||
|
||||
if (project.hasProperty("kotlin-native.swift-export.enabled")) {
|
||||
publish {
|
||||
artifactId = "kotlin-swift-export-compiler-plugin-embeddable"
|
||||
}
|
||||
}
|
||||
|
||||
runtimeJar(rewriteDefaultJarDepsToShadedCompiler())
|
||||
sourcesJarWithSourcesFromEmbedded(
|
||||
project(":kotlin-swift-export-compiler-plugin").tasks.named<Jar>("sourcesJar")
|
||||
)
|
||||
javadocJarWithJavadocFromEmbedded(
|
||||
project(":kotlin-swift-export-compiler-plugin").tasks.named<Jar>("javadocJar")
|
||||
)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.common.CLITool
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.CompilerTestUtil
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
import org.jetbrains.kotlin.test.services.getKtFilesForSourceFiles
|
||||
import org.jetbrains.kotlin.test.services.sourceFileProvider
|
||||
import java.io.File
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class JvmCompilerWithSwiftExportPluginFacade(
|
||||
private val testServices: TestServices,
|
||||
) : AbstractTestFacade<ResultingArtifact.Source, SwiftExportArtifact>() {
|
||||
override val inputKind: TestArtifactKind<ResultingArtifact.Source>
|
||||
get() = SourcesKind
|
||||
override val outputKind: TestArtifactKind<SwiftExportArtifact>
|
||||
get() = SwiftExportArtifact.Kind
|
||||
|
||||
private val tmpDir = FileUtil.createTempDirectory("SwiftExportIntegrationTests", null, false)
|
||||
|
||||
override fun transform(module: TestModule, inputArtifact: ResultingArtifact.Source): SwiftExportArtifact {
|
||||
val configurationProvider = testServices.compilerConfigurationProvider
|
||||
val project = configurationProvider.getProject(module)
|
||||
val ktFiles = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project, findViaVfs = true).values.toList()
|
||||
|
||||
val outputDirPath = tmpDir.absolutePath + "/" + "swift_export_output"
|
||||
val outputDir = File(outputDirPath)
|
||||
outputDir.mkdirs()
|
||||
|
||||
runTest(
|
||||
K2JVMCompiler(),
|
||||
ktFiles,
|
||||
outputDir
|
||||
)
|
||||
|
||||
return SwiftExportArtifact(
|
||||
File(outputDir.absolutePath + "/result.swift"),
|
||||
File(outputDir.absolutePath + "/result.h"),
|
||||
File(outputDir.absolutePath + "/result.kt"),
|
||||
)
|
||||
}
|
||||
|
||||
private fun runTest(
|
||||
compiler: CLITool<*>,
|
||||
src: List<KtFile>,
|
||||
outputDir: File,
|
||||
) {
|
||||
val sources = src.map {
|
||||
tmpDir.resolve(it.name).apply {
|
||||
writeText(it.text)
|
||||
}
|
||||
}
|
||||
|
||||
val plugin = writePlugin(
|
||||
SwiftExportCommandLineProcessor::class,
|
||||
SwiftExportComponentRegistrar::class,
|
||||
)
|
||||
val args = listOf(
|
||||
"-Xplugin=$plugin",
|
||||
"-P", "plugin:org.jetbrains.kotlin.swiftexport:output_dir=${outputDir}"
|
||||
) + sources.map { it.absolutePath }
|
||||
|
||||
val outputPath = listOf(
|
||||
"-language-version", "2.0",
|
||||
"-d", tmpDir.resolve("out").absolutePath
|
||||
)
|
||||
|
||||
val (output, exitCode) = CompilerTestUtil.executeCompiler(
|
||||
compiler,
|
||||
args + outputPath
|
||||
)
|
||||
assertEquals(ExitCode.OK, exitCode, output)
|
||||
}
|
||||
|
||||
private fun writePlugin(
|
||||
cliProcessor: KClass<out CommandLineProcessor>,
|
||||
registrarKClass: KClass<out CompilerPluginRegistrar>,
|
||||
): String {
|
||||
val jarFile = tmpDir.resolve("plugin.jar")
|
||||
ZipOutputStream(jarFile.outputStream()).use {
|
||||
val entryRegistry = ZipEntry("META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar")
|
||||
it.putNextEntry(entryRegistry)
|
||||
it.write(registrarKClass.java.name.toByteArray())
|
||||
|
||||
val entryCLI = ZipEntry("META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor")
|
||||
it.putNextEntry(entryCLI)
|
||||
it.write(cliProcessor.java.name.toByteArray())
|
||||
}
|
||||
return jarFile.absolutePath
|
||||
}
|
||||
|
||||
override fun shouldRunAnalysis(module: TestModule): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.runners.*
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
|
||||
open class AbstractSwiftExportContextTest : AbstractSwiftExportContextTestBase(TargetBackend.JVM) {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
builder.apply {
|
||||
globalDefaults {
|
||||
targetBackend = TargetBackend.JVM
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractSwiftExportContextTestBase(
|
||||
targetBackend: TargetBackend
|
||||
) : AbstractKotlinCompilerWithTargetBackendTest(targetBackend) {
|
||||
|
||||
override fun TestConfigurationBuilder.configuration() {
|
||||
globalDefaults {
|
||||
frontend = FrontendKinds.FIR
|
||||
targetPlatform = JvmPlatforms.defaultJvmPlatform
|
||||
dependencyKind = DependencyKind.Binary
|
||||
}
|
||||
|
||||
useConfigurators(
|
||||
::CommonEnvironmentConfigurator,
|
||||
::JvmEnvironmentConfigurator,
|
||||
)
|
||||
|
||||
facadeStep(::JvmCompilerWithSwiftExportPluginFacade)
|
||||
|
||||
handlersStep(SwiftExportArtifact.Kind) {
|
||||
useHandlers(::SirExportHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
import org.jetbrains.kotlin.test.model.AnalysisHandler
|
||||
import org.jetbrains.kotlin.test.model.TestArtifactKind
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.assertions
|
||||
import java.io.File
|
||||
|
||||
internal class SirExportHandler(testServices: TestServices) : AnalysisHandler<SwiftExportArtifact>(
|
||||
testServices,
|
||||
failureDisablesNextSteps = true,
|
||||
doNotRunIfThereWerePreviousFailures = true
|
||||
) {
|
||||
override val artifactKind: TestArtifactKind<SwiftExportArtifact>
|
||||
get() = SwiftExportArtifact.Kind
|
||||
|
||||
override fun processModule(module: TestModule, info: SwiftExportArtifact) {
|
||||
val originalFile = module.files.first().originalFile
|
||||
val originalFileName = originalFile.absolutePath.removeSuffix(".kt")
|
||||
|
||||
val expectedSwift = File("${originalFileName}.golden.swift")
|
||||
val expectedCHeader = File("${originalFileName}.golden.h")
|
||||
val expectedKotlinBridge = File("${originalFileName}.golden.kt")
|
||||
|
||||
testServices.assertions.assertEqualsToFile(expectedSwift, info.swift.readText())
|
||||
testServices.assertions.assertEqualsToFile(expectedCHeader, info.cHeader.readText())
|
||||
testServices.assertions.assertEqualsToFile(expectedKotlinBridge, info.ktBridge.readText())
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport
|
||||
|
||||
import org.jetbrains.kotlin.test.model.BinaryKind
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
import java.io.File
|
||||
|
||||
internal data class SwiftExportArtifact(
|
||||
val swift: File,
|
||||
val cHeader: File,
|
||||
val ktBridge: File,
|
||||
) : ResultingArtifact.Binary<SwiftExportArtifact>() {
|
||||
object Kind : BinaryKind<SwiftExportArtifact>("SwiftExportArtifact")
|
||||
|
||||
override val kind: BinaryKind<SwiftExportArtifact>
|
||||
get() = Kind
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t namespace1_main_foobar();
|
||||
|
||||
int32_t namespace1_foo();
|
||||
|
||||
int32_t namespace2_bar();
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import kotlin.native.internal.ExportForCppRuntime
|
||||
|
||||
@ExportForCppRuntime("namespace1_main_foobar")
|
||||
public fun namespace1_main_foobar(): Int {
|
||||
val result = namespace1.main.foobar()
|
||||
return result
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("namespace1_foo")
|
||||
public fun namespace1_foo(): Int {
|
||||
val result = namespace1.foo()
|
||||
return result
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("namespace2_bar")
|
||||
public fun namespace2_bar(): Int {
|
||||
val result = namespace2.bar()
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
enum namespace1 {
|
||||
enum main {
|
||||
public func foobar() -> Swift.Int32 { fatalError() }
|
||||
}
|
||||
public func foo() -> Swift.Int32 { fatalError() }
|
||||
}
|
||||
|
||||
enum namespace2 {
|
||||
public func bar() -> Swift.Int32 { fatalError() }
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
// FILE: foo.kt
|
||||
package namespace1
|
||||
fun foo(): Int = 123
|
||||
|
||||
// FILE: bar.kt
|
||||
package namespace2
|
||||
fun bar(): Int = 321
|
||||
|
||||
// FILE: main.kt
|
||||
package namespace1.main
|
||||
|
||||
import namespace1
|
||||
import namespace2
|
||||
fun foobar(): Int = foo() + bar()
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.swiftexport;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.native.swift.sir.GenerateSirTestsKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("plugins/swift-export/testData")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class SwiftExportCompilerPluginTest extends AbstractSwiftExportContextTest {
|
||||
@Test
|
||||
public void testAllFilesPresentInTestData() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/swift-export/testData"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile(".*\\.golden\\.kt$"), TargetBackend.JVM, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("plugins/swift-export/testData/simple.kt");
|
||||
}
|
||||
}
|
||||
@@ -152,6 +152,11 @@ include ":kotlin-noarg-compiler-plugin",
|
||||
":kotlin-noarg-compiler-plugin.backend",
|
||||
":kotlin-noarg-compiler-plugin.cli"
|
||||
|
||||
include ":kotlin-swift-export-compiler-plugin",
|
||||
":kotlin-swift-export-compiler-plugin.embeddable",
|
||||
":kotlin-swift-export-compiler-plugin.backend",
|
||||
":kotlin-swift-export-compiler-plugin.cli"
|
||||
|
||||
include ":kotlin-sam-with-receiver-compiler-plugin",
|
||||
":kotlin-sam-with-receiver-compiler-plugin.embeddable",
|
||||
":kotlin-sam-with-receiver-compiler-plugin.common",
|
||||
@@ -731,6 +736,11 @@ project(':kotlin-noarg-compiler-plugin.k2').projectDir = "$rootDir/plugins/noarg
|
||||
project(':kotlin-noarg-compiler-plugin.backend').projectDir = "$rootDir/plugins/noarg/noarg.backend" as File
|
||||
project(':kotlin-noarg-compiler-plugin.cli').projectDir = "$rootDir/plugins/noarg/noarg.cli" as File
|
||||
|
||||
project(':kotlin-swift-export-compiler-plugin').projectDir = "$rootDir/plugins/swift-export" as File
|
||||
project(':kotlin-swift-export-compiler-plugin.embeddable').projectDir = "$rootDir/plugins/swift-export/swift-export.embeddable" as File
|
||||
project(':kotlin-swift-export-compiler-plugin.backend').projectDir = "$rootDir/plugins/swift-export/swift-export.backend" as File
|
||||
project(':kotlin-swift-export-compiler-plugin.cli').projectDir = "$rootDir/plugins/swift-export/swift-export.cli" as File
|
||||
|
||||
project(':kotlin-sam-with-receiver-compiler-plugin').projectDir = "$rootDir/plugins/sam-with-receiver" as File
|
||||
project(':kotlin-sam-with-receiver-compiler-plugin.embeddable').projectDir = "$rootDir/plugins/sam-with-receiver/sam-with-receiver.embeddable" as File
|
||||
project(':kotlin-sam-with-receiver-compiler-plugin.common').projectDir = "$rootDir/plugins/sam-with-receiver/sam-with-receiver.common" as File
|
||||
|
||||
Reference in New Issue
Block a user