Collect dependent libraries in the manifest.

Link dependencies recursively.
This commit is contained in:
Alexander Gorshenev
2017-09-26 17:08:36 +03:00
committed by alexander-gorshenev
parent 3b1428597b
commit 42cbf8428b
13 changed files with 148 additions and 77 deletions
+2 -2
View File
@@ -162,7 +162,7 @@ targetList.each { target ->
"-Djava.library.path=${project.buildDir}/nativelibs",
"-Dfile.encoding=UTF-8"]
def defaultArgs = ['-nopack', '-nostdlib','-ea' ]
def defaultArgs = ['-nopack', '-nostdlib', '-nodefaultlibs', '-ea' ]
if (target != "wasm32") defaultArgs += '-g'
def konanArgs = [*defaultArgs,
'-target', target,
@@ -176,7 +176,7 @@ targetList.each { target ->
jvmArgs = konanJvmArgs
args = [*konanArgs,
'-output', project(':runtime').file("build/${target}Stdlib"),
'-produce', 'library',
'-produce', 'library', '-module_name', 'stdlib',
project(':Interop:Runtime').file('src/main/kotlin'),
project(':Interop:Runtime').file('src/native/kotlin'),
project(':runtime').file('src/main/kotlin')]
@@ -86,7 +86,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
arguments.libraries.toNonNullList())
put(LINKER_ARGS, arguments.linkerArguments.toNonNullList())
arguments.moduleName ?. let{ put(MODULE_NAME, it) }
arguments.target ?.let{ put(TARGET, it) }
put(INCLUDED_BINARY_FILES,
@@ -45,6 +45,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-manifest", valueDescription = "<path>", description = "Provide a maniferst addend file")
var manifestFile: String? = null
@Argument(value = "-module_name", valueDescription = "<name>", description = "Spicify a name for the compilation module")
var moduleName: String? = null
@Argument(value = "-nativelibrary", shortName = "-nl", valueDescription = "<path>", description = "Include the native bitcode library")
var nativeLibraries: Array<String>? = null
@@ -0,0 +1,87 @@
/*
* 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.LLVMWriteBitcodeToFile
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
internal fun produceOutput(context: Context) {
val llvmModule = context.llvmModule!!
val config = context.config.configuration
when (config.get(KonanConfigKeys.PRODUCE)) {
CompilerOutputKind.PROGRAM -> {
val program = context.config.outputName
val output = "$program.kt.bc"
context.bitcodeFileName = output
PhaseManager(context).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)
}
CompilerOutputKind.LIBRARY -> {
val output = context.config.outputName
val libraryName = context.config.moduleId
val neededLibraries
= context.config.immediateLibraries.purgeUnneeded()
val abiVersion = context.config.currentAbiVersion
val target = context.config.targetManager.target
val nopack = config.getBoolean(KonanConfigKeys.NOPACK)
val manifest = config.get(KonanConfigKeys.MANIFEST_FILE)
val library = buildLibrary(
context.config.nativeLibraries,
context.config.includeBinaries,
neededLibraries,
context.serializedLinkData!!,
abiVersion,
target,
output,
libraryName,
llvmModule,
nopack,
manifest,
context.moduleEscapeAnalysisResult.build().toByteArray())
context.library = library
context.bitcodeFileName = library.mainBitcodeFileName
}
CompilerOutputKind.BITCODE -> {
val output = context.config.outputFile
context.bitcodeFileName = output
LLVMWriteBitcodeToFile(llvmModule, output)
}
}
}
internal fun List<KonanLibraryReader>.purgeUnneeded(): List<KonanLibraryReader> {
return this.map {
if (!it.isNeededForLink && it.isDefaultLink) null else it
}.filterNotNull()
}
@@ -71,15 +71,15 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
val outputFile = outputName.suffixIfNot(produce.suffix(targetManager.target))
val moduleId: String
// This is a decision we could change
get() = outputName
get() = configuration.get(KonanConfigKeys.MODULE_NAME) ?: File(outputName).name
private val libraryNames: List<String>
get() = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
private val repositories = configuration.getList(KonanConfigKeys.REPOSITORIES)
private val resolver = KonanLibrarySearchPathResolver(repositories, distribution.klib, distribution.localKonanDir)
val libraries: List<KonanLibraryReader> by lazy {
val immediateLibraries: List<LibraryReaderImpl> by lazy {
val target = targetManager.target
val defaultLibraries = resolver.defaultLinks(
@@ -91,10 +91,25 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
.map { resolver.resolve(it) }
.map{ LibraryReaderImpl(it, currentAbiVersion, target) }
val resolvedLibraries = defaultLibraries + userProvidedLibraries
checkLibraryDuplicates(resolvedLibraries.map{it.libraryFile})
var resolvedLibraries = defaultLibraries + userProvidedLibraries
warnOnLibraryDuplicates(resolvedLibraries.map{ it.libraryFile })
resolvedLibraries.distinctBy { it.libraryFile.absolutePath }
}
resolvedLibraries
val libraries: List<LibraryReaderImpl> by lazy {
val result = mutableListOf<LibraryReaderImpl>()
result.addAll(immediateLibraries)
do {
val dependencies = result
.map { it.dependencies } .flatten()
.map { resolver.resolve(it) }
.map { LibraryReaderImpl(it, currentAbiVersion, targetManager.target) }
val newDependencies = dependencies.deleteMatching(result, { it.libraryFile.absolutePath })
result.addAll(newDependencies)
} while (newDependencies.size > 0)
result
}
private val loadedDescriptors = loadLibMetadata()
@@ -144,7 +159,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
loadedDescriptors
}
private fun checkLibraryDuplicates(resolvedLibraries: List<File>) {
private fun warnOnLibraryDuplicates(resolvedLibraries: List<File>) {
val duplicates = resolvedLibraries.groupBy { it.absolutePath } .values.filter { it.size > 1 }
duplicates.forEach {
configuration.report(STRONG_WARNING, "library included more than once: ${it.first().absolutePath}")
@@ -152,5 +167,10 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
}
}
private fun <T, K> List<T>.deleteMatching(other: List<T>, transform: (T) -> K): List<T> {
val transformed = other.map { transform(it) }
return this.filterNot { transformed.contains( transform(it) ) }
}
fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String)
= this.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(priority, message)
@@ -55,6 +55,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("generate metadata")
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
= CompilerConfigurationKey.create("module kind")
val MODULE_NAME: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("module name")
val NATIVE_LIBRARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("native library file paths")
val NODEFAULTLIBS: CompilerConfigurationKey<Boolean>
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.backend.konan.ir.DeserializerDriver
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
import org.jetbrains.kotlin.backend.konan.llvm.emitLLVM
import org.jetbrains.kotlin.backend.konan.llvm.produceOutput
import org.jetbrains.kotlin.backend.konan.serialization.KonanSerializationUtil
import org.jetbrains.kotlin.backend.konan.serialization.markBackingFields
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
@@ -235,11 +235,7 @@ internal class LinkStage(val context: Context) {
private val debug = config.get(KonanConfigKeys.DEBUG) ?: false
private val nomain = config.get(KonanConfigKeys.NOMAIN) ?: false
private val emitted = context.bitcodeFileName
private val libraries = context.config.libraries
.map {
if (!it.isNeededForLink && it.isDefaultLink) null else it
}.filterNotNull()
private val libraries = context.config.libraries.purgeUnneeded()
private fun MutableList<String>.addNonEmpty(elements: List<String>) {
addAll(elements.filter { !it.isEmpty() })
}
@@ -22,9 +22,11 @@ import org.jetbrains.kotlin.konan.properties.Properties
interface KonanLibraryReader {
val libraryName: String
val uniqueName: String
val bitcodePaths: List<String>
val includedPaths: List<String>
val linkerOpts: List<String>
val dependencies: List<String>
val escapeAnalysis: ByteArray?
val isNeededForLink: Boolean get() = true
val isDefaultLink: Boolean get() = false
@@ -23,6 +23,7 @@ interface KonanLibraryWriter {
fun addNativeBitcode(library: String)
fun addIncludedBinary(library: String)
fun addKotlinBitcode(llvmModule: LLVMModuleRef)
fun addLinkDependencies(libraries: List<KonanLibraryReader>)
fun addManifestAddend(path: String)
fun addEscapeAnalysis(escapeAnalysis: ByteArray)
val mainBitcodeFileName: String
@@ -56,6 +56,9 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int,
override val libraryName
get() = inPlace.libraryName
override val uniqueName
get() = manifestProperties.propertyString("unique_name")!!
override val bitcodePaths: List<String>
get() = (realFiles.kotlinDir.listFiles + realFiles.nativeDir.listFiles).map{it.absolutePath}
@@ -65,6 +68,9 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int,
override val linkerOpts: List<String>
get() = manifestProperties.propertyList("linkerOpts", target!!.detailedName)
override val dependencies: List<String>
get() = manifestProperties.propertyList("dependencies")
val moduleHeaderData: ByteArray by lazy {
reader.loadSerializedModule()
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.backend.konan.library.impl
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import llvm.LLVMModuleRef
import llvm.LLVMWriteBitcodeToFile
import org.jetbrains.kotlin.backend.konan.library.KonanLibrary
@@ -29,13 +30,13 @@ abstract class FileBasedLibraryWriter (
val file: File, val currentAbiVersion: Int): KonanLibraryWriter {
}
class LibraryWriterImpl(override val libDir: File, currentAbiVersion: Int,
class LibraryWriterImpl(override val libDir: File, moduleName: String, currentAbiVersion: Int,
override val target: KonanTarget?, val nopack: Boolean = false):
FileBasedLibraryWriter(libDir, currentAbiVersion), KonanLibrary {
public constructor(path: String, currentAbiVersion: Int,
public constructor(path: String, moduleName: String, currentAbiVersion: Int,
target:KonanTarget?, nopack: Boolean):
this(File(path), currentAbiVersion, target, nopack)
this(File(path), moduleName, currentAbiVersion, target, nopack)
override val libraryName = libDir.path
val klibFile
@@ -58,6 +59,8 @@ class LibraryWriterImpl(override val libDir: File, currentAbiVersion: Int,
nativeDir.mkdirs()
includedDir.mkdirs()
resourcesDir.mkdirs()
// TODO: <name>:<hash> will go somewhere around here.
manifestProperties.setProperty("unique_name", "$moduleName")
manifestProperties.setProperty("abi_version", "$currentAbiVersion")
}
@@ -82,6 +85,11 @@ class LibraryWriterImpl(override val libDir: File, currentAbiVersion: Int,
File(library).copyTo(File(includedDir, basename))
}
override fun addLinkDependencies(libraries: List<KonanLibraryReader>) {
if (libraries.isEmpty()) return
manifestProperties.setProperty("dependencies", libraries .map { it.uniqueName } . joinToString(" "))
}
override fun addManifestAddend(path: String) {
val properties = File(path).loadProperties()
manifestProperties.putAll(properties)
@@ -102,17 +110,19 @@ class LibraryWriterImpl(override val libDir: File, currentAbiVersion: Int,
internal fun buildLibrary(
natives: List<String>,
included: List<String>,
included: List<String>,
linkDependencies: List<KonanLibraryReader>,
linkData: LinkData,
abiVersion: Int,
target: KonanTarget,
output: String,
moduleName: String,
llvmModule: LLVMModuleRef,
nopack: Boolean,
manifest: String?,
escapeAnalysis: ByteArray?): KonanLibraryWriter {
val library = LibraryWriterImpl(output, abiVersion, target, nopack)
val library = LibraryWriterImpl(output, moduleName, abiVersion, target, nopack)
library.addKotlinBitcode(llvmModule)
library.addLinkData(linkData)
@@ -122,6 +132,7 @@ internal fun buildLibrary(
included.forEach {
library.addIncludedBinary(it)
}
library.addLinkDependencies(linkDependencies)
manifest ?.let { library.addManifestAddend(it) }
escapeAnalysis?.let { library.addEscapeAnalysis(it) }
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -39,7 +38,6 @@ import org.jetbrains.kotlin.ir.util.getArguments
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -52,7 +50,6 @@ import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal fun emitLLVM(context: Context) {
val irModule = context.irModule!!
@@ -89,59 +86,6 @@ internal fun emitLLVM(context: Context) {
}
}
internal fun produceOutput(context: Context) {
val llvmModule = context.llvmModule!!
val config = context.config.configuration
when (config.get(KonanConfigKeys.PRODUCE)) {
CompilerOutputKind.PROGRAM -> {
val program = context.config.outputName
val output = "$program.kt.bc"
context.bitcodeFileName = output
PhaseManager(context).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)
}
CompilerOutputKind.LIBRARY -> {
val libraryName = context.config.outputName
val abiVersion = context.config.currentAbiVersion
val target = context.config.targetManager.target
val nopack = config.getBoolean(KonanConfigKeys.NOPACK)
val manifest = config.get(KonanConfigKeys.MANIFEST_FILE)
val library = buildLibrary(
context.config.nativeLibraries,
context.config.includeBinaries,
context.serializedLinkData!!,
abiVersion,
target,
libraryName,
llvmModule,
nopack,
manifest,
context.moduleEscapeAnalysisResult.build().toByteArray())
context.library = library
context.bitcodeFileName = library.mainBitcodeFileName
}
CompilerOutputKind.BITCODE -> {
val output = context.config.outputFile
context.bitcodeFileName = output
LLVMWriteBitcodeToFile(llvmModule, output)
}
}
}
internal fun verifyModule(llvmModule: LLVMModuleRef, current: String = "") {
memScoped {
val errorRef = allocPointerTo<ByteVar>()