diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 70529028724..7f2e94ba94c 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -78,6 +78,7 @@ class K2Native : CLICompiler() { with(KonanConfigKeys) { with(configuration) { + put(NODEFAULTLIBS, arguments.nodefaultlibs) put(NOSTDLIB, arguments.nostdlib) put(NOPACK, arguments.nopack) put(NOMAIN, arguments.nomain) diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index 3f606fbcc97..5e64dfead59 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -48,6 +48,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-nativelibrary", shortName = "-nl", valueDescription = "", description = "Include the native bitcode library") var nativeLibraries: Array? = 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 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 944e6975683..2864a66245a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -75,26 +75,26 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration get() = outputName private val libraryNames: List - 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 by lazy { - val resolvedLibraries = libraryNames.map{it -> resolver.resolve(it)} - checkLibraryDuplicates(resolvedLibraries) - resolvedLibraries - } - - internal val libraries: List by lazy { + val libraries: List 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() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 132d01018fc..14ef94f9664 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -57,6 +57,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("module kind") val NATIVE_LIBRARY_FILES: CompilerConfigurationKey> = CompilerConfigurationKey.create("native library file paths") + val NODEFAULTLIBS: CompilerConfigurationKey + = CompilerConfigurationKey.create("don't link with the default libraries") val NOMAIN: CompilerConfigurationKey = CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries") val NOSTDLIB: CompilerConfigurationKey diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 9c4d0006075..240d42846b5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -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.addNonEmpty(elements: List) { addAll(elements.filter { !it.isEmpty() }) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt index 097c868563b..c59c6e84798 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt @@ -26,6 +26,8 @@ interface KonanLibraryReader { val includedPaths: List val linkerOpts: List val escapeAnalysis: ByteArray? + val isNeededForLink: Boolean get() = true + val isDefaultLink: Boolean get() = false val manifestProperties: Properties fun moduleDescriptor(specifics: LanguageVersionSettings): ModuleDescriptor } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/SearchPathResolver.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/SearchPathResolver.kt index 7bd4b283ce7..388d1cd30ce 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/SearchPathResolver.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/SearchPathResolver.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.konan.file.File interface SearchPathResolver { val searchRoots: List fun resolve(givenPath: String): File + fun defaultLinks(nostdlib: Boolean, noDefaultLibs: Boolean): List } class KonanLibrarySearchPathResolver(repositories: List, @@ -72,5 +73,19 @@ class KonanLibrarySearchPathResolver(repositories: List, 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 { + val defaultLibs = defaultRoot?.listFiles.orEmpty() + .filterNot { it.name.removeSuffixIfPresent(".klib") == "stdlib" } + .map { File(it.absolutePath) } + val result = mutableListOf() + if (!nostdlib) result.add(resolve("stdlib")) + if (!noDefaultLibs) result.addAll(defaultLibs) + return result + } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt index 7885660d8c9..ffce2efbc95 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt @@ -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() + + fun packageMetadata(fqName: String): ByteArray { + packagesAccessed.add(fqName) + return reader.loadSerializedPackageFragment(fqName) + } override fun moduleDescriptor(specifics: LanguageVersionSettings) = deserializeModule(specifics, {packageMetadata(it)}, moduleHeaderData, createInteropLibrary(this)) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto index 14dc20d4e77..d09842233c1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto @@ -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; } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt index 85965cf37f0..7230de0ef74 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt @@ -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()) }