[Native] Enable real Android Native executables (#4624)

* Add AndroidProgramType binary option, override entry point, fix linker flags

* Add Konan_main_standalone entry point to the Android runtime

* Add compiler warning

* Make standalone programs print to stdout

* Fix warning message and readability
This commit is contained in:
Mattia Iavarone
2021-10-26 06:33:17 +02:00
committed by GitHub
parent 6a59dc7fa5
commit c92e34ca9f
14 changed files with 148 additions and 17 deletions
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2021 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.backend.konan
import org.jetbrains.kotlin.konan.target.LinkerOutputKind
/**
* For historical reasons, Android Native executables can be of two types:
* - [Standalone] is a regular executable, in which the Konan entry point receives command line arguments as usual.
* - [NativeActivity] is a shared library in which the Konan entry point matches Android NDK's NativeActivity
* signature ( https://developer.android.com/ndk/reference/group/native-activity#anativeactivity_createfunc ).
*/
enum class AndroidProgramType(
val konanMainOverride: String?,
val linkerOutputKindOverride: LinkerOutputKind?,
val consolePrintsToLogcat: Boolean
) {
/** Regular executable. The runtime entry point name is not Konan_main for historical reasons. */
Standalone("Konan_main_standalone", null, false),
/** Native activity "executable" - a shared library with a specific entry point. */
NativeActivity(null, LinkerOutputKind.DYNAMIC_LIBRARY, true);
companion object {
val Default = NativeActivity
}
}
@@ -21,6 +21,8 @@ object BinaryOptions : BinaryOptionRegistry() {
val stripDebugInfoFromNativeLibs by booleanOption()
val sourceInfoType by option<SourceInfoType>()
val androidProgramType by option<AndroidProgramType>()
}
open class BinaryOption<T : Any>(
@@ -85,8 +85,9 @@ private fun insertAliasToEntryPoint(context: Context) {
if (context.config.produce != CompilerOutputKind.PROGRAM || nomain)
return
val module = context.llvmModule
val entryPoint = LLVMGetNamedFunction(module, "Konan_main")
?: error("Module doesn't contain `Konan_main`")
val entryPointName = context.config.entryPointName
val entryPoint = LLVMGetNamedFunction(module, entryPointName)
?: error("Module doesn't contain `$entryPointName`")
LLVMAddAlias(module, LLVMTypeOf(entryPoint)!!, entryPoint, "main")
}
@@ -275,6 +275,29 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
internal val propertyLazyInitialization: Boolean get() = configuration.get(KonanConfigKeys.PROPERTY_LAZY_INITIALIZATION)!!
internal val lazyIrForCaches: Boolean get() = configuration.get(KonanConfigKeys.LAZY_IR_FOR_CACHES)!!
internal val entryPointName: String by lazy {
if (target.family == Family.ANDROID) {
val androidProgramTypeOrNull = configuration.get(BinaryOptions.androidProgramType)
if (androidProgramTypeOrNull == null) {
configuration.report(CompilerMessageSeverity.WARNING, """
Android Native executables are currently built as shared libraries with NativeActivity support, but the default behavior is going to change in 1.7.0 to build regular executables instead.
To keep using NativeActivity support, add binaryOptions["androidProgramType"] = "nativeActivity" to your androidNative executable configuration block in Gradle script:
binaries {
executable {
binaryOptions["androidProgramType"] = "nativeActivity"
}
}
See https://youtrack.jetbrains.com/issue/KT-49406 for more details.
""".trimIndent())
}
val androidProgramType = androidProgramTypeOrNull ?: AndroidProgramType.Default
if (androidProgramType.konanMainOverride != null) {
return@lazy androidProgramType.konanMainOverride
}
}
"Konan_main"
}
}
fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String)
@@ -21,7 +21,16 @@ internal fun determineLinkerOutput(context: Context): LinkerOutputKind =
CompilerOutputKind.DYNAMIC -> LinkerOutputKind.DYNAMIC_LIBRARY
CompilerOutputKind.STATIC_CACHE,
CompilerOutputKind.STATIC -> LinkerOutputKind.STATIC_LIBRARY
CompilerOutputKind.PROGRAM -> LinkerOutputKind.EXECUTABLE
CompilerOutputKind.PROGRAM -> run {
if (context.config.target.family == Family.ANDROID) {
val configuration = context.config.configuration
val androidProgramType = configuration.get(BinaryOptions.androidProgramType) ?: AndroidProgramType.Default
if (androidProgramType.linkerOutputKindOverride != null) {
return@run androidProgramType.linkerOutputKindOverride
}
}
LinkerOutputKind.EXECUTABLE
}
else -> TODO("${context.config.produce} should not reach native linker stage")
}
@@ -2745,6 +2745,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionType(int32Type, false, int8TypePtr, int8TypePtr, int32Type))
overrideRuntimeGlobal("Kotlin_getSourceInfo_Function", constValue(getSourceInfoFunction!!))
}
if (context.config.target.family == Family.ANDROID && context.config.produce == CompilerOutputKind.PROGRAM) {
val configuration = context.config.configuration
val programType = configuration.get(BinaryOptions.androidProgramType) ?: AndroidProgramType.Default
overrideRuntimeGlobal("Kotlin_printToAndroidLogcat", Int32(if (programType.consolePrintsToLogcat) 1 else 0))
}
}
//-------------------------------------------------------------------------//