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 83a489f35ba..71a4ca00965 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 @@ -24,10 +24,11 @@ import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.Services +import org.jetbrains.kotlin.konan.KonanAbiVersion import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.CompilerOutputKind -import org.jetbrains.kotlin.konan.util.profile +import org.jetbrains.kotlin.konan.util.* import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.utils.KotlinPaths @@ -142,7 +143,7 @@ class K2Native : CLICompiler() { val outputKind = CompilerOutputKind.valueOf( (arguments.produce ?: "program").toUpperCase()) put(PRODUCE, outputKind) - put(ABI_VERSION, 1) + arguments.libraryVersion ?. let { put(LIBRARY_VERSION, it) } arguments.mainPackage ?.let{ put(ENTRY, it) } arguments.manifestFile ?.let{ put(MANIFEST_FILE, it) } 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 ef81ccecfd5..5b9e8eafde1 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 @@ -30,6 +30,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-library", shortName = "-l", valueDescription = "", description = "Link with the library") var libraries: Array? = null + @Argument(value = "-library-version", shortName = "-lv", valueDescription = "", description = "Set library version") + var libraryVersion: String? = null + @Argument(value = "-list-targets", deprecatedName = "-list_targets", description = "List available hardware targets") var listTargets: Boolean = false diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index 219b339516f..0e6214a2591 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -8,6 +8,9 @@ import llvm.LLVMLinkModules2 import llvm.LLVMWriteBitcodeToFile import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.konan.library.KonanLibraryVersioning import org.jetbrains.kotlin.konan.target.CompilerOutputKind val CompilerOutputKind.isNativeBinary: Boolean get() = when (this) { @@ -62,17 +65,21 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) { val libraryName = context.config.moduleId val neededLibraries = context.llvm.librariesForLibraryManifest - val abiVersion = context.config.currentAbiVersion + val abiVersion = KonanAbiVersion.CURRENT + val compilerVersion = KonanVersion.CURRENT + val libraryVersion = config.get(KonanConfigKeys.LIBRARY_VERSION) + val versions = KonanLibraryVersioning(abiVersion = abiVersion, libraryVersion = libraryVersion, compilerVersion = compilerVersion) val target = context.config.target val nopack = config.getBoolean(KonanConfigKeys.NOPACK) val manifestProperties = context.config.manifestProperties + val library = buildLibrary( context.config.nativeLibraries, context.config.includeBinaries, neededLibraries, - context.serializedLinkData!!, - abiVersion, + context.serializedLinkData!!, + versions, target, output, libraryName, 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 72a88660033..10b3a0e3e97 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 @@ -20,11 +20,11 @@ import org.jetbrains.kotlin.konan.library.defaultResolver import org.jetbrains.kotlin.konan.library.libraryResolver import org.jetbrains.kotlin.konan.properties.loadProperties import org.jetbrains.kotlin.konan.target.* +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.library.toUnresolvedLibraries class KonanConfig(val project: Project, val configuration: CompilerConfiguration) { - val currentAbiVersion: Int = configuration.get(KonanConfigKeys.ABI_VERSION)!! - internal val distribution = Distribution( false, null, @@ -71,15 +71,18 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration private val libraryNames: List get() = configuration.getList(KonanConfigKeys.LIBRARY_FILES) + private val unresolvedLibraries = libraryNames.toUnresolvedLibraries + private val repositories = configuration.getList(KonanConfigKeys.REPOSITORIES) - private val resolver = defaultResolver(repositories, target, distribution).libraryResolver(currentAbiVersion) + private fun resolverLogger(msg: String) = configuration.report(STRONG_WARNING, msg) + + private val resolver = defaultResolver(repositories, target, distribution, ::resolverLogger).libraryResolver() internal val resolvedLibraries by lazy { resolver.resolveWithDependencies( - libraryNames, + unresolvedLibraries, noStdLib = configuration.getBoolean(KonanConfigKeys.NOSTDLIB), - noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS) ) { msg -> - configuration.report(STRONG_WARNING, msg) } + noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS) ) } fun librariesWithDependencies(moduleDescriptor: ModuleDescriptor?): List { @@ -114,3 +117,4 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String) = this.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(priority, message) + 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 d5c488c7b08..ad3dc414312 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 @@ -12,8 +12,6 @@ import org.jetbrains.kotlin.konan.target.CompilerOutputKind class KonanConfigKeys { companion object { // Keep the list lexically sorted. - val ABI_VERSION: CompilerConfigurationKey - = CompilerConfigurationKey.create("current abi version") val CHECK_DEPENDENCIES: CompilerConfigurationKey = CompilerConfigurationKey.create("check dependencies and download the missing ones") val DEBUG: CompilerConfigurationKey @@ -26,12 +24,16 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("enable backend phases") val ENTRY: CompilerConfigurationKey = CompilerConfigurationKey.create("fully qualified main() name") + val FRIEND_MODULES: CompilerConfigurationKey> + = CompilerConfigurationKey.create>("friend module paths") val GENERATE_TEST_RUNNER: CompilerConfigurationKey = CompilerConfigurationKey.create("generate test runner") val INCLUDED_BINARY_FILES: CompilerConfigurationKey> = CompilerConfigurationKey.create("included binary file paths") val LIBRARY_FILES: CompilerConfigurationKey> = CompilerConfigurationKey.create("library file paths") + val LIBRARY_VERSION: CompilerConfigurationKey + = CompilerConfigurationKey.create("library version") val LINKER_ARGS: CompilerConfigurationKey> = CompilerConfigurationKey.create("additional linker arguments") val LIST_PHASES: CompilerConfigurationKey @@ -94,8 +96,6 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("verify ir") val VERBOSE_PHASES: CompilerConfigurationKey> = CompilerConfigurationKey.create("verbose backend phases") - val FRIEND_MODULES: CompilerConfigurationKey> - = CompilerConfigurationKey.create>("friend module paths") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt index f4e299b9088..bf0662e5be1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt @@ -7,11 +7,11 @@ package org.jetbrains.kotlin.backend.konan.library import llvm.LLVMModuleRef import org.jetbrains.kotlin.konan.library.KonanLibrary -import java.util.* - -const val KLIB_CURRENT_ABI_VERSION = 1 +import org.jetbrains.kotlin.konan.library.KonanLibraryVersioning +import org.jetbrains.kotlin.konan.properties.Properties interface KonanLibraryWriter { + val versions: KonanLibraryVersioning fun addLinkData(linkData: LinkData) fun addNativeBitcode(library: String) fun addIncludedBinary(library: String) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt index 0737a0dec3b..3918c57ea9e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt @@ -11,10 +11,11 @@ import org.jetbrains.kotlin.backend.konan.library.KonanLibraryWriter import org.jetbrains.kotlin.backend.konan.library.LinkData import org.jetbrains.kotlin.konan.file.* import org.jetbrains.kotlin.konan.library.* +import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION import org.jetbrains.kotlin.konan.properties.* import org.jetbrains.kotlin.konan.target.KonanTarget -abstract class FileBasedLibraryWriter (val file: File, val currentAbiVersion: Int): KonanLibraryWriter +abstract class FileBasedLibraryWriter (val file: File): KonanLibraryWriter /** * Requires non-null [target]. @@ -22,13 +23,13 @@ abstract class FileBasedLibraryWriter (val file: File, val currentAbiVersion: In class LibraryWriterImpl( override val libDir: File, moduleName: String, - currentAbiVersion: Int, + override val versions: KonanLibraryVersioning, override val target: KonanTarget, val nopack: Boolean = false -): FileBasedLibraryWriter(libDir, currentAbiVersion), KonanLibraryLayout { +): FileBasedLibraryWriter(libDir), KonanLibraryLayout { - constructor(path: String, moduleName: String, currentAbiVersion: Int, target: KonanTarget, nopack: Boolean): - this(File(path), moduleName, currentAbiVersion, target, nopack) + constructor(path: String, moduleName: String, versions: KonanLibraryVersioning, target: KonanTarget, nopack: Boolean): + this(File(path), moduleName, versions, target, nopack) override val libraryName = libDir.path val klibFile @@ -53,7 +54,7 @@ class LibraryWriterImpl( resourcesDir.mkdirs() // TODO: : will go somewhere around here. manifestProperties.setProperty(KLIB_PROPERTY_UNIQUE_NAME, moduleName) - manifestProperties.setProperty(KLIB_PROPERTY_ABI_VERSION, currentAbiVersion.toString()) + manifestProperties.writeKonanLibraryVersioning(versions) } var llvmModule: LLVMModuleRef? = null @@ -85,6 +86,11 @@ class LibraryWriterImpl( } else { val newValue = libraries.joinToString(" ") { it.uniqueName } manifestProperties.setProperty(KLIB_PROPERTY_DEPENDS, newValue) + libraries.forEach { it -> + if (it.versions.libraryVersion != null) { + manifestProperties.setProperty("${KLIB_PROPERTY_DEPENDENCY_VERSION}_${it.uniqueName}", it.versions.libraryVersion) + } + } } } @@ -110,7 +116,7 @@ internal fun buildLibrary( included: List, linkDependencies: List, linkData: LinkData, - abiVersion: Int, + versions: KonanLibraryVersioning, target: KonanTarget, output: String, moduleName: String, @@ -119,7 +125,7 @@ internal fun buildLibrary( manifestProperties: Properties?, dataFlowGraph: ByteArray?): KonanLibraryWriter { - val library = LibraryWriterImpl(output, moduleName, abiVersion, target, nopack) + val library = LibraryWriterImpl(File(output), moduleName, versions, target, nopack) library.addKotlinBitcode(llvmModule) library.addLinkData(linkData) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index b8da0a3101a..7c8059b0c91 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2892,6 +2892,37 @@ task produce_dynamic(type: DynamicKonanTest) { "topLevel = 777 3\n" } +task library_mismatch(type: RunStandaloneKonanTest) { + def dir = buildDir.absolutePath + def lib = "$projectDir/link/versioning/empty.kt" + + def currentTarget = project.target.name + def someOtherTarget = (project.testTarget == 'wasm32' ? project.hostName : 'wasm32') + + if (!useCustomDist) { + dependsOn ':wasm32CrossDistRuntime' // we actually need any target other than the current one. + } + + doFirst { + def konancScript = isWindows() ? "konanc.bat" : "konanc" + def konanc = "$dist/bin/$konancScript" + + "$konanc $lib -p library -o $dir/1.2/empty -target $currentTarget -lv 1.2".execute().waitFor() + "$konanc $lib -p library -o $dir/3.4/empty -target $someOtherTarget".execute().waitFor() + "$konanc $lib -p library -o $dir/5.6/empty -target $currentTarget -lv 5.6".execute().waitFor() + "$konanc $lib -p library -o $dir/7.8/empty -target $currentTarget -lv 7.8".execute().waitFor() + + } + source = "link/versioning/hello.kt" + flags = ['-l', 'empty@5.6', '-r', "$dir/1.2", '-r', "$dir/3.4", '-r', "$dir/5.6"] + compilerMessages = true + def messages = + "warning: skipping $dir/1.2/empty.klib. The library versions don't match. Expected '5.6', found '1.2'\n" + + "warning: skipping $dir/3.4/empty.klib. The target doesn't match. Expected '$currentTarget', found [$someOtherTarget]\n" + + "Hello, versioned world!\n" + goldValue = isWindows() ? messages.replaceAll('\\/', '\\\\') : messages +} + if (isMac() && project.testTarget != 'wasm32') { def target = ext.platformManager.targetByName(project.testTarget ?: 'host').name diff --git a/backend.native/tests/link/versioning/empty.kt b/backend.native/tests/link/versioning/empty.kt new file mode 100644 index 00000000000..48f97f12737 --- /dev/null +++ b/backend.native/tests/link/versioning/empty.kt @@ -0,0 +1,2 @@ +/* This file has intentionally been left blank */ + diff --git a/backend.native/tests/link/versioning/hello.kt b/backend.native/tests/link/versioning/hello.kt new file mode 100644 index 00000000000..369b0870dee --- /dev/null +++ b/backend.native/tests/link/versioning/hello.kt @@ -0,0 +1,3 @@ +fun main(args: Array) { + println("Hello, versioned world!") +} diff --git a/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy b/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy index f30bebab813..c327c9e1a45 100644 --- a/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy +++ b/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy @@ -47,6 +47,7 @@ abstract class KonanTest extends JavaExec { boolean enabled = true boolean expectedFail = false boolean run = true + boolean compilerMessages = false void setDisabled(boolean value) { this.enabled = !value @@ -268,6 +269,8 @@ abstract class KonanTest extends JavaExec { } println "execution: $exe" + def compilerMessagesText = compilerMessages ? project.file("${program}.compilation.log").getText('UTF-8') : "" + def out = new ByteArrayOutputStream() //TODO Add test timeout ExecResult execResult = project.execute { @@ -284,7 +287,7 @@ abstract class KonanTest extends JavaExec { ignoreExitValue = true } - def result = out.toString("UTF-8") + def result = compilerMessagesText + out.toString("UTF-8") println(result) diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/ResolverUtils.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/ResolverUtils.kt new file mode 100644 index 00000000000..952a916620f --- /dev/null +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/ResolverUtils.kt @@ -0,0 +1,8 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolver +import org.jetbrains.kotlin.konan.library.resolver.impl.KonanLibraryResolverImpl +import org.jetbrains.kotlin.konan.KonanAbiVersion + +fun SearchPathResolverWithTarget.libraryResolver(): KonanLibraryResolver = + KonanLibraryResolverImpl(this) diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt index 1e69bae3fbd..b8efc20f5ec 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt @@ -2,23 +2,20 @@ package org.jetbrains.kotlin.konan.library.resolver import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.SearchPathResolverWithTarget - -typealias DuplicatedLibraryLogger = (String) -> Unit +import org.jetbrains.kotlin.konan.library.UnresolvedLibrary interface KonanLibraryResolver { val searchPathResolver: SearchPathResolverWithTarget - val abiVersion: Int /** * Given the list of Kotlin/Native library names, ABI version and other parameters * resolves libraries and evaluates dependencies between them. */ fun resolveWithDependencies( - libraryNames: List, + unresolvedLibraries: List, noStdLib: Boolean = false, - noDefaultLibs: Boolean = false, - logger: DuplicatedLibraryLogger? = null + noDefaultLibs: Boolean = false ): KonanLibraryResolveResult } @@ -31,7 +28,6 @@ interface KonanLibraryResolveResult { fun forEach(action: (KonanLibrary, PackageAccessedHandler) -> Unit) } - typealias LibraryOrder = (Iterable) -> List val TopologicalLibraryOrder: LibraryOrder = { input -> diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt index 314d5b2aacb..1adbb295560 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt @@ -1,11 +1,10 @@ package org.jetbrains.kotlin.konan.library.resolver import org.jetbrains.kotlin.konan.library.KonanLibrary -import org.jetbrains.kotlin.name.FqName interface PackageAccessedHandler { - fun markPackageAccessed(fqName: FqName) + fun markPackageAccessed(fqName: String) } /** diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt index 3810c94ed68..e337374bce3 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt @@ -1,24 +1,20 @@ package org.jetbrains.kotlin.konan.library.resolver.impl import org.jetbrains.kotlin.konan.file.File -import org.jetbrains.kotlin.konan.library.KonanLibrary -import org.jetbrains.kotlin.konan.library.SearchPathResolverWithTarget -import org.jetbrains.kotlin.konan.library.createKonanLibrary +import org.jetbrains.kotlin.konan.library.* import org.jetbrains.kotlin.konan.library.resolver.* -import org.jetbrains.kotlin.konan.library.unresolvedDependencies +import org.jetbrains.kotlin.konan.util.WithLogger internal class KonanLibraryResolverImpl( - override val searchPathResolver: SearchPathResolverWithTarget, - override val abiVersion: Int -): KonanLibraryResolver { + override val searchPathResolver: SearchPathResolverWithTarget +): KonanLibraryResolver, WithLogger by searchPathResolver { override fun resolveWithDependencies( - libraryNames: List, + unresolvedLibraries: List, noStdLib: Boolean, - noDefaultLibs: Boolean, - logger: DuplicatedLibraryLogger? - ) = findLibraries(libraryNames, noStdLib, noDefaultLibs) - .leaveDistinct(logger) + noDefaultLibs: Boolean + ) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs) + .leaveDistinct() .resolveDependencies() /** @@ -28,19 +24,16 @@ internal class KonanLibraryResolverImpl( * from the original library set (root set). */ private fun findLibraries( - libraryNames: List, + unresolvedLibraries: List, noStdLib: Boolean, noDefaultLibs: Boolean ): List { - val userProvidedLibraries = libraryNames.asSequence() + val userProvidedLibraries = unresolvedLibraries.asSequence() .map { searchPathResolver.resolve(it) } - .map { createKonanLibrary(it, abiVersion, searchPathResolver.target) } .toList() - val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs).map { - createKonanLibrary(it, abiVersion, searchPathResolver.target, isDefault = true) - } + val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs) // Make sure the user provided ones appear first, so that // they have precedence over defaults when duplicates are eliminated. @@ -50,14 +43,13 @@ internal class KonanLibraryResolverImpl( /** * Leaves only distinct libraries (by absolute path), warns on duplicated paths. */ - private fun List.leaveDistinct(logger: DuplicatedLibraryLogger?) = + private fun List.leaveDistinct() = this.groupBy { it.libraryFile.absolutePath }.let { groupedByAbsolutePath -> - warnOnLibraryDuplicates(groupedByAbsolutePath.filter { it.value.size > 1 }.keys, logger) + warnOnLibraryDuplicates(groupedByAbsolutePath.filter { it.value.size > 1 }.keys) groupedByAbsolutePath.map { it.value.first() } } - private fun warnOnLibraryDuplicates(duplicatedPaths: Iterable, logger: DuplicatedLibraryLogger? ) { - if (logger == null) return + private fun warnOnLibraryDuplicates(duplicatedPaths: Iterable) { duplicatedPaths.forEach { logger("library included more than once: $it") } } @@ -84,18 +76,20 @@ internal class KonanLibraryResolverImpl( do { newDependencies = newDependencies.map { library: KonanResolvedLibraryImpl -> library.library.unresolvedDependencies.asSequence() - .map { searchPathResolver.resolve(it).absoluteFile } - .mapNotNull { - if (it in cache) { - library.addDependency(cache[it]!!) + + .map { KonanResolvedLibraryImpl(searchPathResolver.resolve(it)) } + .map { resolved -> + val absoluteFile = resolved.library.libraryFile.absoluteFile + if (absoluteFile in cache) { + library.addDependency(cache[absoluteFile]!!) null } else { - val newLibrary = KonanResolvedLibraryImpl(createKonanLibrary(it, abiVersion, searchPathResolver.target)) - cache[it] = newLibrary - library.addDependency(newLibrary) - newLibrary + cache.put(absoluteFile, resolved) + library.addDependency(resolved) + resolved } - } + + }.filterNotNull() .toList() }.flatten() } while (newDependencies.isNotEmpty()) diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt index 81f8da29b2c..37c94d875f3 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt @@ -2,7 +2,6 @@ package org.jetbrains.kotlin.konan.library.resolver.impl import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.resolver.KonanResolvedLibrary -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.serialization.konan.parseModuleHeader internal class KonanResolvedLibraryImpl( @@ -23,9 +22,9 @@ internal class KonanResolvedLibraryImpl( override val isDefault: Boolean get() = library.isDefault - override fun markPackageAccessed(fqName: FqName) { + override fun markPackageAccessed(fqName: String) { if (!isNeededForLink // fast path - && !_emptyPackages.contains(fqName.asString())) { + && !_emptyPackages.contains(fqName)) { isNeededForLink = true } } diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt index 9b4aaf022da..f22189f6b36 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt @@ -35,7 +35,7 @@ class KonanPackageFragment( } val proto: KonanProtoBuf.LinkDataPackageFragment - get() = protoForNames.also { packageAccessedHandler?.markPackageAccessed(fqName) } + get() = protoForNames.also { packageAccessedHandler?.markPackageAccessed(fqName.toString()) } private val nameResolver by lazy { NameResolverImpl(protoForNames.stringTable, protoForNames.nameTable) diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt index 77a4c9b10b1..906c59b7f34 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt @@ -41,9 +41,9 @@ internal object KonanDeserializedPackageFragmentsFactoryImpl: KonanDeserializedP if (!library.isInterop) return emptyList() - val mainPackageFqName = library.packageFqName + val mainPackageFqName = library.packageFqName ?.let { FqName(it) } ?: error("Inconsistent manifest: interop library ${library.libraryName} should have `package` specified") - val exportForwardDeclarations = library.exportForwardDeclarations + val exportForwardDeclarations = library.exportForwardDeclarations.map { FqName(it) } val aliasedPackageFragments = deserializedPackageFragments.filter { it.fqName == mainPackageFqName } diff --git a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt index 042425d0bb4..6e75a3b694c 100644 --- a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt +++ b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.cli.klib // TODO: Extract `library` package as a shared jar? -import org.jetbrains.kotlin.backend.konan.library.KLIB_CURRENT_ABI_VERSION import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl @@ -15,11 +14,12 @@ import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION_WITH_DOT import org.jetbrains.kotlin.konan.library.createKonanLibrary -import org.jetbrains.kotlin.konan.library.noTargetResolver -import org.jetbrains.kotlin.konan.library.unpackZippedKonanLibraryTo +import org.jetbrains.kotlin.konan.library.resolverByName import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.konan.target.PlatformManager import org.jetbrains.kotlin.konan.util.DependencyProcessor +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.library.unpackZippedKonanLibraryTo import org.jetbrains.kotlin.konan.utils.KonanFactories.DefaultDeserializedDescriptorFactory import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf import org.jetbrains.kotlin.serialization.konan.parseModuleHeader @@ -96,31 +96,30 @@ class Library(val name: String, val requestedRepository: String?, val target: St val repository = requestedRepository?.File() ?: defaultRepository fun info() { val library = libraryInRepoOrCurrentDir(repository, name) - val reader = createKonanLibrary(library, currentAbiVersion) - val headerAbiVersion = reader.abiVersion - val moduleName = ModuleDeserializer(reader.moduleHeaderData).moduleName + val headerAbiVersion = library.versions.abiVersion + val moduleName = ModuleDeserializer(library.moduleHeaderData).moduleName println("") - println("Resolved to: ${reader.libraryName.File().absolutePath}") + println("Resolved to: ${library.libraryName.File().absolutePath}") println("Module name: $moduleName") println("ABI version: $headerAbiVersion") - val targets = reader.targetList.joinToString(", ") + val targets = library.targetList.joinToString(", ") print("Available targets: $targets\n") } fun install() { Library(File(name).name.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT), requestedRepository, target).remove(true) - val klibFile = libraryInCurrentDir(name) - val newLibDir = File(repository, klibFile.name.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT)) + val library = libraryInCurrentDir(name) + val newLibDir = File(repository, library.libraryFile.name.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT)) - klibFile.unpackZippedKonanLibraryTo(newLibDir) + library.libraryFile.unpackZippedKonanLibraryTo(newLibDir) } fun remove(blind: Boolean = false) { if (!repository.exists) error("Repository does not exist: $repository") - val libDir = try { + val library = try { val library = libraryInRepo(repository, name) if (blind) warn("Removing The previously installed $name from $repository.") library @@ -130,26 +129,26 @@ class Library(val name: String, val requestedRepository: String?, val target: St null } - libDir?.deleteRecursively() + library?.libraryFile?.deleteRecursively() } fun contents(output: Appendable = out) { val storageManager = LockBasedStorageManager() - val library = createKonanLibrary(libraryInRepoOrCurrentDir(repository, name), currentAbiVersion) + val library = libraryInRepoOrCurrentDir(repository, name) val versionSpec = LanguageVersionSettingsImpl(currentLanguageVersion, currentApiVersion) val module = DefaultDeserializedDescriptorFactory.createDescriptorAndNewBuiltIns(library, versionSpec, storageManager) val defaultModules = mutableListOf() if (!module.isKonanStdlib()) { - val resolver = noTargetResolver( + val resolver = resolverByName( emptyList(), distributionKlib = Distribution().klib, skipCurrentDir = true) resolver.defaultLinks(false, true) .mapTo(defaultModules) { DefaultDeserializedDescriptorFactory.createDescriptor( - createKonanLibrary(it, currentAbiVersion), versionSpec, storageManager, module.builtIns) + it, versionSpec, storageManager, module.builtIns) } } @@ -161,17 +160,16 @@ class Library(val name: String, val requestedRepository: String?, val target: St } } -const val currentAbiVersion = KLIB_CURRENT_ABI_VERSION val currentLanguageVersion = LanguageVersion.LATEST_STABLE val currentApiVersion = ApiVersion.LATEST_STABLE fun libraryInRepo(repository: File, name: String) = - noTargetResolver(listOf(repository.absolutePath), skipCurrentDir = true).resolve(name) + resolverByName(listOf(repository.absolutePath), skipCurrentDir = true).resolve(name) -fun libraryInCurrentDir(name: String) = noTargetResolver(emptyList()).resolve(name) +fun libraryInCurrentDir(name: String) = resolverByName(emptyList()).resolve(name) fun libraryInRepoOrCurrentDir(repository: File, name: String) = - noTargetResolver(listOf(repository.absolutePath)).resolve(name) + resolverByName(listOf(repository.absolutePath)).resolve(name) fun main(args: Array) { val command = Command(args) diff --git a/shared/build.gradle b/shared/build.gradle index e53747e93bb..0a8aca9ef1e 100644 --- a/shared/build.gradle +++ b/shared/build.gradle @@ -40,6 +40,7 @@ task generateCompilerVersion(type: VersionGenerator){} sourceSets { main.kotlin { srcDir 'src/main/kotlin' + srcDir 'src/library/kotlin' srcDir generateCompilerVersion.versionSourceDirectory } } diff --git a/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt b/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt index 9b295932cf4..1d2a2b560bb 100644 --- a/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt +++ b/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt @@ -93,6 +93,17 @@ open class VersionGenerator: DefaultTask() { | | override fun toString() = versionString |} + |fun String.parseKonanVersion(): KonanVersion { + | val (major, minor, maintenance, meta, build) = + | Regex(""${'"'}([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-(\p{Alpha}\p{Alnum}*))?(?:-([0-9]+))?""${'"'}) + | .matchEntire(this)!!.destructured + | return KonanVersionImpl( + | MetaVersion.valueOf(meta.toUpperCase()), + | major.toInt(), + | minor.toInt(), + | maintenance.toIntOrNull() ?: 0, + | build.toIntOrNull() ?: -1) + |} """.trimMargin() } versionFile.printWriter().use { diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt similarity index 81% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt index fea54118972..44786fa2e73 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt @@ -3,9 +3,11 @@ package org.jetbrains.kotlin.konan.library import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.konan.properties.propertyList -import org.jetbrains.kotlin.name.FqName const val KLIB_PROPERTY_ABI_VERSION = "abi_version" +const val KLIB_PROPERTY_COMPILER_VERSION = "compiler_version" +const val KLIB_PROPERTY_DEPENDENCY_VERSION = "dependency_version" +const val KLIB_PROPERTY_LIBRARY_VERSION = "library_version" const val KLIB_PROPERTY_UNIQUE_NAME = "unique_name" const val KLIB_PROPERTY_LINKED_OPTS = "linkerOpts" const val KLIB_PROPERTY_DEPENDS = "depends" @@ -27,7 +29,6 @@ interface KonanLibrary { // Properties: val manifestProperties: Properties - val abiVersion: String val linkerOpts: List // Paths: @@ -35,6 +36,7 @@ interface KonanLibrary { val includedPaths: List val targetList: List + val versions: KonanLibraryVersioning val dataFlowGraph: ByteArray? val moduleHeaderData: ByteArray @@ -44,21 +46,24 @@ interface KonanLibrary { val KonanLibrary.uniqueName get() = manifestProperties.getProperty(KLIB_PROPERTY_UNIQUE_NAME)!! -val KonanLibrary.unresolvedDependencies: List +val KonanLibrary.unresolvedDependencies: List get() = manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS) + .map { + UnresolvedLibrary(it, manifestProperties.getProperty("dependency_version_$it")) + } val KonanLibrary.isInterop get() = manifestProperties.getProperty(KLIB_PROPERTY_INTEROP) == "true" val KonanLibrary.packageFqName - get() = manifestProperties.getProperty(KLIB_PROPERTY_PACKAGE)?.let { FqName(it) } + get() = manifestProperties.getProperty(KLIB_PROPERTY_PACKAGE) val KonanLibrary.exportForwardDeclarations get() = manifestProperties.getProperty(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS) .split(' ').asSequence() .map { it.trim() } .filter { it.isNotEmpty() } - .map { FqName(it) } + .map { it } .toList() val KonanLibrary.includedHeaders diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt similarity index 100% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt similarity index 69% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt index da2c0a3b874..ebae056a999 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt @@ -3,14 +3,14 @@ package org.jetbrains.kotlin.konan.library import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.file.file import org.jetbrains.kotlin.konan.file.withMutableZipFileSystem -import org.jetbrains.kotlin.konan.file.withZipFileSystem import org.jetbrains.kotlin.konan.library.impl.DefaultMetadataReaderImpl import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryChecks -import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolver -import org.jetbrains.kotlin.konan.library.resolver.impl.KonanLibraryResolverImpl import org.jetbrains.kotlin.konan.target.KonanTarget +val KLIB_FILE_EXTENSION = "klib" +val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION" + fun File.unpackZippedKonanLibraryTo(newDir: File) { // First, run validity checks for the given KLIB file. @@ -29,13 +29,18 @@ fun File.unpackZippedKonanLibraryTo(newDir: File) { check(newDir.exists) { "Could not unpack $this as $newDir." } } +val List.toUnresolvedLibraries + get() = this.map { + + val version = it.substringAfterLast('@', "") + .let { if (it.isEmpty()) null else it} + val name = it.substringBeforeLast('@') + UnresolvedLibrary(name, version) + } + fun createKonanLibrary( libraryFile: File, - currentAbiVersion: Int, target: KonanTarget? = null, isDefault: Boolean = false, metadataReader: MetadataReader = DefaultMetadataReaderImpl -): KonanLibrary = KonanLibraryImpl(libraryFile, currentAbiVersion, target, isDefault, metadataReader) - -fun SearchPathResolverWithTarget.libraryResolver(abiVersion: Int): KonanLibraryResolver = - KonanLibraryResolverImpl(this, abiVersion) +): KonanLibrary = KonanLibraryImpl(libraryFile, target, isDefault, metadataReader) diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryVersioning.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryVersioning.kt new file mode 100644 index 00000000000..d1947daab66 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryVersioning.kt @@ -0,0 +1,28 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.konan.parseKonanVersion +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.parseKonanAbiVersion + + +data class KonanLibraryVersioning( + val libraryVersion: String?, + val compilerVersion: KonanVersion, + val abiVersion: KonanAbiVersion +) + +fun Properties.writeKonanLibraryVersioning(versions: KonanLibraryVersioning) { + this.setProperty(KLIB_PROPERTY_ABI_VERSION, versions.abiVersion.toString()) + versions.libraryVersion ?. let { this.setProperty(KLIB_PROPERTY_LIBRARY_VERSION, it) } + this.setProperty(KLIB_PROPERTY_COMPILER_VERSION, "${versions.compilerVersion}") +} + +fun Properties.readKonanLibraryVersioning(): KonanLibraryVersioning { + val abiVersion = this.getProperty(KLIB_PROPERTY_ABI_VERSION)!!.parseKonanAbiVersion() + val libraryVersion = this.getProperty(KLIB_PROPERTY_LIBRARY_VERSION) + val compilerVersion = this.getProperty(KLIB_PROPERTY_COMPILER_VERSION)!!.parseKonanVersion() + + return KonanLibraryVersioning(abiVersion = abiVersion, libraryVersion = libraryVersion, compilerVersion = compilerVersion) +} \ No newline at end of file diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt similarity index 100% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt new file mode 100644 index 00000000000..4fe0f171c8d --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt @@ -0,0 +1,200 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl +import org.jetbrains.kotlin.konan.target.Distribution +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.util.* + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the constants below: +const val KONAN_STDLIB_NAME = "stdlib" + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! +interface SearchPathResolver : WithLogger { + val searchRoots: List + fun resolutionSequence(givenPath: String): Sequence + fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): KonanLibraryImpl + fun resolve(givenPath: String): KonanLibraryImpl + fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List +} + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! +interface SearchPathResolverWithTarget: SearchPathResolver { + val target: KonanTarget + val knownAbiVersions: List? + val knownCompilerVersions: List? +} + +fun defaultResolver( + repositories: List, + target: KonanTarget +): SearchPathResolverWithTarget = defaultResolver(repositories, target, Distribution()) + +fun defaultResolver( + repositories: List, + target: KonanTarget, + distribution: Distribution, + logger: Logger = ::dummyLogger, + skipCurrentDir: Boolean = false +): SearchPathResolverWithTarget = KonanLibraryProperResolver( + repositories, + target, + listOf(KonanAbiVersion.CURRENT), + listOf(KonanVersion.CURRENT), + distribution.klib, + distribution.localKonanDir.absolutePath, + skipCurrentDir, + logger) + +fun resolverByName( + repositories: List, + distributionKlib: String? = null, + localKonanDir: String? = null, + skipCurrentDir: Boolean = false +): SearchPathResolver = KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir) + +internal open class KonanLibrarySearchPathResolver( + repositories: List, + val distributionKlib: String?, + val localKonanDir: String?, + val skipCurrentDir: Boolean, + override val logger:Logger = ::dummyLogger +) : SearchPathResolver { + + val localHead: File? + get() = localKonanDir?.File()?.klib + + val distHead: File? + get() = distributionKlib?.File()?.child("common") + + open val distPlatformHead: File? = null + + val currentDirHead: File? + get() = if (!skipCurrentDir) File.userDir else null + + private val repoRoots: List by lazy { repositories.map { File(it) } } + + // This is the place where we specify the order of library search. + override val searchRoots: List by lazy { + (listOf(currentDirHead) + repoRoots + listOf(localHead, distHead, distPlatformHead)).filterNotNull() + } + + private fun found(candidate: File): File? { + fun check(file: File): Boolean = + file.exists && (file.isFile || File(file, "manifest").exists) + + val noSuffix = File(candidate.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT)) + val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT)) + return when { + check(withSuffix) -> withSuffix + check(noSuffix) -> noSuffix + else -> null + } + } + + override fun resolutionSequence(givenPath: String): Sequence { + val given = File(givenPath) + val sequence = if (given.isAbsolute) { + sequenceOf(found(given)) + } else { + searchRoots.asSequence().map { + found(File(it, givenPath)) + } + } + return sequence.filterNotNull() + } + + override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): KonanLibraryImpl { + val givenPath = unresolved.path + return resolutionSequence(givenPath).firstOrNull() ?. let { + createKonanLibrary(it, null, isDefaultLink) as KonanLibraryImpl + } ?: error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.") + } + + override fun resolve(givenPath: String) = resolve(UnresolvedLibrary(givenPath, null), false) + + + private val File.klib + get() = File(this, "klib") + + // The libraries from the default root are linked automatically. + val defaultRoots: List + get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists } + + override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List { + + val result = mutableListOf() + + if (!noStdLib) { + result.add(resolve(UnresolvedLibrary(KONAN_STDLIB_NAME, null), true)) + } + + if (!noDefaultLibs) { + val defaultLibs = defaultRoots.flatMap { it.listFiles } + .filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME } + .map { UnresolvedLibrary(it.absolutePath, null) } + .map {resolve(it, isDefaultLink = true) } + result.addAll(defaultLibs) + } + + return result + } +} + +internal class KonanLibraryProperResolver( + repositories: List, + override val target: KonanTarget, + override val knownAbiVersions: List?, + override val knownCompilerVersions: List?, + distributionKlib: String?, + localKonanDir: String?, + skipCurrentDir: Boolean, + override val logger: Logger = ::dummyLogger +): KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir, logger), SearchPathResolverWithTarget { + + override val distPlatformHead: File? + get() = distributionKlib?.File()?.child("platform")?.child(target.visibleName) + + override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): KonanLibraryImpl { + val givenPath = unresolved.path + val fileSequence = resolutionSequence(givenPath) + val matching = fileSequence.map { createKonanLibrary(it, target, isDefaultLink) as KonanLibraryImpl } + .map { it.takeIf { libraryMatch(it, unresolved) } } + .filterNotNull() + + return matching.firstOrNull() ?: error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.") + } +} + +internal fun SearchPathResolverWithTarget.libraryMatch(candidate: KonanLibraryImpl, unresolved: UnresolvedLibrary): Boolean { + val resolverTarget = this.target + val candidatePath = candidate.libraryFile.absolutePath + + if (resolverTarget != null && !candidate.targetList.contains(resolverTarget.visibleName)) { + logger("skipping $candidatePath. The target doesn't match. Expected '$resolverTarget', found ${candidate.targetList}") + return false + } + + if (knownCompilerVersions != null && + !knownCompilerVersions!!.contains(candidate.versions.compilerVersion)) { + logger("skipping $candidatePath. The compiler versions don't match. Expected '${knownCompilerVersions}', found '${candidate.versions.compilerVersion}'") + return false + } + + if (knownAbiVersions != null && + !knownAbiVersions!!.contains(candidate.versions.abiVersion)) { + logger("skipping $candidatePath. The abi versions don't match. Expected '${knownAbiVersions}', found '${candidate.versions.abiVersion}'") + return false + } + + if (candidate.versions.libraryVersion != unresolved.libraryVersion && + candidate.versions.libraryVersion != null && + unresolved.libraryVersion != null) { + logger("skipping $candidatePath. The library versions don't match. Expected '${unresolved.libraryVersion}', found '${candidate.versions.libraryVersion}'") + return false + } + + return true +} diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/UnresolvedLibrary.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/UnresolvedLibrary.kt new file mode 100644 index 00000000000..d3ef4766f18 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/UnresolvedLibrary.kt @@ -0,0 +1,10 @@ +package org.jetbrains.kotlin.konan.library + +data class UnresolvedLibrary( + val path: String, + val libraryVersion: String?) { + + fun substitutePath(newPath: String): UnresolvedLibrary { + return UnresolvedLibrary(newPath, libraryVersion) + } +} \ No newline at end of file diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt similarity index 100% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt similarity index 83% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt index 00146b1c38c..ce5512f9692 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt @@ -9,9 +9,9 @@ import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.util.defaultTargetSubstitutions import org.jetbrains.kotlin.konan.util.substitute -internal class KonanLibraryImpl( + +class KonanLibraryImpl( override val libraryFile: File, - private val currentAbiVersion: Int, internal val target: KonanTarget?, override val isDefault: Boolean, private val metadataReader: MetadataReader @@ -32,14 +32,8 @@ internal class KonanLibraryImpl( properties } - override val abiVersion: String - get() { - val manifestAbiVersion = manifestProperties.getProperty(KLIB_PROPERTY_ABI_VERSION) - check(currentAbiVersion.toString() == manifestAbiVersion) { - "ABI version mismatch. Compiler expects: $currentAbiVersion, the library is $manifestAbiVersion" - } - return manifestAbiVersion - } + override val versions: KonanLibraryVersioning + get() = manifestProperties.readKonanLibraryVersioning() override val linkerOpts: List get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName) diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt similarity index 100% rename from extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt rename to shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/KonanAbiVersion.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/KonanAbiVersion.kt new file mode 100644 index 00000000000..96df516945e --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/KonanAbiVersion.kt @@ -0,0 +1,12 @@ +package org.jetbrains.kotlin.konan + +fun String.parseKonanAbiVersion(): KonanAbiVersion { + return KonanAbiVersion(this.toInt()) +} + +data class KonanAbiVersion(val version: Int) { + companion object { + val CURRENT = KonanAbiVersion(1) + } + override fun toString() = "$version" +} \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt index b54534386fa..384d8264f53 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.konan.file // FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! +import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent import java.io.BufferedReader import java.io.InputStream import java.io.InputStreamReader @@ -38,7 +39,7 @@ data class File constructor(internal val javaPath: Path) { val absoluteFile: File get() = File(absolutePath) val name: String - get() = javaPath.fileName.toString() + get() = javaPath.fileName.toString().removeSuffixIfPresent("/") // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8153248 val extension: String get() = name.substringAfterLast('.', "") val parent: String diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt deleted file mode 100644 index f1308d36775..00000000000 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt +++ /dev/null @@ -1,138 +0,0 @@ -package org.jetbrains.kotlin.konan.library - -import org.jetbrains.kotlin.konan.file.File -import org.jetbrains.kotlin.konan.target.Distribution -import org.jetbrains.kotlin.konan.target.KonanTarget -import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent -import org.jetbrains.kotlin.konan.util.suffixIfNot - -// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the constants below: -const val KLIB_FILE_EXTENSION = "klib" -const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION" - -const val KONAN_STDLIB_NAME = "stdlib" - -// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! -interface SearchPathResolver { - val searchRoots: List - fun resolve(givenPath: String): File - fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List -} - -// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! -interface SearchPathResolverWithTarget: SearchPathResolver { - val target: KonanTarget -} - -fun defaultResolver( - repositories: List, - target: KonanTarget -): SearchPathResolverWithTarget = defaultResolver(repositories, target, Distribution()) - -fun defaultResolver( - repositories: List, - target: KonanTarget, - distribution: Distribution, - skipCurrentDir: Boolean = false -): SearchPathResolverWithTarget = KonanLibrarySearchPathResolverWithTarget( - repositories, - target, - distribution.klib, - distribution.localKonanDir.absolutePath, - skipCurrentDir) - -fun noTargetResolver( - repositories: List, - distributionKlib: String? = null, - localKonanDir: String? = null, - skipCurrentDir: Boolean = false -): SearchPathResolver = KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir) - - -internal open class KonanLibrarySearchPathResolver( - repositories: List, - val distributionKlib: String?, - val localKonanDir: String?, - val skipCurrentDir: Boolean -) : SearchPathResolver { - - val localHead: File? - get() = localKonanDir?.File()?.klib - - val distHead: File? - get() = distributionKlib?.File()?.child("common") - - open val distPlatformHead: File? = null - - val currentDirHead: File? - get() = if (!skipCurrentDir) File.userDir else null - - private val repoRoots: List by lazy { repositories.map { File(it) } } - - // This is the place where we specify the order of library search. - override val searchRoots: List by lazy { - (listOf(currentDirHead) + repoRoots + listOf(localHead, distHead, distPlatformHead)).filterNotNull() - } - - private fun found(candidate: File): File? { - fun check(file: File): Boolean = - file.exists && (file.isFile || File(file, "manifest").exists) - - val noSuffix = File(candidate.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT)) - val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT)) - return when { - check(withSuffix) -> withSuffix - check(noSuffix) -> noSuffix - else -> null - } - } - - override fun resolve(givenPath: String): File { - val given = File(givenPath) - if (given.isAbsolute) { - found(given)?.apply { return this } - } else { - searchRoots.forEach { - found(File(it, givenPath))?.apply { return this } - } - } - error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.") - } - - private val File.klib - get() = File(this, "klib") - - // The libraries from the default root are linked automatically. - val defaultRoots: List - get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists } - - override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List { - - val result = mutableListOf() - - if (!noStdLib) { - result.add(resolve(KONAN_STDLIB_NAME)) - } - - if (!noDefaultLibs) { - val defaultLibs = defaultRoots.flatMap { it.listFiles } - .filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME } - .map { File(it.absolutePath) } - result.addAll(defaultLibs) - } - - return result - } -} - -internal class KonanLibrarySearchPathResolverWithTarget( - repositories: List, - override val target: KonanTarget, - distributionKlib: String?, - localKonanDir: String?, - skipCurrentDir: Boolean -): KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir), SearchPathResolverWithTarget { - - override val distPlatformHead: File? - get() = distributionKlib?.File()?.child("platform")?.child(target.visibleName) -} \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/WithLogger.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/WithLogger.kt new file mode 100644 index 00000000000..6a0908b8061 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/WithLogger.kt @@ -0,0 +1,10 @@ +package org.jetbrains.kotlin.konan.util + +typealias Logger = (String) -> Unit + +interface WithLogger { + val logger: Logger +} + +fun dummyLogger(message: String) {} + diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt index 11e74ee2d20..8206b6c129f 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt @@ -154,6 +154,6 @@ open class KonanLibrariesSpec(val task: KonanArtifactWithLibrariesTask, val proj files.flatMapTo(this) { it.files } addAll(artifactFiles) addAll(task.platformConfiguration.files) - namedKlibs.mapTo(this) { project.file(resolver.resolve(it).absolutePath) } + namedKlibs.mapTo(this) { project.file(resolver.resolve(it).libraryFile.absolutePath) } } } diff --git a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt index 5798f6584c1..5ca1a6a76be 100644 --- a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt +++ b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt @@ -4,13 +4,10 @@ */ package org.jetbrains.kotlin.cli.utilities -import org.jetbrains.kotlin.backend.konan.library.KLIB_CURRENT_ABI_VERSION import org.jetbrains.kotlin.konan.file.File -import org.jetbrains.kotlin.konan.library.defaultResolver -import org.jetbrains.kotlin.konan.library.includedHeaders -import org.jetbrains.kotlin.konan.library.libraryResolver -import org.jetbrains.kotlin.konan.library.packageFqName import org.jetbrains.kotlin.konan.target.PlatformManager +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.library.* import org.jetbrains.kotlin.native.interop.gen.jvm.interop import org.jetbrains.kotlin.utils.addIfNotNull @@ -57,14 +54,14 @@ fun invokeInterop(flavor: String, args: Array): Array { val manifest = File(buildDir, "manifest.properties") val target = PlatformManager().targetManager(targetRequest).target - val resolver = defaultResolver(repos, target).libraryResolver(KLIB_CURRENT_ABI_VERSION) + val resolver = defaultResolver(repos, target).libraryResolver() val allLibraries = resolver.resolveWithDependencies( - libraries, noStdLib = true, noDefaultLibs = noDefaultLibs + libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs ).getFullList() val importArgs = allLibraries.flatMap { library -> // TODO: handle missing properties? - library.packageFqName?.asString()?.let { packageFqName -> + library.packageFqName?.let { packageFqName -> val headerIds = library.includedHeaders val arg = "$packageFqName:${headerIds.joinToString(";")}" listOf("-import", arg)