Auto link everything found in dist/klib.

Disable such behavior with -nodefaultlibs.
Omit libraries from the link stage if their packages have not been referenced.
This commit is contained in:
Alexander Gorshenev
2017-09-15 15:42:54 +03:00
committed by alexander-gorshenev
parent 5d79c079b4
commit 27f7e4263b
10 changed files with 79 additions and 20 deletions
@@ -78,6 +78,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
with(KonanConfigKeys) {
with(configuration) {
put(NODEFAULTLIBS, arguments.nodefaultlibs)
put(NOSTDLIB, arguments.nostdlib)
put(NOPACK, arguments.nopack)
put(NOMAIN, arguments.nomain)
@@ -48,6 +48,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-nativelibrary", shortName = "-nl", valueDescription = "<path>", description = "Include the native bitcode library")
var nativeLibraries: Array<String>? = null
@Argument(value = "-nodefaultlibs", description = "Don't link the libraries from dist/klib automatically")
var nodefaultlibs: Boolean = false
@Argument(value = "-nomain", description = "Assume 'main' entry point to be provided by external libraries")
var nomain: Boolean = false
@@ -75,26 +75,26 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
get() = outputName
private val libraryNames: List<String>
get() {
val fromCommandLine = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
if (configuration.get(KonanConfigKeys.NOSTDLIB) ?: false) {
return fromCommandLine
}
return fromCommandLine + "stdlib"
}
get() = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
private val repositories = configuration.getList(KonanConfigKeys.REPOSITORIES)
private val resolver = KonanLibrarySearchPathResolver(repositories, distribution.klib, distribution.localKonanDir)
private val librariesFound: List<File> by lazy {
val resolvedLibraries = libraryNames.map{it -> resolver.resolve(it)}
checkLibraryDuplicates(resolvedLibraries)
resolvedLibraries
}
internal val libraries: List<KonanLibraryReader> by lazy {
val libraries: List<KonanLibraryReader> by lazy {
val target = targetManager.target
// Here we have chosen a particular KonanLibraryReader implementation.
librariesFound.map{it -> LibraryReaderImpl(it, currentAbiVersion, target)}
val defaultLibraries = resolver.defaultLinks(
configuration.getBoolean(KonanConfigKeys.NOSTDLIB),
configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS)
) .map { LibraryReaderImpl(it, currentAbiVersion, target, isDefaultLink=true) }
val userProvidedLibraries = libraryNames
.map { resolver.resolve(it) }
.map{ LibraryReaderImpl(it, currentAbiVersion, target) }
val resolvedLibraries = defaultLibraries + userProvidedLibraries
checkLibraryDuplicates(resolvedLibraries.map{it.libraryFile})
resolvedLibraries
}
private val loadedDescriptors = loadLibMetadata()
@@ -57,6 +57,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("module kind")
val NATIVE_LIBRARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("native library file paths")
val NODEFAULTLIBS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with the default libraries")
val NOMAIN: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries")
val NOSTDLIB: CompilerConfigurationKey<Boolean>
@@ -231,7 +231,15 @@ 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
private val libraries = context.config.libraries
.map {
if (!it.isNeededForLink) {
if (!it.isDefaultLink) {
context.reportCompilationWarning("The '${it.libraryName}' library has not been referenced. Omitted from the final link.")
}
null
} else it
}.filterNotNull()
private fun MutableList<String>.addNonEmpty(elements: List<String>) {
addAll(elements.filter { !it.isEmpty() })
@@ -26,6 +26,8 @@ interface KonanLibraryReader {
val includedPaths: List<String>
val linkerOpts: List<String>
val escapeAnalysis: ByteArray?
val isNeededForLink: Boolean get() = true
val isDefaultLink: Boolean get() = false
val manifestProperties: Properties
fun moduleDescriptor(specifics: LanguageVersionSettings): ModuleDescriptor
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.konan.file.File
interface SearchPathResolver {
val searchRoots: List<File>
fun resolve(givenPath: String): File
fun defaultLinks(nostdlib: Boolean, noDefaultLibs: Boolean): List<File>
}
class KonanLibrarySearchPathResolver(repositories: List<String>,
@@ -72,5 +73,19 @@ class KonanLibrarySearchPathResolver(repositories: List<String>,
private val File.klib
get() = File(this, "klib")
// The libraries from the default root are linked autimatically.
val defaultRoot: File?
get() = if (distHead?.exists ?: false) distHead else null
override fun defaultLinks(nostdlib: Boolean, noDefaultLibs: Boolean): List<File> {
val defaultLibs = defaultRoot?.listFiles.orEmpty()
.filterNot { it.name.removeSuffixIfPresent(".klib") == "stdlib" }
.map { File(it.absolutePath) }
val result = mutableListOf<File>()
if (!nostdlib) result.add(resolve("stdlib"))
if (!noDefaultLibs) result.addAll(defaultLibs)
return result
}
}
@@ -18,13 +18,16 @@ package org.jetbrains.kotlin.backend.konan.library.impl
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import org.jetbrains.kotlin.backend.konan.createInteropLibrary
import org.jetbrains.kotlin.backend.konan.serialization.emptyPackages
import org.jetbrains.kotlin.backend.konan.serialization.deserializeModule
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.properties.*
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.konan.target.KonanTarget
class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, val target: KonanTarget? = null) : KonanLibraryReader {
class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int,
val target: KonanTarget? = null, override val isDefaultLink: Boolean = false)
: KonanLibraryReader {
// For the zipped libraries inPlace gives files from zip file system
// whereas realFiles extracts them to /tmp.
@@ -66,8 +69,22 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, val t
reader.loadSerializedModule()
}
fun packageMetadata(fqName: String): ByteArray =
reader.loadSerializedPackageFragment(fqName)
override val isNeededForLink: Boolean
get() {
packagesAccessed.forEach {
if (!emptyPackages(moduleHeaderData).contains(it)) {
return true
}
}
return false
}
val packagesAccessed = mutableSetOf<String>()
fun packageMetadata(fqName: String): ByteArray {
packagesAccessed.add(fqName)
return reader.loadSerializedPackageFragment(fqName)
}
override fun moduleDescriptor(specifics: LanguageVersionSettings)
= deserializeModule(specifics, {packageMetadata(it)}, moduleHeaderData, createInteropLibrary(this))
@@ -76,6 +76,7 @@ message PackageFragment {
required string fq_name = 1;
required Package package = 4;
required Classes classes = 5;
required bool is_empty = 6;
// To construct name resolver
required QualifiedNameTable name_table = 2;
@@ -91,5 +92,6 @@ message Classes {
message Library {
required string module_name = 1;
repeated string package_fragment_name = 2;
repeated string empty_package = 3;
}
@@ -119,6 +119,9 @@ public fun parseModuleHeader(libraryData: ByteArray): Library =
Library.parseFrom(libraryData,
KonanSerializerProtocol.extensionRegistry)
public fun emptyPackages(libraryData: ByteArray)
= parseModuleHeader(libraryData).emptyPackageList
internal fun deserializeModule(languageVersionSettings: LanguageVersionSettings,
packageLoader:(String)->ByteArray, library: ByteArray,
interopLibrary: InteropLibrary?): ModuleDescriptorImpl {
@@ -220,6 +223,7 @@ internal class KonanSerializationUtil(val context: Context) {
val strings = serializerExtension.stringTable
val (stringTableProto, nameTableProto) = strings.buildProto()
val isEmpty = members.isEmpty() && classifierDescriptors.isEmpty()
val fragmentBuilder = KonanLinkData.PackageFragment.newBuilder()
val fragmentProto = fragmentBuilder
@@ -228,6 +232,7 @@ internal class KonanSerializationUtil(val context: Context) {
.setClasses(classesProto)
.setStringTable(stringTableProto)
.setNameTable(nameTableProto)
.setIsEmpty(isEmpty)
.build()
return fragmentProto
@@ -251,6 +256,7 @@ internal class KonanSerializationUtil(val context: Context) {
}
}
}
internal fun serializeModule(moduleDescriptor: ModuleDescriptor): LinkData {
val libraryProto = KonanLinkData.Library.newBuilder()
libraryProto.moduleName = moduleDescriptor.name.asString()
@@ -262,6 +268,9 @@ internal class KonanSerializationUtil(val context: Context) {
if (packageProto == null) return@iteration
libraryProto.addPackageFragmentName(it.asString())
if (packageProto.isEmpty) {
libraryProto.addEmptyPackage(it.asString())
}
fragments.add(packageProto.toByteArray())
fragmentNames.add(it.asString())
}