Initial checkin of klib support.

The libraries exist in two incarnations:
    packed foo.klib and unpacked foo/ .
The compiler when given '-library foo' expects to find either
a foo.klib file and unpack it, or an already unpacked foo/ directory.

The stdlib is always unpacked in dist/klib .

The semantics of -o has changed slightly.
It now accepts '-o foo' and the compiler
either produces foo.kexe or foo.klib .
This commit is contained in:
Alexander Gorshenev
2017-04-27 16:14:17 +03:00
committed by alexander-gorshenev
parent 77e093354f
commit 3b5ce031f8
24 changed files with 564 additions and 203 deletions
+3
View File
@@ -38,6 +38,9 @@ model {
include '**/*.c'
}
binaries.all {
def host = rootProject.ext.host
def hostLibffiDir = rootProject.ext.get("${host}LibffiDir")
cCompiler.args compilerArgsForJniIncludes
cCompiler.args "-I$hostLibffiDir/include"
+10 -10
View File
@@ -153,7 +153,7 @@ kotlinNativeInterop {
compilerOpts '-fPIC'
linkerOpts '-fPIC'
linker 'clang++'
linkOutputs ':common:hostHash'
linkOutputs ":common:${host}Hash"
headers fileTree('../common/src/hash/headers') {
include '**/*.h'
@@ -194,8 +194,8 @@ build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses'
// These are just a couple of aliases
task stdlib(dependsOn: 'hostStdlib')
task start(dependsOn: 'hostStart')
task stdlib(dependsOn: "${host}Stdlib")
task start(dependsOn: "${host}Start")
// These files are built before the 'dist' is complete,
// so we provide custom values for
@@ -208,8 +208,8 @@ targetList.each { target ->
jvmArgs "-ea",
"-Dkonan.home=${project.parent.file('dist')}",
"-Djava.library.path=${project.buildDir}/nativelibs"
args('-output', project(':runtime').file("build/${target}/stdlib.kt.bc"),
'-nolink', '-nostdlib', '-ea',
args('-output', project(':runtime').file("build/${target}Stdlib"),
'-nolink', '-nopack', '-nostdlib','-ea',
'-target', target,
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
'-properties', project(':backend.native').file('konan.properties'),
@@ -221,7 +221,7 @@ targetList.each { target ->
inputs.dir(project(':runtime').file('src/main/kotlin'))
inputs.dir(project(':Interop:Runtime').file('src/main/kotlin'))
inputs.dir(project(':Interop:Runtime').file('src/native/kotlin'))
outputs.file(project(':runtime').file("build/${target}/stdlib.kt.bc"))
outputs.file(project(':runtime').file("build/${target}Stdlib"))
dependsOn ":runtime:${target}Runtime"
}
@@ -234,17 +234,17 @@ targetList.each { target ->
jvmArgs "-ea",
"-Dkonan.home=${project.parent.file('dist')}",
"-Djava.library.path=${project.buildDir}/nativelibs"
args('-output', project(':runtime').file("build/${target}/start.kt.bc"),
'-nolink', '-nostdlib', '-ea',
args('-output', project(':runtime').file("build/${target}Start"),
'-nolink', '-nopack', '-nostdlib', '-ea',
'-target', target,
'-library', project(':runtime').file("build/${target}/stdlib.kt.bc"),
'-library', project(':runtime').file("build/${target}Stdlib"),
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
'-properties', project(':backend.native').file('konan.properties'),
project(':runtime').file('src/launcher/kotlin'),
*project.globalBuildArgs)
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
outputs.file(project(':runtime').file("build/${target}/start.kt.bc"))
outputs.file(project(':runtime').file("build/${target}Start"))
dependsOn ":runtime:${target}Runtime", "${target}Stdlib"
}
@@ -90,36 +90,38 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
put(NOSTDLIB, arguments.nostdlib)
put(NOLINK, arguments.nolink)
put(NOPACK, arguments.nopack)
put(NOMAIN, arguments.nomain)
put(LIBRARY_FILES,
arguments.libraries.toNonNullList())
put(LINKER_ARGS, arguments.linkerArguments.toNonNullList())
if (arguments.target != null)
put(TARGET, arguments.target)
put(NATIVE_LIBRARY_FILES,
arguments.nativeLibraries.toNonNullList())
// TODO: Collect all the explicit file names into an object
// and teach the compiler to work with temporaries and -save-temps.
val bitcodeFile = if (arguments.nolink) {
arguments.outputFile ?: "program.kt.bc"
} else {
"${arguments.outputFile ?: "program"}.kt.bc"
val library = arguments.outputFile ?: "library"
if (arguments.nolink)
put(LIBRARY_NAME, library)
put(LIBRARY_FILE, "${library}.klib")
val program = arguments.outputFile ?: "program"
if (!arguments.nolink) {
put(PROGRAM_NAME,program)
put(EXECUTABLE_FILE,"${program}.kexe")
}
put(BITCODE_FILE, bitcodeFile)
// This is a decision we could change
put(CommonConfigurationKeys.MODULE_NAME, bitcodeFile)
val module = if (arguments.nolink) library else program
put(CommonConfigurationKeys.MODULE_NAME, module)
put(ABI_VERSION, 1)
put(EXECUTABLE_FILE,
arguments.outputFile ?: "program.kexe")
if (arguments.runtimeFile != null)
put(RUNTIME_FILE, arguments.runtimeFile)
if (arguments.propertyFile != null)
put(PROPERTY_FILE, arguments.propertyFile)
if (arguments.target != null)
put(TARGET, arguments.target)
put(LIST_TARGETS, arguments.listTargets)
put(OPTIMIZATION, arguments.optimization)
put(DEBUG, arguments.debug)
@@ -38,6 +38,9 @@ public class K2NativeCompilerArguments extends CommonCompilerArguments {
@Argument(value = "-nolink", description = "Don't link, just produce a bitcode file")
public boolean nolink;
@Argument(value = "-nopack", description = "Don't pack the library into a klib file")
public boolean nopack;
@Argument(value = "-nomain", description = "Assume 'main' entry point to be provided by external libraries")
public boolean nomain;
@@ -55,11 +55,6 @@ import java.util.*
import kotlin.LazyThreadSafetyMode.PUBLICATION
import kotlin.reflect.KProperty
internal class LinkData(
val module: String,
val fragments: List<String>,
val fragmentNames: List<String> )
internal class SpecialDescriptorsFactory(val context: Context) {
private val enumSpecialDescriptorsFactory by lazy { EnumSpecialDescriptorsFactory(context) }
private val outerThisDescriptors = mutableMapOf<ClassDescriptor, PropertyDescriptor>()
@@ -270,6 +265,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
lateinit var llvm: Llvm
lateinit var llvmDeclarations: LlvmDeclarations
lateinit var bitcodeFileName: String
lateinit var library: KonanLibraryWriter
var phase: KonanPhase? = null
var depth: Int = 0
@@ -22,9 +22,7 @@ import java.io.File
class Distribution(val config: CompilerConfiguration) {
val targetManager = TargetManager(config)
val target =
if (!targetManager.crossCompile) "host"
else targetManager.current.name.toLowerCase()
val target = targetManager.currentName
val suffix = targetManager.currentSuffix()
val hostSuffix = TargetManager.host.suffix
init { if (!targetManager.crossCompile) assert(suffix == hostSuffix) }
@@ -40,16 +38,14 @@ class Distribution(val config: CompilerConfiguration) {
?: "$konanHome/konan/konan.properties"
val properties = KonanProperties(propertyFile)
val lib = "$konanHome/lib/$target"
val klib = "$konanHome/klib"
val dependenciesDir = "$konanHome/dependencies"
val dependencies = properties.propertyList("dependencies.$suffix")
val stdlib = "$lib/stdlib.kt.bc"
val start = "$lib/start.kt.bc"
val launcher = "$lib/launcher.bc"
val stdlib = "$klib/stdlib"
val runtime = config.get(KonanConfigKeys.RUNTIME_FILE)
?: "$lib/runtime.bc"
?: "$stdlib/$target/native/runtime.bc"
val llvmHome = "$dependenciesDir/${properties.propertyString("llvmHome.$hostSuffix")}"
val sysRoot = "$dependenciesDir/${properties.propertyString("sysRoot.$hostSuffix")}"
@@ -28,6 +28,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
val moduleId: String
get() = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)
internal val targetManager = TargetManager(configuration)
internal val distribution = Distribution(configuration)
private val libraryNames: List<String>
@@ -39,16 +40,15 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
return fromCommandLine + distribution.stdlib
}
internal val libraries: List<KonanLibrary> by lazy {
// Here we have chosen a particular .kt.bc KonanLibrary implementation
libraryNames.map{it -> KtBcLibrary(it, configuration)}
internal val libraries: List<KonanLibraryReader> by lazy {
// Here we have chosen a particular KonanLibraryReader implementation
libraryNames.map{it -> SplitLibraryReader(it, configuration)}
}
private val loadedDescriptors = loadLibMetadata()
internal val nativeLibraries: List<String> = configuration.getList(KonanConfigKeys.NATIVE_LIBRARY_FILES)
fun loadLibMetadata(): List<ModuleDescriptorImpl> {
val allMetadata = mutableListOf<ModuleDescriptorImpl>()
@@ -25,8 +25,12 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("library file paths")
val NATIVE_LIBRARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("native library file paths")
val BITCODE_FILE: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("emitted bitcode file path")
val LIBRARY_NAME: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("library name")
val LIBRARY_FILE: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("library file path")
val PROGRAM_NAME: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("program name")
val EXECUTABLE_FILE: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("final executable file path")
val RUNTIME_FILE: CompilerConfigurationKey<String?>
@@ -43,6 +47,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("don't link with stdlib")
val NOLINK: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link, only produce a bitcode file ")
val NOPACK: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't the library into a klib file")
val NOMAIN: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries")
val LINKER_ARGS: CompilerConfigurationKey<List<String>>
@@ -48,7 +48,7 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
val config = konanConfig.configuration
val targets = TargetManager(config)
val targets = konanConfig.targetManager
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
targets.list()
}
@@ -1,69 +0,0 @@
/*
* 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
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.backend.konan.llvm.MetadataReader
import org.jetbrains.kotlin.backend.konan.llvm.loadSerializedModule
import org.jetbrains.kotlin.backend.konan.llvm.loadSerializedPackageFragment
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.backend.konan.serialization.Base64
import org.jetbrains.kotlin.backend.konan.serialization.deserializeModule
import java.io.File
interface KonanLibrary {
val libraryName: String
val moduleName: String
val moduleDescriptor: ModuleDescriptorImpl
val bitcodePaths: List<String>
}
class KtBcLibrary(val file: File, val configuration: CompilerConfiguration): KonanLibrary {
constructor(path: String, configuration: CompilerConfiguration) : this(File(path), configuration)
init {
if (!file.exists())
error("Path '" + file.path + "' does not exist")
}
override val libraryName: String
get() = file.path
override val bitcodePaths: List<String>
get() = listOf(libraryName)
private val reader = MetadataReader(file)
private val namedModuleData by lazy {
val currentAbiVersion = configuration.get(KonanConfigKeys.ABI_VERSION)!!
reader.loadSerializedModule(currentAbiVersion)
}
override val moduleName = namedModuleData.name
private val tableOfContentsAsString = namedModuleData.base64
private fun packageMetadata(fqName: String): Base64 =
reader.loadSerializedPackageFragment(fqName)
override val moduleDescriptor: ModuleDescriptorImpl by lazy {
deserializeModule(configuration,
{it -> packageMetadata(it)},
tableOfContentsAsString, moduleName)
}
}
@@ -77,6 +77,7 @@ object KonanPhases {
// Don't serialize anything to a final executable.
KonanPhase.SERIALIZER.enabled = getBoolean(NOLINK)
KonanPhase.METADATOR.enabled = getBoolean(NOLINK)
val disabled = get(DISABLED_PHASES)
disabled?.forEach { phases[known(it)]!!.enabled = false }
@@ -30,6 +30,8 @@ enum class KonanTarget(val suffix: String, var enabled: Boolean = false) {
class TargetManager(val config: CompilerConfiguration) {
val targets = KonanTarget.values().associate{ it.name.toLowerCase() to it }
val current = determineCurrent()
val currentName
get() = current.name.toLowerCase()
init {
when (host) {
@@ -147,9 +147,9 @@ internal class LinkStage(val context: Context) {
val config = context.config.configuration
val targetManager = TargetManager(config)
val targetManager = context.config.targetManager
private val distribution =
Distribution(context.config.configuration)
context.config.distribution
private val properties = distribution.properties
val platform = when (TargetManager.host) {
@@ -164,8 +164,8 @@ internal class LinkStage(val context: Context) {
val suffix = targetManager.currentSuffix()
val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false
val emitted = config.get(KonanConfigKeys.BITCODE_FILE)!!
val nomain = config.get(KonanConfigKeys.NOMAIN) ?: false
val emitted = context.bitcodeFileName
val libraries = context.config.libraries
fun llvmLto(files: List<BitcodeFile>): ObjectFile {
@@ -265,8 +265,7 @@ internal class LinkStage(val context: Context) {
fun linkStage() {
context.log{"# Compiler root: ${distribution.konanHome}"}
val bitcodeFiles = listOf<BitcodeFile>(emitted, distribution.start,
distribution.runtime, distribution.launcher) +
val bitcodeFiles = listOf<BitcodeFile>(emitted) +
libraries.map{it -> it.bitcodePaths}.flatten()
var objectFiles: List<String> = listOf()
@@ -0,0 +1,70 @@
/*
* 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
import java.io.File
import java.net.URI
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
val File.zipUri: URI
get() = URI.create("jar:file:${this.absolutePath}")
val File.zipRootPath: Path
get() {
val zipUri = this.zipUri
val allowCreation = hashMapOf("create" to "true")
val zipfs = FileSystems.newFileSystem(zipUri, allowCreation, null)
return zipfs.getPath("/")
}
fun File.zipDirAs(zipFile: File) {
val zipPath = zipFile.zipRootPath
this.toPath().recursiveCopyTo(zipPath)
zipPath.fileSystem.close()
}
fun File.unzipAs(directory: File) {
val zipPath = this.zipRootPath
zipPath.recursiveCopyTo(directory.toPath())
zipPath.fileSystem.close()
}
fun Path.recursiveCopyTo(destPath: Path) {
val sourcePath = this
Files.walk(sourcePath).forEach next@ { oldPath ->
val relative = sourcePath.relativize(oldPath)
val destFs = destPath.getFileSystem()
// We are copying files between file systems,
// so pass the relative path through the Sting.
val newPath = destFs.getPath(destPath.toString(), relative.toString())
// File systems don't allow replacing an existing root.
if (newPath == newPath.getRoot()) return@next
Files.copy(oldPath, newPath,
StandardCopyOption.REPLACE_EXISTING)
}
}
fun File.copyTo(destination: File) {
Files.copy(this.toPath(), destination.toPath())
}
@@ -0,0 +1,225 @@
/*
* 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
import llvm.LLVMLinkModules2
import llvm.LLVMModuleRef
import llvm.LLVMWriteBitcodeToFile
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.serialization.Base64
import org.jetbrains.kotlin.backend.konan.serialization.deserializeModule
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import java.io.File
interface KonanLibraryReader {
val libraryName: String
val moduleName: String
val moduleDescriptor: ModuleDescriptorImpl
val bitcodePaths: List<String>
}
abstract class FileBasedLibraryReader(
val file: File,
val configuration: CompilerConfiguration,
val reader: MetadataReader): KonanLibraryReader {
override val libraryName: String
get() = file.path
protected val namedModuleData by lazy {
val currentAbiVersion = configuration.get(KonanConfigKeys.ABI_VERSION)!!
reader.loadSerializedModule(currentAbiVersion)
}
override val moduleName: String
get() = namedModuleData.name
protected val tableOfContentsAsString : String
get() = namedModuleData.base64
protected fun packageMetadata(fqName: String): Base64 =
reader.loadSerializedPackageFragment(fqName)
override val moduleDescriptor: ModuleDescriptorImpl by lazy {
deserializeModule(configuration,
{it -> packageMetadata(it)},
tableOfContentsAsString, moduleName)
}
}
class KtBcLibraryReader(file: File, configuration: CompilerConfiguration)
: FileBasedLibraryReader(file, configuration, KtBcMetadataReader(file)) {
public constructor(path: String, configuration: CompilerConfiguration) : this(File(path), configuration)
override val bitcodePaths: List<String>
get() = listOf(libraryName)
}
// TODO: Get rid of the configuration here.
class SplitLibraryReader(val libDir: File, configuration: CompilerConfiguration)
: FileBasedLibraryReader(libDir, configuration, SplitMetadataReader(libDir)) {
public constructor(path: String, configuration: CompilerConfiguration) : this(File(path), configuration)
val klibFile = File("${libDir.path}.klib")
init {
unpackIfNeeded()
}
// TODO: Search path processing is also goes somewhere around here.
fun unpackIfNeeded() {
// TODO: Clarify the policy here.
if (libDir.exists()) {
if (libDir.isDirectory()) return
}
if (!klibFile.exists()) {
error("Could not find neither $libDir nor $klibFile.")
}
if (klibFile.isFile()) {
klibFile.unzipAs(libDir)
if (!libDir.exists()) error("Could not unpack $klibFile as $libDir.")
} else {
error("Expected $klibFile to be a regular libDir.")
}
}
private val File.dirAbsolutePaths: List<String>
get() = this.listFiles()!!.toList()!!.map{it->it.absolutePath}
private val targetDir: File
get() {
val target = TargetManager(configuration).currentName
val dir = File(libDir, target)
return dir
}
override val bitcodePaths: List<String>
get() = File(targetDir, "kotlin").dirAbsolutePaths +
File(targetDir, "native").dirAbsolutePaths
}
/* ------------ writer part ----------------*/
interface KonanLibraryWriter {
fun addLinkData(linkData: LinkData)
fun addNativeBitcode(library: String)
fun addKotlinBitcode(llvmModule: LLVMModuleRef)
fun commit()
val mainBitcodeFileName: String
}
class LinkData(
val abiVersion: Int,
val module: String,
val moduleName: String,
val fragments: List<String>,
val fragmentNames: List<String> )
abstract class FileBasedLibraryWriter (
val file: File): KonanLibraryWriter {
}
class KtBcLibraryWriter(file: File, val llvmModule: LLVMModuleRef)
: FileBasedLibraryWriter(file) {
override val mainBitcodeFileName = file.path
public constructor(path: String, llvmModule: LLVMModuleRef)
: this(File(path), llvmModule)
override fun addKotlinBitcode(llvmModule: LLVMModuleRef) {
// This is a noop for .kt.bc based libraries,
// because the bitcode itself is the container.
}
override fun addLinkData(linkData: LinkData) {
MetadataGenerator(llvmModule).addLinkData(linkData)
}
override fun addNativeBitcode(library: String) {
val libraryModule = parseBitcodeFile(library)
val failed = LLVMLinkModules2(llvmModule, libraryModule)
if (failed != 0) {
throw Error("failed to link $library") // TODO: retrieve error message from LLVM.
}
}
override fun commit() {
LLVMWriteBitcodeToFile(llvmModule, file.path)
}
}
class SplitLibraryWriter(val libDir: File, target: String, val nopack: Boolean = false): FileBasedLibraryWriter(libDir) {
public constructor(path: String, target: String, nopack: Boolean): this(File(path), target, nopack)
val klibFile = File("${libDir.path}.klib")
val linkdataDir = File(libDir, "linkdata")
val resourcesDir = File(libDir, "resources")
val targetDir = File(libDir, target)
val kotlinDir = File(targetDir, "kotlin")
val nativeDir = File(targetDir, "native")
// TODO: Experiment with separate bitcode files.
// Per package or per class.
val mainBitcodeFile = File(kotlinDir, "program.kt.bc")
override val mainBitcodeFileName = mainBitcodeFile.path
init {
// TODO: figure out the proper policy here.
libDir.deleteRecursively()
klibFile.delete()
libDir.mkdirs()
linkdataDir.mkdirs()
targetDir.mkdirs()
kotlinDir.mkdirs()
nativeDir.mkdirs()
resourcesDir.mkdirs()
}
var llvmModule: LLVMModuleRef? = null
override fun addKotlinBitcode(llvmModule: LLVMModuleRef) {
this.llvmModule = llvmModule
LLVMWriteBitcodeToFile(llvmModule, mainBitcodeFileName)
}
override fun addLinkData(linkData: LinkData) {
SplitMetadataGenerator(linkdataDir).addLinkData(linkData)
}
override fun addNativeBitcode(library: String) {
val basename = File(library).getName()
File(library).copyTo(File(nativeDir, basename))
}
override fun commit() {
if (!nopack) {
// This is no-op for the Split library.
// Or should we zip the directory?
libDir.zipDirAs(klibFile)
libDir.deleteRecursively()
}
}
}
@@ -22,6 +22,7 @@ import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.KonanVersion
import org.jetbrains.kotlin.backend.konan.TargetManager
import org.jetbrains.kotlin.backend.konan.util.File
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -94,7 +95,15 @@ internal fun String?.toFileAndFolder():FileAndFolder {
internal fun generateDebugInfoHeader(context: Context) {
if (context.shouldContainDebugInfo()) {
val path = context.config.configuration.get(KonanConfigKeys.BITCODE_FILE).toFileAndFolder()
val path = with(context.config.configuration) {
if (!getBoolean(KonanConfigKeys.NOLINK)) {
get(KonanConfigKeys.EXECUTABLE_FILE)!!
} else {
get(KonanConfigKeys.LIBRARY_NAME)!!
}
}.toFileAndFolder()
context.debugInfo.module = DICreateModule(
builder = context.debugInfo.builder,
scope = context.llvmModule as DIScopeOpaqueRef,
@@ -23,9 +23,14 @@ import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.KonanPhase
import org.jetbrains.kotlin.backend.konan.LinkData
import org.jetbrains.kotlin.backend.konan.PhaseManager
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.TargetManager
import org.jetbrains.kotlin.backend.konan.KonanLibraryWriter
import org.jetbrains.kotlin.backend.konan.KtBcLibraryWriter
import org.jetbrains.kotlin.backend.konan.SplitLibraryWriter
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -52,7 +57,9 @@ import org.jetbrains.kotlin.types.typeUtil.isUnit
internal fun emitLLVM(context: Context) {
val config = context.config.configuration
val irModule = context.irModule!!
// Note that we don't set module target explicitly.
// It is determined by the target of runtime.bc
// (see Llvm class in ContextUtils)
@@ -61,7 +68,6 @@ internal fun emitLLVM(context: Context) {
val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose
context.llvmModule = llvmModule
context.debugInfo.builder = DICreateBuilder(llvmModule)
context.llvmDeclarations = createLlvmDeclarations(context)
val phaser = PhaseManager(context)
@@ -70,33 +76,74 @@ internal fun emitLLVM(context: Context) {
irModule.acceptVoid(RTTIGeneratorVisitor(context))
}
generateDebugInfoHeader(context)
phaser.phase(KonanPhase.CODEGEN) {
irModule.acceptVoid(CodeGeneratorVisitor(context))
}
phaser.phase(KonanPhase.METADATOR) {
irModule.acceptVoid(MetadatorVisitor(context))
}
phaser.phase(KonanPhase.BITCODE_LINKER) {
for (library in context.config.nativeLibraries) {
val libraryModule = parseBitcodeFile(library)
val failed = LLVMLinkModules2(llvmModule, libraryModule)
if (failed != 0) {
throw Error("failed to link $library") // TODO: retrieve error message from LLVM.
}
}
}
if (context.shouldContainDebugInfo()) {
DIFinalize(context.debugInfo.builder)
}
LLVMWriteBitcodeToFile(llvmModule, context.config.configuration.get(KonanConfigKeys.BITCODE_FILE)!!)
if (!config.getBoolean(KonanConfigKeys.NOLINK)) {
val program = config.get(KonanConfigKeys.PROGRAM_NAME)!!
val output = "$program}.kt.bc"
context.bitcodeFileName = output
phaser.phase(KonanPhase.BITCODE_LINKER) {
for (library in context.config.nativeLibraries) {
val libraryModule = parseBitcodeFile(library)
val failed = LLVMLinkModules2(llvmModule, libraryModule)
if (failed != 0) {
throw Error("failed to link $library") // TODO: retrieve error message from LLVM.
}
}
}
LLVMWriteBitcodeToFile(llvmModule, output)
} else {
val libraryName = config.get(KonanConfigKeys.LIBRARY_NAME)!!
val nopack = config.getBoolean(KonanConfigKeys.NOPACK)
val targetName = context.config.targetManager.currentName
val library = buildLibrary(
phaser,
context.config.nativeLibraries,
context.serializedLinkData!!,
targetName,
libraryName,
llvmModule,
nopack)
context.library = library
context.bitcodeFileName =
library.mainBitcodeFileName
}
}
internal fun buildLibrary(phaser: PhaseManager, natives: List<String>, linkData: LinkData, target: String, output: String, llvmModule: LLVMModuleRef, nopack: Boolean): KonanLibraryWriter {
// TODO: May be we need a factory?
//val library = KtBcLibraryWriter(output, llvmModule)
val library = SplitLibraryWriter(output, target, nopack)
library.addKotlinBitcode(llvmModule)
phaser.phase(KonanPhase.METADATOR) {
library.addLinkData(linkData)
}
phaser.phase(KonanPhase.BITCODE_LINKER) {
natives.forEach {
library.addNativeBitcode(it)
}
}
library.commit()
return library
}
internal fun verifyModule(llvmModule: LLVMModuleRef, current: String = "") {
memScoped {
@@ -134,19 +181,6 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
//-------------------------------------------------------------------------//
internal class MetadatorVisitor(val context: Context) : IrElementVisitorVoid {
val metadator = MetadataGenerator(context)
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitModuleFragment(module: IrModuleFragment) {
module.acceptChildrenVoid(this)
metadator.endModule(module)
}
}
/**
* Defines how to generate context-dependent operations.
@@ -20,13 +20,51 @@ import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.LinkData
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import java.io.Closeable
import java.io.File
import kotlin.io.readText
import java.util.Properties
class NamedModuleData(val name:String, val base64: String)
fun MetadataReader.loadSerializedModule(currentAbiVersion: Int): NamedModuleData {
interface MetadataReader {
fun loadSerializedModule(currentAbiVersion: Int): NamedModuleData
fun loadSerializedPackageFragment(fqName: String): String
}
class SplitMetadataReader(file: File) : MetadataReader {
val linkDataDir = File(file, "linkdata")
override fun loadSerializedModule(currentAbiVersion: Int): NamedModuleData {
val header = Properties()
val file = File(linkDataDir, "module")
file.bufferedReader().use { reader ->
header.load(reader)
}
val headerAbiVersion = header.getProperty("abi_version")!!
val moduleName = header.getProperty("module_name")!!
val moduleData = header.getProperty("module_data")!!
if ("$currentAbiVersion" != headerAbiVersion)
error("ABI version mismatch. Compiler expects: $currentAbiVersion, the library is $headerAbiVersion")
return NamedModuleData(moduleName, moduleData)
}
override fun loadSerializedPackageFragment(fqName: String): String {
val realName = if (fqName == "") "<root>" else fqName
return File(linkDataDir, realName).readText()
}
}
class KtBcMetadataReader(file: File) : Closeable, MetadataReader {
override fun loadSerializedModule(currentAbiVersion: Int): NamedModuleData {
val (nodeCount, kmetadataNodeArg) = namedMetadataNode("kmetadata")
if (nodeCount != 1) {
@@ -48,7 +86,7 @@ fun MetadataReader.loadSerializedModule(currentAbiVersion: Int): NamedModuleData
return NamedModuleData(moduleName, tableOfContentsAsString)
}
fun MetadataReader.loadSerializedPackageFragment(fqName: String): String {
override fun loadSerializedPackageFragment(fqName: String): String {
val (nodeCount, kpackageNodeArg) = namedMetadataNode("kpackage:$fqName")
if (nodeCount != 1) {
@@ -61,8 +99,6 @@ fun MetadataReader.loadSerializedPackageFragment(fqName: String): String {
return base64
}
class MetadataReader(file: File) : Closeable {
lateinit var llvmModule: LLVMModuleRef
lateinit var llvmContext: LLVMContextRef
@@ -141,7 +177,7 @@ class MetadataReader(file: File) : Closeable {
}
}
internal class MetadataGenerator(override val context: Context): ContextUtils {
internal class MetadataGenerator(val llvmModule: LLVMModuleRef) {
private fun metadataNode(args: List<LLVMValueRef?>): LLVMValueRef {
return LLVMMDNode(args.toCValues(), args.size)!!
@@ -154,38 +190,52 @@ internal class MetadataGenerator(override val context: Context): ContextUtils {
}
private fun emitModuleMetadata(name: String, md: LLVMValueRef?) {
LLVMAddNamedMetadataOperand(context.llvmModule, name, md)
LLVMAddNamedMetadataOperand(llvmModule, name, md)
}
private fun metadataString(str: String): LLVMValueRef {
return LLVMMDString(str, str.length)!!
}
private fun addLinkData(module: IrModuleFragment) {
val linkData = context.serializedLinkData
fun addLinkData(linkData: LinkData) {
if (linkData == null) return
val abiVersion = context.config.configuration.get(KonanConfigKeys.ABI_VERSION)
val abiNode = metadataString("$abiVersion")
val moduleName = metadataString(module.descriptor.name.asString())
val abiNode = metadataString("${linkData.abiVersion}")
val moduleNameNode = metadataString(linkData.moduleName)
val module = linkData.module
val fragments = linkData.fragments
val fragmentNames = linkData.fragmentNames
val dataNode = metadataString(module)
val kmetadataArg = metadataNode(listOf(abiNode, moduleName, dataNode))
val kmetadataArg = metadataNode(listOf(abiNode, moduleNameNode, dataNode))
emitModuleMetadata("kmetadata", kmetadataArg)
fragments.forEachIndexed { index, it ->
val name = fragmentNames.get(index)
val name = fragmentNames[index]
val dataNode = metadataString(it)
val kpackageArg = metadataNode(listOf(dataNode))
emitModuleMetadata("kpackage:$name", kpackageArg)
}
}
}
internal fun endModule(module: IrModuleFragment) {
addLinkData(module)
internal class SplitMetadataGenerator(val file: File) {
fun addLinkData(linkData: LinkData) {
val header = Properties()
header.putAll(hashMapOf(
"abi_version" to "${linkData.abiVersion}",
"module_name" to "${linkData.moduleName}",
"module_data" to "${linkData.module}"
))
header.store(File(file, "module").outputStream(), null)
linkData.fragments.forEachIndexed { index, it ->
val name = linkData.fragmentNames[index]
val realName = if (name == "") "<root>" else name
File(file, realName).writeText(it)
}
}
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.LinkData
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.llvm.base64Decode
import org.jetbrains.kotlin.backend.konan.llvm.base64Encode
import org.jetbrains.kotlin.backend.konan.llvm.isExported
@@ -274,7 +275,9 @@ internal class KonanSerializationUtil(val context: Context) {
}
val libraryAsByteArray = libraryProto.build().toByteArray()
val library = byteArrayToBase64(libraryAsByteArray)
return LinkData(library, fragments, fragmentNames)
val abiVersion = context.config.configuration.get(KonanConfigKeys.ABI_VERSION)!!
val moduleName = moduleDescriptor.name.asString()
return LinkData(abiVersion, library, moduleName, fragments, fragmentNames)
}
}
+52 -26
View File
@@ -67,8 +67,9 @@ void setupCompilationFlags() {
final String binDir
if (isLinux()) {
ext.host = "linux"
ext.targetArgs <<
["host":
[(ext.host):
["--sysroot=${gccToolchainDir}/${gnuTriplet}/sysroot"],
"raspberrypi":
["-target", "armv7-unknown-linux-gnueabihf",
@@ -79,9 +80,10 @@ void setupCompilationFlags() {
"-I$raspberryPiSysrootDir/usr/include/c++/4.8.3/arm-linux-gnueabihf"]
]
} else {
ext.host = "macbook"
ext.targetArgs <<
["host":
["--sysroot=$hostSysrootDir", "-mmacosx-version-min=$minMacOsVersion"],
[(ext.host):
["--sysroot=$macbookSysrootDir", "-mmacosx-version-min=$minMacOsVersion"],
"iphone":
["-stdlib=libc++", "-arch", "arm64", "-isysroot", "$iphoneSysrootDir",
"-miphoneos-version-min=8.0.0"]
@@ -99,11 +101,11 @@ void setupCompilationFlags() {
"-L$llvmDir/lib"
])
} else {
binDir = "$hostSysrootDir/usr/bin"
binDir = "$macbookSysrootDir/usr/bin"
}
ext.clangArgs << "-B$binDir"
ext.hostClangArgs = ext.clangArgs.clone()
ext.hostClangArgs.addAll(ext.targetArgs['host'])
ext.hostClangArgs.addAll(ext.targetArgs[host])
ext.clangPath << binDir
ext.jvmArgs = ["-ea"]
@@ -164,7 +166,12 @@ class PlatformInfo {
}
}
task dist_compiler(type: Copy) {
task dist_compiler(dependsOn: "distCompiler")
task dist_runtime(dependsOn: "distRuntime")
task cross_dist(dependsOn: "crossDist")
task list_dist(dependsOn: "listDist")
task distCompiler(type: Copy) {
dependsOn ':backend.native:jars'
dependsOn ':tools:helpers:jar'
@@ -221,46 +228,65 @@ task dist_compiler(type: Copy) {
}
}
task list_dist(type: Exec) {
task listDist(type: Exec) {
commandLine 'find', 'dist'
}
task dist_runtime(type: Copy) {
dependsOn ':runtime:hostRuntime'
dependsOn ':backend.native:hostStdlib'
dependsOn ':backend.native:hostStart'
task distRuntime(type: Copy) {
dependsOn "${host}CrossDistRuntime"
dependsOn('commonDistRuntime')
}
task commonDistRuntime(type: Copy) {
destinationDir file('dist')
from(project(':runtime').file('build')) {
include('*/*.bc')
into('lib')
// Target independant common part.
from(project(':runtime').file("build/${host}Stdlib")) {
include('**')
into('klib/stdlib')
}
}
task cross_dist_runtime(type: Copy) {
dependsOn.addAll(targetList.collect { ":runtime:${it}Runtime" })
dependsOn.addAll(targetList.collect { ":backend.native:${it}Stdlib" })
dependsOn.addAll(targetList.collect { ":backend.native:${it}Start" })
task crossDistRuntime(type: Copy) {
dependsOn.addAll(targetList.collect { "${it}CrossDistRuntime" })
dependsOn('commonDistRuntime')
}
destinationDir file('dist')
targetList.each { target ->
task("${target}CrossDistRuntime", type: Copy) {
dependsOn ":runtime:${target}Runtime"
dependsOn ":backend.native:${target}Stdlib"
dependsOn ":backend.native:${target}Start"
from(project(':runtime').file('build')) {
include('*/*.bc')
into('lib')
destinationDir file('dist')
from(project(':runtime').file("build/$target")) {
include("*.bc")
eachFile {println("eachfile= $it")}
into("klib/stdlib/$target/native")
}
from(project(':runtime').file("build/${target}Stdlib")) {
include('**')
into('klib/stdlib')
}
from(project(':runtime').file("build/${target}Start/$target/kotlin")) {
include("program.kt.bc")
rename('program.kt.bc', 'start.kt.bc')
into("klib/stdlib/$target/native")
}
}
}
task dist {
dependsOn 'dist_compiler', 'dist_runtime', ':tools:gradle-plugin:dist'
dependsOn 'distCompiler', 'distRuntime', ':tools:gradle-plugin:dist'
}
task cross_dist {
dependsOn 'cross_dist_runtime', 'dist'
task crossDist {
dependsOn 'crossDistRuntime', 'distCompiler'
}
task bundle(type: Tar) {
dependsOn('cross_dist')
dependsOn('crossDist')
baseName = "kotlin-native-${simpleOsName()}-${project.konanVersion}"
from("$project.rootDir/dist") {
include '**'
@@ -117,7 +117,7 @@ abstract class KonanTest extends JavaExec {
}
String buildExePath() {
def exeName = project.file(source).name.replace(".kt", ".kt.exe")
def exeName = project.file(source).name.replace(".kt", "")
return "$outputDirectory/$exeName"
}
@@ -206,9 +206,10 @@ fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation<Any?> = ob
@TaskAction
void executeTest() {
createOutputDirectory()
def exe = buildExePath()
def program = buildExePath()
def exe = "${program}.kexe"
compileTest(buildCompileList(), exe)
compileTest(buildCompileList(), program)
if (!run) {
println "to be executed manually: $exe"
+1 -1
View File
@@ -27,7 +27,7 @@ targetList.each { targetName ->
}
task build {
dependsOn hostHash
dependsOn "${host}Hash"
}
task clean << {
+12 -11
View File
@@ -178,16 +178,14 @@ class HelperNativeDep extends TgzNativeDep {
}
}
task hostLibffi(type: HelperNativeDep) {
baseName = "libffi-3.2.1-2-$host"
}
task llvm(type: HelperNativeDep) {
baseName = "clang-llvm-$llvmVersion-$host"
}
if (isLinux()) {
task linuxLibffi(type: HelperNativeDep) {
baseName = "libffi-3.2.1-2-$host"
}
task gccToolchain(type: HelperNativeDep) {
baseName = "target-gcc-toolchain-3-$host"
}
@@ -198,7 +196,10 @@ if (isLinux()) {
baseName = "libffi-3.2.1-2-raspberrypi"
}
} else {
task hostSysroot(type: HelperNativeDep) {
task macbookLibffi(type: HelperNativeDep) {
baseName = "libffi-3.2.1-2-$host"
}
task macbookSysroot(type: HelperNativeDep) {
baseName = "target-sysroot-1-$host"
}
task iphoneSysroot(type: HelperNativeDep) {
@@ -207,11 +208,11 @@ if (isLinux()) {
task iphoneLibffi(type: HelperNativeDep) {
baseName = "libffi-3.2.1-2-darwin-ios"
}
// TODO: re-enable when we known how to bring the simulator
// sysroot to dependencies.
// task iphoneSimSysroot(type: TgzNativeDep) {
// baseName = "target-sysroot-1-darwin-ios-sim"
// }
// TODO: re-enable when we known how to bring the simulator
// sysroot to dependencies.
// task iphoneSimSysroot(type: TgzNativeDep) {
// baseName = "target-sysroot-1-darwin-ios-sim"
// }
}
+2
View File
@@ -43,6 +43,8 @@ targetList.each { targetName ->
}
}
task hostRuntime(dependsOn: "${rootProject.ext.host}Runtime")
task clean << {
delete buildDir
}