Entry point selection.
$ kotlinc -entry foo.bar.qux ... selects `qux(args: Array<String>):Unit` in the package `foo.bar` as an entry point. The short flag is `-e`.
This commit is contained in:
committed by
alexander-gorshenev
parent
a2fa4938f3
commit
5192bd1313
@@ -122,6 +122,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
||||
put(CommonConfigurationKeys.MODULE_NAME, output)
|
||||
put(ABI_VERSION, 1)
|
||||
|
||||
arguments.mainPackage ?.let{ put(ENTRY, it) }
|
||||
arguments.runtimeFile ?.let{ put(RUNTIME_FILE, it) }
|
||||
arguments.propertyFile ?.let{ put(PROPERTY_FILE, it) }
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
|
||||
@field:Argument(value = "-output", shortName = "-o", valueDescription = "<path>", description = "Output file path")
|
||||
@JvmField var outputFile: String? = null
|
||||
|
||||
@field:Argument(value = "-entry", shortName = "-e", valueDescription = "<name>", description = "Qualified entry point name")
|
||||
@JvmField var mainPackage: String? = null
|
||||
|
||||
@field:Argument(value = "-produce", shortName = "-p", valueDescription = "{program|library|bitcode}", description = "Produce either .kexe, .klib or a .bc file.")
|
||||
@JvmField var produce: String? = null
|
||||
|
||||
|
||||
+2
@@ -40,6 +40,8 @@ class KonanConfigKeys {
|
||||
= CompilerConfigurationKey.create("list backend phases")
|
||||
val LIST_TARGETS: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("list available targets")
|
||||
val ENTRY: CompilerConfigurationKey<String?>
|
||||
= CompilerConfigurationKey.create("fully qualified main() name")
|
||||
val META_INFO: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("generate metadata")
|
||||
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.utils.alwaysTrue
|
||||
|
||||
internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
|
||||
|
||||
val config = context.config.configuration
|
||||
if (config.get(KonanConfigKeys.PRODUCE) != CompilerOutputKind.PROGRAM) return null
|
||||
|
||||
val entryPoint = FqName(config.get(KonanConfigKeys.ENTRY) ?: defaultEntryName)
|
||||
|
||||
val entryName = entryPoint.shortName()
|
||||
val packageName = entryPoint.parent()
|
||||
|
||||
val packageScope = context.builtIns.builtInsModule.getPackage(packageName).memberScope
|
||||
|
||||
val main = packageScope.getContributedFunctions(entryName,
|
||||
NoLookupLocation.FROM_BACKEND).singleOrNull {
|
||||
it.returnType?.isUnit() == true &&
|
||||
it.hasSingleArrayOfStringParameter &&
|
||||
it.typeParameters.isEmpty() &&
|
||||
it.isExported()
|
||||
}
|
||||
return main
|
||||
}
|
||||
|
||||
private val defaultEntryName = "main"
|
||||
|
||||
private val defaultEntryPackage = FqName.ROOT
|
||||
|
||||
private val KotlinType.filterClass: ClassDescriptor?
|
||||
get() {
|
||||
val constr = constructor.declarationDescriptor
|
||||
return constr as? ClassDescriptor
|
||||
}
|
||||
|
||||
private val ClassDescriptor.isString
|
||||
get() = fqNameSafe.asString() == "kotlin.String"
|
||||
|
||||
private val KotlinType.isString
|
||||
get() = filterClass?.isString ?: false
|
||||
|
||||
private val KotlinType.isArrayOfString: Boolean
|
||||
get() = (filterClass?.isArray ?: false) &&
|
||||
(arguments.singleOrNull()?.type?.isString ?: false)
|
||||
|
||||
private val FunctionDescriptor.hasSingleArrayOfStringParameter: Boolean
|
||||
get() = valueParameters.singleOrNull()?.type?.isArrayOfString ?: false
|
||||
|
||||
+39
@@ -334,6 +334,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
module.acceptChildrenVoid(this)
|
||||
appendLlvmUsed(context.llvm.usedFunctions)
|
||||
appendStaticInitializers(context.llvm.staticInitializers)
|
||||
appendEntryPointSelector(findMainEntryPoint(context))
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -2096,6 +2097,44 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun entryPointSelector(entryPoint: LLVMValueRef,
|
||||
entryPointType: LLVMTypeRef, selectorName: String): LLVMValueRef {
|
||||
|
||||
assert(LLVMCountParams(entryPoint) == 1)
|
||||
|
||||
val selector = LLVMAddFunction(context.llvmModule, selectorName, entryPointType)!!
|
||||
codegen.prologue(selector, voidType)
|
||||
|
||||
// Note, that 'parameter' is an object reference, and as such, shall
|
||||
// be accounted for in the rootset. However, current object management
|
||||
// scheme for arguments guarantees, that reference is being held in C++
|
||||
// launcher, so we could optimize out creating slot for 'parameter' in
|
||||
// this function.
|
||||
val parameter = LLVMGetParam(selector, 0)!!
|
||||
codegen.callAtFunctionScope(entryPoint, listOf(parameter), Lifetime.IRRELEVANT)
|
||||
|
||||
codegen.ret(null)
|
||||
codegen.epilogue()
|
||||
return selector
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun appendEntryPointSelector(descriptor: FunctionDescriptor?) {
|
||||
if (descriptor == null) return
|
||||
|
||||
val entryPoint = codegen.llvmFunction(descriptor)
|
||||
val selectorName = "EntryPointSelector"
|
||||
val entryPointType = getFunctionType(entryPoint)!!
|
||||
|
||||
val selector = entryPointSelector(entryPoint, entryPointType, selectorName)
|
||||
|
||||
LLVMSetLinkage(selector, LLVMLinkage.LLVMExternalLinkage)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
// Create type { i32, void ()*, i8* }
|
||||
|
||||
|
||||
@@ -371,6 +371,25 @@ task hello4(type: RunKonanTest) {
|
||||
source = "runtime/basic/hello4.kt"
|
||||
}
|
||||
|
||||
task entry0(type: RunKonanTest) {
|
||||
goldValue = "Hello.\n"
|
||||
source = "runtime/basic/entry0.kt"
|
||||
flags = ["-entry", "koko.lala.main"]
|
||||
}
|
||||
|
||||
task entry1(type: RunKonanTest) {
|
||||
goldValue = "Hello.\n"
|
||||
source = "runtime/basic/entry1.kt"
|
||||
flags = ["-entry", "foo"]
|
||||
}
|
||||
|
||||
task entry2(type: LinkKonanTest) {
|
||||
goldValue = "Hello.\n"
|
||||
source = "runtime/basic/entry2.kt"
|
||||
lib = "runtime/basic/libentry2.kt"
|
||||
flags = ["-entry", "foo"]
|
||||
}
|
||||
|
||||
task tostring0(type: RunKonanTest) {
|
||||
goldValue = "127\n-1\n239\nA\nЁ\nト\n1122334455\n112233445566778899\n3.14159265358\n1.0E27\n1.0E-300\ntrue\nfalse\n"
|
||||
source = "runtime/basic/tostring0.kt"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package koko.lala
|
||||
|
||||
fun fail() {
|
||||
println("Test failed, this is a wrong main() function.")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
fail()
|
||||
}
|
||||
|
||||
fun <T> main(args: Array<String>) {
|
||||
fail()
|
||||
}
|
||||
|
||||
fun main(args: Array<Int>) {
|
||||
fail()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>, second_arg: Int) {
|
||||
fail()
|
||||
}
|
||||
|
||||
class Foo {
|
||||
fun main(args: Array<String>) {
|
||||
fail()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("Hello.")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fun fail() {
|
||||
println("Test failed, this is a wrong main() function.")
|
||||
}
|
||||
|
||||
fun foo(args: Array<String>) {
|
||||
println("Hello.")
|
||||
}
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
fail()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
fun main(args: Array<String>) {
|
||||
fail()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fun fail() {
|
||||
println("Test failed, this is a wrong main() function.")
|
||||
}
|
||||
|
||||
fun foo(args: Array<String>) {
|
||||
println("Hello.")
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
|
||||
import konan.internal.ExportForCppRuntime
|
||||
|
||||
external fun main(args: Array<String>)
|
||||
// This function is produced by the code generator given
|
||||
// the '-entry foo.bar.main' flag.
|
||||
// It calls the requested entry point.
|
||||
// The default is main(Array<String>):Unit in the root package.
|
||||
@ExportForCppRuntime
|
||||
external fun EntryPointSelector(args: Array<String>)
|
||||
|
||||
@ExportForCppRuntime
|
||||
private fun Konan_start(args: Array<String>): Int {
|
||||
try {
|
||||
|
||||
// This is kotlin program main entry point
|
||||
main(args)
|
||||
EntryPointSelector(args)
|
||||
|
||||
// Successfully finished:
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user