Support static library production. (#1515)

This commit is contained in:
Nikolay Igotti
2018-04-20 12:25:59 +01:00
committed by GitHub
parent 6abf95d86d
commit 569bb71bca
10 changed files with 296 additions and 202 deletions
@@ -796,7 +796,7 @@ internal class CAdapterGenerator(
output("#endif /* KONAN_${prefix.toUpperCase()}_H */") output("#endif /* KONAN_${prefix.toUpperCase()}_H */")
outputStreamWriter.close() outputStreamWriter.close()
println("Produced dynamic library API in ${prefix}_api.h") println("Produced library API in ${prefix}_api.h")
outputStreamWriter = context.config.tempFiles outputStreamWriter = context.config.tempFiles
.cAdapterCpp .cAdapterCpp
@@ -22,7 +22,8 @@ import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
val CompilerOutputKind.isNativeBinary: Boolean get() = when (this) { val CompilerOutputKind.isNativeBinary: Boolean get() = when (this) {
CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC, CompilerOutputKind.FRAMEWORK -> true CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC,
CompilerOutputKind.STATIC, CompilerOutputKind.FRAMEWORK -> true
CompilerOutputKind.LIBRARY, CompilerOutputKind.BITCODE -> false CompilerOutputKind.LIBRARY, CompilerOutputKind.BITCODE -> false
} }
@@ -34,6 +35,7 @@ internal fun produceOutput(context: Context) {
val produce = config.get(KonanConfigKeys.PRODUCE) val produce = config.get(KonanConfigKeys.PRODUCE)
when (produce) { when (produce) {
CompilerOutputKind.STATIC,
CompilerOutputKind.DYNAMIC, CompilerOutputKind.DYNAMIC,
CompilerOutputKind.FRAMEWORK, CompilerOutputKind.FRAMEWORK,
CompilerOutputKind.PROGRAM -> { CompilerOutputKind.PROGRAM -> {
@@ -41,7 +43,7 @@ internal fun produceOutput(context: Context) {
context.bitcodeFileName = output context.bitcodeFileName = output
val generatedBitcodeFiles = val generatedBitcodeFiles =
if (produce == CompilerOutputKind.DYNAMIC) { if (produce == CompilerOutputKind.DYNAMIC || produce == CompilerOutputKind.STATIC) {
produceCAdapterBitcode( produceCAdapterBitcode(
context.config.clang, context.config.clang,
tempFiles.cAdapterCppName, tempFiles.cAdapterCppName,
@@ -394,8 +394,9 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
lateinit var debugInfo: DebugInfo lateinit var debugInfo: DebugInfo
val isDynamicLibrary: Boolean by lazy { val isNativeLibrary: Boolean by lazy {
config.configuration.get(KonanConfigKeys.PRODUCE) == CompilerOutputKind.DYNAMIC val kind = config.configuration.get(KonanConfigKeys.PRODUCE)
kind == CompilerOutputKind.DYNAMIC || kind == CompilerOutputKind.STATIC
} }
} }
@@ -35,8 +35,12 @@ internal class LinkStage(val context: Context) {
private val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false private val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false
private val debug = config.get(KonanConfigKeys.DEBUG) ?: false private val debug = config.get(KonanConfigKeys.DEBUG) ?: false
private val dynamic = context.config.produce == CompilerOutputKind.DYNAMIC || private val linkerOutput = when (context.config.produce) {
context.config.produce == CompilerOutputKind.FRAMEWORK CompilerOutputKind.DYNAMIC, CompilerOutputKind.FRAMEWORK -> LinkerOutputKind.DYNAMIC_LIBRARY
CompilerOutputKind.STATIC -> LinkerOutputKind.STATIC_LIBRARY
CompilerOutputKind.PROGRAM -> LinkerOutputKind.EXECUTABLE
else -> TODO("${context.config.produce} should not reach native linker stage")
}
private val nomain = config.get(KonanConfigKeys.NOMAIN) ?: false private val nomain = config.get(KonanConfigKeys.NOMAIN) ?: false
private val emitted = context.bitcodeFileName private val emitted = context.bitcodeFileName
private val libraries = context.llvm.librariesToLink private val libraries = context.llvm.librariesToLink
@@ -46,9 +50,9 @@ internal class LinkStage(val context: Context) {
private fun runTool(command: List<String>) = runTool(*command.toTypedArray()) private fun runTool(command: List<String>) = runTool(*command.toTypedArray())
private fun runTool(vararg command: String) = private fun runTool(vararg command: String) =
Command(*command) Command(*command)
.logWith(context::log) .logWith(context::log)
.execute() .execute()
private fun llvmLto(files: List<BitcodeFile>): ObjectFile { private fun llvmLto(files: List<BitcodeFile>): ObjectFile {
val combined = temporary("combined", ".o") val combined = temporary("combined", ".o")
@@ -58,8 +62,8 @@ internal class LinkStage(val context: Context) {
command.addNonEmpty(platform.llvmLtoFlags) command.addNonEmpty(platform.llvmLtoFlags)
when { when {
optimize -> command.addNonEmpty(platform.llvmLtoOptFlags) optimize -> command.addNonEmpty(platform.llvmLtoOptFlags)
debug -> command.addNonEmpty(platform.llvmDebugOptFlags) debug -> command.addNonEmpty(platform.llvmDebugOptFlags)
else -> command.addNonEmpty(platform.llvmLtoNooptFlags) else -> command.addNonEmpty(platform.llvmLtoNooptFlags)
} }
command.addNonEmpty(platform.llvmLtoDynamicFlags) command.addNonEmpty(platform.llvmLtoDynamicFlags)
command.addNonEmpty(files) command.addNonEmpty(files)
@@ -89,27 +93,27 @@ internal class LinkStage(val context: Context) {
hostLlvmTool("llvm-link", *bitcodeFiles.toTypedArray(), "-o", combinedBc) hostLlvmTool("llvm-link", *bitcodeFiles.toTypedArray(), "-o", combinedBc)
val optFlags = (configurables.optFlags + when { val optFlags = (configurables.optFlags + when {
optimize -> configurables.optOptFlags optimize -> configurables.optOptFlags
debug -> configurables.optDebugFlags debug -> configurables.optDebugFlags
else -> configurables.optNooptFlags else -> configurables.optNooptFlags
}).toTypedArray() }).toTypedArray()
val optimizedBc = temporary("optimized", ".bc") val optimizedBc = temporary("optimized", ".bc")
hostLlvmTool("opt", combinedBc, "-o", optimizedBc, *optFlags) hostLlvmTool("opt", combinedBc, "-o", optimizedBc, *optFlags)
val llcFlags = (configurables.llcFlags + when { val llcFlags = (configurables.llcFlags + when {
optimize -> configurables.llcOptFlags optimize -> configurables.llcOptFlags
debug -> configurables.llcDebugFlags debug -> configurables.llcDebugFlags
else -> configurables.llcNooptFlags else -> configurables.llcNooptFlags
}).toTypedArray() }).toTypedArray()
val combinedS = temporary("combined", ".s") val combinedS = temporary("combined", ".s")
targetTool("llc", optimizedBc, "-o", combinedS, *llcFlags) targetTool("llc", optimizedBc, "-o", combinedS, *llcFlags)
val s2wasmFlags = configurables.s2wasmFlags.toTypedArray() val s2wasmFlags = configurables.s2wasmFlags.toTypedArray()
val combinedWast = temporary( "combined", ".wast") val combinedWast = temporary("combined", ".wast")
targetTool("s2wasm", combinedS, "-o", combinedWast, *s2wasmFlags) targetTool("s2wasm", combinedS, "-o", combinedWast, *s2wasmFlags)
val combinedWasm = temporary( "combined", ".wasm") val combinedWasm = temporary("combined", ".wasm")
val combinedSmap = temporary( "combined", ".smap") val combinedSmap = temporary("combined", ".smap")
targetTool("wasm-as", combinedWast, "-o", combinedWasm, "-g", "-s", combinedSmap) targetTool("wasm-as", combinedWast, "-o", combinedWasm, "-g", "-s", combinedSmap)
return combinedWasm return combinedWasm
@@ -153,9 +157,11 @@ internal class LinkStage(val context: Context) {
// So we stick to "-alias _main _konan_main" on Mac. // So we stick to "-alias _main _konan_main" on Mac.
// And just do the same on Linux. // And just do the same on Linux.
private val entryPointSelector: List<String> private val entryPointSelector: List<String>
get() = if (nomain || dynamic) emptyList() else platform.entrySelector get() = if (nomain || linkerOutput != LinkerOutputKind.EXECUTABLE) emptyList() else platform.entrySelector
private fun link(objectFiles: List<ObjectFile>, includedBinaries: List<String>, libraryProvidedLinkerFlags: List<String>): ExecutableFile? { private fun link(objectFiles: List<ObjectFile>,
includedBinaries: List<String>,
libraryProvidedLinkerFlags: List<String>): ExecutableFile? {
val frameworkLinkerArgs: List<String> val frameworkLinkerArgs: List<String>
val executable: String val executable: String
@@ -177,23 +183,16 @@ internal class LinkStage(val context: Context) {
} }
try { try {
linker.linkCommand(objectFiles, executable, optimize, debug, dynamic).apply { File(executable).delete()
+ linker.targetLibffi linker.linkCommands(objectFiles = objectFiles, executable = executable,
+ asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) libraries = linker.targetLibffi + linker.linkStaticLibraries(includedBinaries),
+ entryPointSelector linkerArgs = entryPointSelector +
+ frameworkLinkerArgs asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) +
+ linker.linkCommandSuffix() libraryProvidedLinkerFlags + frameworkLinkerArgs,
+ linker.linkStaticLibraries(includedBinaries) optimize = optimize, debug = debug, kind = linkerOutput,
+ libraryProvidedLinkerFlags outputDsymBundle = context.config.outputFile + ".dSYM").forEach {
logger = context::log it.logWith(context::log)
}.execute() it.execute()
if (debug && linker is MacOSBasedLinker) {
val outputDsymBundle = context.config.outputFile + ".dSYM" // `outputFile` is either binary or bundle.
linker.dsymUtilCommand(executable, outputDsymBundle)
.logWith(context::log)
.execute()
} }
} catch (e: KonanExternalToolFailure) { } catch (e: KonanExternalToolFailure) {
context.reportCompilationError("${e.toolName} invocation reported errors") context.reportCompilationError("${e.toolName} invocation reported errors")
@@ -204,27 +203,27 @@ internal class LinkStage(val context: Context) {
fun linkStage() { fun linkStage() {
val bitcodeFiles = listOf(emitted) + val bitcodeFiles = listOf(emitted) +
libraries.map{it -> it.bitcodePaths}.flatten() libraries.map { it -> it.bitcodePaths }.flatten()
val includedBinaries = val includedBinaries =
libraries.map{it -> it.includedPaths}.flatten() libraries.map { it -> it.includedPaths }.flatten()
val libraryProvidedLinkerFlags = val libraryProvidedLinkerFlags =
libraries.map{it -> it.linkerOpts}.flatten() libraries.map { it -> it.linkerOpts }.flatten()
val objectFiles: MutableList<String> = mutableListOf() val objectFiles: MutableList<String> = mutableListOf()
val phaser = PhaseManager(context) val phaser = PhaseManager(context)
phaser.phase(KonanPhase.OBJECT_FILES) { phaser.phase(KonanPhase.OBJECT_FILES) {
objectFiles.add( objectFiles.add(
when (platform.configurables) { when (platform.configurables) {
is WasmConfigurables is WasmConfigurables
-> bitcodeToWasm(bitcodeFiles) -> bitcodeToWasm(bitcodeFiles)
is ZephyrConfigurables is ZephyrConfigurables
-> llvmLinkAndLlc(bitcodeFiles) -> llvmLinkAndLlc(bitcodeFiles)
else else
-> llvmLto(bitcodeFiles) -> llvmLto(bitcodeFiles)
} }
) )
} }
phaser.phase(KonanPhase.LINKER) { phaser.phase(KonanPhase.LINKER) {
@@ -348,7 +348,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
appendLlvmUsed("llvm.compiler.used", context.llvm.compilerUsedGlobals) appendLlvmUsed("llvm.compiler.used", context.llvm.compilerUsedGlobals)
appendStaticInitializers() appendStaticInitializers()
appendEntryPointSelector(context.ir.symbols.entryPoint?.owner) appendEntryPointSelector(context.ir.symbols.entryPoint?.owner)
if (context.isDynamicLibrary) { if (context.isNativeLibrary) {
appendCAdapters() appendCAdapters()
} }
@@ -19,14 +19,15 @@ package org.jetbrains.kotlin.konan.target
import org.jetbrains.kotlin.konan.target.KonanTarget.* import org.jetbrains.kotlin.konan.target.KonanTarget.*
import org.jetbrains.kotlin.konan.util.Named import org.jetbrains.kotlin.konan.util.Named
enum class Family(val exeSuffix:String, val dynamicPrefix: String, val dynamicSuffix: String) { enum class Family(val exeSuffix:String, val dynamicPrefix: String, val dynamicSuffix: String,
OSX ("kexe", "lib", "dylib"), val staticPrefix: String, val staticSuffix: String) {
IOS ("kexe", "lib", "dylib"), OSX ("kexe", "lib", "dylib", "lib", "a"),
LINUX ("kexe", "lib", "so" ), IOS ("kexe", "lib", "dylib", "lib", "a"),
MINGW ("exe" , "" , "dll" ), LINUX ("kexe", "lib", "so" , "lib", "a"),
ANDROID ("so" , "lib", "so" ), MINGW ("exe" , "" , "dll" , "lib", "a"),
WASM ("wasm", "" , "wasm" ), ANDROID ("so" , "lib", "so" , "lib", "a"),
ZEPHYR ("o" , "lib", "a" ) WASM ("wasm", "" , "wasm" , "", "wasm"),
ZEPHYR ("o" , "lib", "a" , "lib", "a")
} }
enum class Architecture(val bitness: Int) { enum class Architecture(val bitness: Int) {
@@ -68,6 +69,10 @@ enum class CompilerOutputKind {
override fun suffix(target: KonanTarget?) = ".${target!!.family.dynamicSuffix}" override fun suffix(target: KonanTarget?) = ".${target!!.family.dynamicSuffix}"
override fun prefix(target: KonanTarget?) = "${target!!.family.dynamicPrefix}" override fun prefix(target: KonanTarget?) = "${target!!.family.dynamicPrefix}"
}, },
STATIC {
override fun suffix(target: KonanTarget?) = ".${target!!.family.staticSuffix}"
override fun prefix(target: KonanTarget?) = "${target!!.family.staticPrefix}"
},
FRAMEWORK { FRAMEWORK {
override fun suffix(target: KonanTarget?): String = ".framework" override fun suffix(target: KonanTarget?): String = ".framework"
}, },
@@ -21,14 +21,19 @@ import java.lang.ProcessBuilder.Redirect
import org.jetbrains.kotlin.konan.exec.Command import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.file.* import org.jetbrains.kotlin.konan.file.*
typealias BitcodeFile = String
typealias ObjectFile = String typealias ObjectFile = String
typealias ExecutableFile = String typealias ExecutableFile = String
enum class LinkerOutputKind {
DYNAMIC_LIBRARY,
STATIC_LIBRARY,
EXECUTABLE
}
// Use "clang -v -save-temps" to write linkCommand() method // Use "clang -v -save-temps" to write linkCommand() method
// for another implementation of this class. // for another implementation of this class.
abstract class LinkerFlags(val configurables: Configurables) abstract class LinkerFlags(val configurables: Configurables)
/* : Configurables by configurables */{ /* : Configurables by configurables */ {
protected val llvmBin = "${configurables.absoluteLlvmHome}/bin" protected val llvmBin = "${configurables.absoluteLlvmHome}/bin"
protected val llvmLib = "${configurables.absoluteLlvmHome}/lib" protected val llvmLib = "${configurables.absoluteLlvmHome}/lib"
@@ -41,55 +46,80 @@ abstract class LinkerFlags(val configurables: Configurables)
val libLTO = "$libLTODir/${System.mapLibraryName("LTO")}" val libLTO = "$libLTODir/${System.mapLibraryName("LTO")}"
val targetLibffi = configurables.libffiDir ?.let { listOf("${configurables.absoluteLibffiDir}/lib/libffi.a") } ?: emptyList() val targetLibffi = configurables.libffiDir?.let { listOf("${configurables.absoluteLibffiDir}/lib/libffi.a") }
?: emptyList()
open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor. open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor.
abstract fun linkCommand(objectFiles: List<ObjectFile>, abstract fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
open fun linkCommandSuffix(): List<String> = emptyList() kind: LinkerOutputKind, outputDsymBundle: String): List<Command>
abstract fun filterStaticLibraries(binaries: List<String>): List<String> abstract fun filterStaticLibraries(binaries: List<String>): List<String>
open fun linkStaticLibraries(binaries: List<String>): List<String> { open fun linkStaticLibraries(binaries: List<String>): List<String> {
val libraries = filterStaticLibraries(binaries) val libraries = filterStaticLibraries(binaries)
// Let's just pass them as absolute paths // Let's just pass them as absolute paths.
return libraries return libraries
} }
protected fun postLinkGnuArCommand(ar: String, executable: ExecutableFile) =
Command("/bin/sh", "-c").apply {
+"/bin/echo -e 'create $executable\\naddlib $executable\\nsave\\nend' | $ar -M"
}
} }
open class AndroidLinker(targetProperties: AndroidConfigurables) open class AndroidLinker(targetProperties: AndroidConfigurables)
: LinkerFlags(targetProperties), AndroidConfigurables by targetProperties { : LinkerFlags(targetProperties), AndroidConfigurables by targetProperties {
private val prefix = "$absoluteTargetToolchain/bin/" private val prefix = "$absoluteTargetToolchain/bin/"
private val clang = "$prefix/clang" private val clang = "$prefix/clang"
private val ar = "$prefix/ar"
override val useCompilerDriverAsLinker: Boolean get() = true override val useCompilerDriverAsLinker: Boolean get() = true
override fun filterStaticLibraries(binaries: List<String>) override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isUnixStaticLib }
= binaries.filter { it.isUnixStaticLib }
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
// liblog.so must be linked in, as we use its functionality in runtime. libraries: List<String>, linkerArgs: List<String>,
return Command(clang).apply { optimize: Boolean, debug: Boolean,
+ "-o" kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
+ executable if (kind == LinkerOutputKind.STATIC_LIBRARY) {
+ "-fPIC" // Here we take somewhat unexpected approach - we create the thin
+ "-shared" // library, and then repack it during post-link phase.
+ "-llog" // This way we ensure .a inputs are properly processed.
+ objectFiles return listOf(
if (optimize) + linkerOptimizationFlags Command(ar, "cqT", executable).apply {
if (!debug) + linkerNoDebugFlags +objectFiles
if (dynamic) + linkerDynamicFlags +libraries
+ linkerKonanFlags },
postLinkGnuArCommand(ar, executable))
} }
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
// liblog.so must be linked in, as we use its functionality in runtime.
return listOf(Command(clang).apply {
+"-o"
+executable
+"-fPIC"
+"-shared"
+"-llog"
+objectFiles
if (optimize) +linkerOptimizationFlags
if (!debug) +linkerNoDebugFlags
if (dynamic) +linkerDynamicFlags
+linkerKonanFlags
+libraries
+linkerArgs
})
} }
} }
open class MacOSBasedLinker(targetProperties: AppleConfigurables) open class MacOSBasedLinker(targetProperties: AppleConfigurables)
: LinkerFlags(targetProperties), AppleConfigurables by targetProperties { : LinkerFlags(targetProperties), AppleConfigurables by targetProperties {
private val libtool = "$absoluteTargetToolchain/usr/bin/libtool"
private val linker = "$absoluteTargetToolchain/usr/bin/ld" private val linker = "$absoluteTargetToolchain/usr/bin/ld"
internal val dsymutil = "$absoluteLlvmHome/bin/llvm-dsymutil" internal val dsymutil = "$absoluteLlvmHome/bin/llvm-dsymutil"
@@ -99,30 +129,42 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
osVersionMin + ".0") osVersionMin + ".0")
} }
override fun filterStaticLibraries(binaries: List<String>) override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isUnixStaticLib }
= binaries.filter { it.isUnixStaticLib }
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
return object : Command(linker) {} .apply { libraries: List<String>, linkerArgs: List<String>,
+ "-demangle" optimize: Boolean, debug: Boolean, kind: LinkerOutputKind,
+ listOf("-object_path_lto", "temporary.o", "-lto_library", libLTO) outputDsymBundle: String): List<Command> {
+ listOf("-dynamic", "-arch", arch) if (kind == LinkerOutputKind.STATIC_LIBRARY)
+ osVersionMinFlags return listOf(Command(libtool).apply {
+ listOf("-syslibroot", absoluteTargetSysRoot, "-o", executable) +"-static"
+ objectFiles +listOf("-o", executable)
if (optimize) + linkerOptimizationFlags +objectFiles
if (!debug) + linkerNoDebugFlags +libraries
if (dynamic) + linkerDynamicFlags })
+ linkerKonanFlags val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
+ "-lSystem" return listOf(Command(linker).apply {
} +"-demangle"
+listOf("-object_path_lto", "temporary.o", "-lto_library", libLTO)
+listOf("-dynamic", "-arch", arch)
+osVersionMinFlags
+listOf("-syslibroot", absoluteTargetSysRoot, "-o", executable)
+objectFiles
if (optimize) +linkerOptimizationFlags
if (!debug) +linkerNoDebugFlags
if (dynamic) +linkerDynamicFlags
+linkerKonanFlags
+"-lSystem"
+libraries
+linkerArgs
}) + if (debug) listOf(dsymUtilCommand(executable, outputDsymBundle)) else emptyList()
} }
fun dsymUtilCommand(executable: ExecutableFile, outputDsymBundle: String) = fun dsymUtilCommand(executable: ExecutableFile, outputDsymBundle: String) =
object : Command(dsymutilCommand(executable, outputDsymBundle)) { object : Command(dsymutilCommand(executable, outputDsymBundle)) {
override fun runProcess(): Int = override fun runProcess(): Int =
executeCommandWithFilter(command) executeCommandWithFilter(command)
} }
// TODO: consider introducing a better filtering directly in Command. // TODO: consider introducing a better filtering directly in Command.
private fun executeCommandWithFilter(command: List<String>): Int { private fun executeCommandWithFilter(command: List<String>): Int {
@@ -156,105 +198,135 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
} }
open fun dsymutilCommand(executable: ExecutableFile, outputDsymBundle: String): List<String> = open fun dsymutilCommand(executable: ExecutableFile, outputDsymBundle: String): List<String> =
listOf(dsymutil, executable, "-o", outputDsymBundle) listOf(dsymutil, executable, "-o", outputDsymBundle)
open fun dsymutilDryRunVerboseCommand(executable: ExecutableFile): List<String> = open fun dsymutilDryRunVerboseCommand(executable: ExecutableFile): List<String> =
listOf(dsymutil, "-dump-debug-map" ,executable) listOf(dsymutil, "-dump-debug-map", executable)
} }
open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables) open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables)
: LinkerFlags(targetProperties), LinuxBasedConfigurables by targetProperties { : LinkerFlags(targetProperties), LinuxBasedConfigurables by targetProperties {
private val ar = "$absoluteTargetToolchain/bin/ar"
override val libGcc: String = "$absoluteTargetSysRoot/${super.libGcc}" override val libGcc: String = "$absoluteTargetSysRoot/${super.libGcc}"
private val linker = "$absoluteTargetToolchain/bin/ld.gold" private val linker = "$absoluteTargetToolchain/bin/ld.gold"
private val specificLibs private val specificLibs = abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" }
= abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" }
override fun filterStaticLibraries(binaries: List<String>) override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isUnixStaticLib }
= binaries.filter { it.isUnixStaticLib }
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
val isMips = (configurables is LinuxMIPSConfigurables) libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
// TODO: Can we extract more to the konan.configurables? kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
return Command(linker).apply { if (kind == LinkerOutputKind.STATIC_LIBRARY) {
+ "--sysroot=${absoluteTargetSysRoot}" // Here we take somewhat unexpected approach - we create the thin
+ "-export-dynamic" // library, and then repack it during post-link phase.
+ "-z" // This way we ensure .a inputs are properly processed.
+ "relro" return listOf(Command(ar, "cqT", executable).apply {
+ "--build-id" +objectFiles
+ "--eh-frame-hdr" +libraries
+ "-dynamic-linker" },
+ dynamicLinker postLinkGnuArCommand(ar, executable))
+ "-o"
+ executable
if (!dynamic) + "$absoluteTargetSysRoot/usr/lib64/crt1.o"
+ "$absoluteTargetSysRoot/usr/lib64/crti.o"
if (dynamic)
+ "$libGcc/crtbeginS.o"
else
+ "$libGcc/crtbegin.o"
+ "-L$llvmLib"
+ "-L$libGcc"
if (!isMips) + "--hash-style=gnu" // MIPS doesn't support hash-style=gnu
+ specificLibs
+ listOf("-L$absoluteTargetSysRoot/../lib", "-L$absoluteTargetSysRoot/lib", "-L$absoluteTargetSysRoot/usr/lib")
if (optimize) {
+ "-plugin"
+"$llvmLib/LLVMgold.so"
+ pluginOptimizationFlags
}
if (optimize) + linkerOptimizationFlags
if (!debug) + linkerNoDebugFlags
if (dynamic) + linkerDynamicFlags
+ objectFiles
+ linkerKonanFlags
+ listOf("-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed",
"-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed")
if (dynamic)
+ "$libGcc/crtendS.o"
else
+ "$libGcc/crtend.o"
+ "$absoluteTargetSysRoot/usr/lib64/crtn.o"
} }
val isMips = (configurables is LinuxMIPSConfigurables)
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
// TODO: Can we extract more to the konan.configurables?
return listOf(Command(linker).apply {
+"--sysroot=${absoluteTargetSysRoot}"
+"-export-dynamic"
+"-z"
+"relro"
+"--build-id"
+"--eh-frame-hdr"
+"-dynamic-linker"
+dynamicLinker
+"-o"
+executable
if (!dynamic) +"$absoluteTargetSysRoot/usr/lib64/crt1.o"
+"$absoluteTargetSysRoot/usr/lib64/crti.o"
+if (dynamic) "$libGcc/crtbeginS.o" else "$libGcc/crtbegin.o"
+"-L$llvmLib"
+"-L$libGcc"
if (!isMips) +"--hash-style=gnu" // MIPS doesn't support hash-style=gnu
+specificLibs
+listOf("-L$absoluteTargetSysRoot/../lib", "-L$absoluteTargetSysRoot/lib", "-L$absoluteTargetSysRoot/usr/lib")
if (optimize) {
+"-plugin"
+"$llvmLib/LLVMgold.so"
+pluginOptimizationFlags
}
if (optimize) +linkerOptimizationFlags
if (!debug) +linkerNoDebugFlags
if (dynamic) +linkerDynamicFlags
+objectFiles
+linkerKonanFlags
+listOf("-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed",
"-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed")
+if (dynamic) "$libGcc/crtendS.o" else "$libGcc/crtend.o"
+"$absoluteTargetSysRoot/usr/lib64/crtn.o"
+libraries
+linkerArgs
})
} }
} }
open class MingwLinker(targetProperties: MingwConfigurables) open class MingwLinker(targetProperties: MingwConfigurables)
: LinkerFlags(targetProperties), MingwConfigurables by targetProperties { : LinkerFlags(targetProperties), MingwConfigurables by targetProperties {
private val ar = "$absoluteTargetToolchain\\bin\\ar"
private val linker = "$absoluteTargetToolchain/bin/clang++" private val linker = "$absoluteTargetToolchain/bin/clang++"
override val useCompilerDriverAsLinker: Boolean get() = true override val useCompilerDriverAsLinker: Boolean get() = true
override fun filterStaticLibraries(binaries: List<String>) override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib }
= binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib }
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
return Command(linker).apply { libraries: List<String>, linkerArgs: List<String>,
+ listOf("-o", executable) optimize: Boolean, debug: Boolean,
+ objectFiles kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
if (optimize) + linkerOptimizationFlags if (kind == LinkerOutputKind.STATIC_LIBRARY) {
if (!debug) + linkerNoDebugFlags // Here we take somewhat unexpected approach - we create the thin
if (dynamic) + linkerDynamicFlags // library, and then repack it during post-link phase.
// This way we ensure .a inputs are properly processed.
val temp = executable.replace('/', '\\') + "__"
return listOf(
Command(ar, "-rucT", temp).apply {
+objectFiles
+libraries
},
Command("cmd", "/c").apply {
+"(echo create $executable & echo addlib ${temp} & echo save & echo end) | $ar -M"
},
Command("cmd", "/c", "del", "/q", temp))
} }
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
return listOf(Command(linker).apply {
+listOf("-o", executable)
+objectFiles
if (optimize) +linkerOptimizationFlags
if (!debug) +linkerNoDebugFlags
if (dynamic) +linkerDynamicFlags
+libraries
+linkerArgs
+linkerKonanFlags
})
} }
override fun linkCommandSuffix() = linkerKonanFlags
} }
open class WasmLinker(targetProperties: WasmConfigurables) open class WasmLinker(targetProperties: WasmConfigurables)
: LinkerFlags(targetProperties), WasmConfigurables by targetProperties { : LinkerFlags(targetProperties), WasmConfigurables by targetProperties {
private val clang = "clang"
override val useCompilerDriverAsLinker: Boolean get() = false override val useCompilerDriverAsLinker: Boolean get() = false
override fun filterStaticLibraries(binaries: List<String>) override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isJavaScript }
= binaries.filter{it.isJavaScript}
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
return object: Command("") { libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind")
// TODO(horsh): make it proper list.
return listOf(object : Command() {
override fun execute() { override fun execute() {
val src = File(objectFiles.single()) val src = File(objectFiles.single())
val dst = File(executable) val dst = File(executable)
@@ -281,7 +353,7 @@ open class WasmLinker(targetProperties: WasmConfigurables)
linkedJavaScript.appendBytes(linkerFooter.toByteArray()); linkedJavaScript.appendBytes(linkerFooter.toByteArray());
return linkedJavaScript.name return linkedJavaScript.name
} }
} })
} }
} }
@@ -292,33 +364,38 @@ open class ZephyrLinker(targetProperties: ZephyrConfigurables)
override val useCompilerDriverAsLinker: Boolean get() = false override val useCompilerDriverAsLinker: Boolean get() = false
override fun filterStaticLibraries(binaries: List<String>) override fun filterStaticLibraries(binaries: List<String>) = emptyList<String>()
= emptyList<String>()
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
return Command(linker).apply { libraries: List<String>, linkerArgs: List<String>,
+ listOf("-r", "--gc-sections", "--entry", "main") optimize: Boolean, debug: Boolean,
+ listOf("-o", executable) kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
+ objectFiles if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind: $kind")
} return listOf(Command(linker).apply {
+listOf("-r", "--gc-sections", "--entry", "main")
+listOf("-o", executable)
+objectFiles
+libraries
+linkerArgs
})
} }
} }
fun linker(configurables: Configurables): LinkerFlags = fun linker(configurables: Configurables): LinkerFlags =
when (configurables.target) { when (configurables.target) {
KonanTarget.LINUX_X64, KonanTarget.LINUX_ARM32_HFP -> KonanTarget.LINUX_X64, KonanTarget.LINUX_ARM32_HFP ->
LinuxBasedLinker(configurables as LinuxConfigurables) LinuxBasedLinker(configurables as LinuxConfigurables)
KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 -> KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 ->
LinuxBasedLinker(configurables as LinuxMIPSConfigurables) LinuxBasedLinker(configurables as LinuxMIPSConfigurables)
KonanTarget.MACOS_X64, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> KonanTarget.MACOS_X64, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 ->
MacOSBasedLinker(configurables as AppleConfigurables) MacOSBasedLinker(configurables as AppleConfigurables)
KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64 -> KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64 ->
AndroidLinker(configurables as AndroidConfigurables) AndroidLinker(configurables as AndroidConfigurables)
KonanTarget.MINGW_X64 -> KonanTarget.MINGW_X64 ->
MingwLinker(configurables as MingwConfigurables) MingwLinker(configurables as MingwConfigurables)
KonanTarget.WASM32 -> KonanTarget.WASM32 ->
WasmLinker(configurables as WasmConfigurables) WasmLinker(configurables as WasmConfigurables)
is KonanTarget.ZEPHYR -> is KonanTarget.ZEPHYR ->
ZephyrLinker(configurables as ZephyrConfigurables) ZephyrLinker(configurables as ZephyrConfigurables)
} }
@@ -110,6 +110,10 @@ class EnvVariableSpecification extends BaseKonanSpecification {
prefix = target.family.dynamicPrefix prefix = target.family.dynamicPrefix
suffix = target.family.dynamicSuffix suffix = target.family.dynamicSuffix
break break
case ArtifactType.STATIC:
prefix = target.family.staticPrefix
suffix = target.family.staticSuffix
break
case ArtifactType.FRAMEWORK: case ArtifactType.FRAMEWORK:
suffix = "framework" suffix = "framework"
} }
@@ -194,6 +198,7 @@ class EnvVariableSpecification extends BaseKonanSpecification {
files.contains(artifactFileName("program", ArtifactType.PROGRAM)) files.contains(artifactFileName("program", ArtifactType.PROGRAM))
files.contains(artifactFileName("library", ArtifactType.LIBRARY)) files.contains(artifactFileName("library", ArtifactType.LIBRARY))
files.contains(artifactFileName("dynamic", ArtifactType.DYNAMIC)) files.contains(artifactFileName("dynamic", ArtifactType.DYNAMIC))
files.contains(artifactFileName("static", ArtifactType.STATIC))
if (HostManager.hostIsMac) { if (HostManager.hostIsMac) {
files.contains(artifactFileName("framework", ArtifactType.FRAMEWORK)) files.contains(artifactFileName("framework", ArtifactType.FRAMEWORK))
} }
@@ -29,6 +29,7 @@ enum ArtifactType {
BITCODE("bitcode"), BITCODE("bitcode"),
INTEROP("interop"), INTEROP("interop"),
DYNAMIC("dynamic"), DYNAMIC("dynamic"),
STATIC("static"),
FRAMEWORK("framework") FRAMEWORK("framework")
String type String type
@@ -32,6 +32,10 @@ open class PropertiesAsEnvVariables {
prefix = target.family.dynamicPrefix prefix = target.family.dynamicPrefix
suffix = target.family.dynamicSuffix suffix = target.family.dynamicSuffix
} }
ArtifactType.STATIC -> {
prefix = target.family.staticPrefix
suffix = target.family.staticSuffix
}
} }
return "$prefix${baseName}.$suffix" return "$prefix${baseName}.$suffix"