Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support KLIB for Common platform #2441

Merged
merged 3 commits into from Apr 18, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -23,7 +23,6 @@ class Multiplatform0GradleIntegrationTest(override val versions: BuildVersions)
}

@Test
@Ignore("KLIB is currently not supported, planned for 1.6.21")
fun execute() {
val result = createGradleRunner("dokkaHtml", "-i", "-s").buildRelaxed()

Expand Down
Expand Up @@ -20,6 +20,7 @@ import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.analysis.resolve.*
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
Expand Down Expand Up @@ -50,6 +51,7 @@ import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
Expand Down Expand Up @@ -184,7 +186,15 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
Platform.jvm -> JvmPlatforms.defaultJvmPlatform
}

val nativeLibraries: Map<AbsolutePathString, LibraryModuleInfo> = loadNativeLibraries()
val kotlinLibraries: Map<AbsolutePathString, KotlinLibrary> = resolveKotlinLibraries()

val commonDependencyContainer = if (analysisPlatform == Platform.common) DokkaKlibMetadataCommonDependencyContainer(
kotlinLibraries.values.toList(),
environment.configuration,
projectContext.storageManager
) else null

val extraModuleDependencies = kotlinLibraries.values.registerLibraries() + commonDependencyContainer?.moduleInfos.orEmpty()

val library = object : LibraryModuleInfo {
override val analyzerServices: PlatformDependentAnalyzerServices =
Expand All @@ -194,15 +204,16 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
override fun dependencies(): List<ModuleInfo> = listOf(this)
override fun getLibraryRoots(): Collection<String> = classpath
.map { libraryFile -> libraryFile.absolutePath }
.filter { path -> path !in nativeLibraries }
.filter { path -> path !in kotlinLibraries }
}

val module = object : ModuleInfo {
override val analyzerServices: PlatformDependentAnalyzerServices =
analysisPlatform.analyzerServices()
override val name: Name = Name.special("<module>")
override val platform: TargetPlatform = targetPlatform
override fun dependencies(): List<ModuleInfo> = listOf(this, library) + nativeLibraries.values
override fun dependencies(): List<ModuleInfo> =
listOf(this, library) + extraModuleDependencies
}

val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles)
Expand All @@ -211,10 +222,11 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
module -> ModuleContent(it, emptyList(), GlobalSearchScope.allScope(environment.project))
is DokkaKlibLibraryInfo -> {
if (it.libraryRoot in nativeLibraries)
if (it.libraryRoot in kotlinLibraries)
ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
else null
}
is CommonKlibModuleInfo -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
else -> null
} ?: throw IllegalArgumentException("Unexpected module info")
}
Expand All @@ -240,7 +252,8 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
projectContext,
module,
modulesContent,
environment
environment,
commonDependencyContainer
)
Platform.js -> createJsResolverForProject(projectContext, module, modulesContent)
Platform.native -> createNativeResolverForProject(projectContext, module, modulesContent)
Expand Down Expand Up @@ -282,13 +295,23 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
Platform.jvm -> JvmPlatformAnalyzerServices
}

@OptIn(ExperimentalStdlibApi::class)
private fun loadNativeLibraries(): Map<AbsolutePathString, LibraryModuleInfo> {
if (analysisPlatform != Platform.native && analysisPlatform != Platform.js) return emptyMap()

fun Collection<KotlinLibrary>.registerLibraries(): List<DokkaKlibLibraryInfo> {
if (analysisPlatform != Platform.native && analysisPlatform != Platform.js) return emptyList()
val dependencyResolver = DokkaKlibLibraryDependencyResolver()
val analyzerServices = analysisPlatform.analyzerServices()

return map { kotlinLibrary ->
if (analysisPlatform == org.jetbrains.dokka.Platform.native) DokkaNativeKlibLibraryInfo(
kotlinLibrary,
analyzerServices,
dependencyResolver
)
else DokkaJsKlibLibraryInfo(kotlinLibrary, analyzerServices, dependencyResolver)
}
}

@OptIn(ExperimentalStdlibApi::class)
private fun resolveKotlinLibraries(): Map<AbsolutePathString, KotlinLibrary> {
return buildMap {
classpath
.filter { it.isDirectory || (it.extension == "jar" || it.extension == KLIB_FILE_EXTENSION) }
Expand All @@ -303,12 +326,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
// exists, is KLIB, has compatible format
put(
libraryFile.absolutePath,
if (analysisPlatform == Platform.native) DokkaNativeKlibLibraryInfo(
kotlinLibrary,
analyzerServices,
dependencyResolver
)
else DokkaJsKlibLibraryInfo(kotlinLibrary, analyzerServices, dependencyResolver)
kotlinLibrary
)
}
} catch (e: Throwable) {
Expand All @@ -323,7 +341,8 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
projectContext: ProjectContext,
module: ModuleInfo,
modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo>,
environment: KotlinCoreEnvironment
environment: KotlinCoreEnvironment,
dependencyContainer: CommonDependenciesContainer?
): ResolverForProject<ModuleInfo> {
return object : AbstractResolverForProject<ModuleInfo>(
"Dokka",
Expand All @@ -344,7 +363,8 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
},
CompilerEnvironment,
unspecifiedJvmPlatform,
true
true,
dependencyContainer
).createResolverForModule(
descriptor as ModuleDescriptorImpl,
projectContext.withModule(descriptor),
Expand Down
@@ -0,0 +1,25 @@
package org.jetbrains.dokka.analysis.resolve

import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices

internal class CommonKlibModuleInfo(
override val name: Name,
val kotlinLibrary: KotlinLibrary,
private val dependOnModules: List<ModuleInfo>
) : ModuleInfo {
override fun dependencies(): List<ModuleInfo> = dependOnModules

override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns = ModuleInfo.DependencyOnBuiltIns.LAST

override val platform: TargetPlatform
get() = CommonPlatforms.defaultCommonPlatform

override val analyzerServices: PlatformDependentAnalyzerServices
get() = CommonPlatformAnalyzerServices
}
@@ -0,0 +1,140 @@
package org.jetbrains.dokka.analysis.resolve

import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.metadata.NativeTypeTransformer
import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescriptorFactoryImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.keysToMap

/**
* Adapted from org.jetbrains.kotlin.cli.metadata.KlibMetadataDependencyContainer
*/
class DokkaKlibMetadataCommonDependencyContainer(
kotlinLibraries: List<KotlinLibrary>,
private val configuration: CompilerConfiguration,
private val storageManager: StorageManager
) : CommonDependenciesContainer {

private val builtIns
get() = DefaultBuiltIns.Instance

private val mutableDependenciesForAllModuleDescriptors = mutableListOf<ModuleDescriptorImpl>().apply {
add(builtIns.builtInsModule)
}

private val mutableDependenciesForAllModules = mutableListOf<ModuleInfo>()

private val moduleDescriptorsForKotlinLibraries: Map<KotlinLibrary, ModuleDescriptorImpl> =
kotlinLibraries.keysToMap { library ->
val moduleHeader = parseModuleHeader(library.moduleHeaderData)
val moduleName = Name.special(moduleHeader.moduleName)
val moduleOrigin = DeserializedKlibModuleOrigin(library)
MetadataFactories.DefaultDescriptorFactory.createDescriptor(
moduleName, storageManager, builtIns, moduleOrigin
)
}.also { result ->
val resultValues = result.values
resultValues.forEach { module ->
module.setDependencies(mutableDependenciesForAllModuleDescriptors)
}
mutableDependenciesForAllModuleDescriptors.addAll(resultValues)
}

private val moduleInfosImpl: List<CommonKlibModuleInfo> = mutableListOf<CommonKlibModuleInfo>().apply {
addAll(
moduleDescriptorsForKotlinLibraries.map { (kotlinLibrary, moduleDescriptor) ->
CommonKlibModuleInfo(moduleDescriptor.name, kotlinLibrary, mutableDependenciesForAllModules)
}
)
mutableDependenciesForAllModules.addAll(this@apply)
}

override val moduleInfos: List<ModuleInfo> get() = moduleInfosImpl

/* not used in Dokka */
override val friendModuleInfos: List<ModuleInfo> = emptyList()

/* not used in Dokka */
override val refinesModuleInfos: List<ModuleInfo> = emptyList()

override fun moduleDescriptorForModuleInfo(moduleInfo: ModuleInfo): ModuleDescriptor {
if (moduleInfo !in moduleInfos)
error("Unknown module info $moduleInfo")

// Ensure that the package fragment provider has been created and the module descriptor has been
// initialized with the package fragment provider:
packageFragmentProviderForModuleInfo(moduleInfo)

return moduleDescriptorsForKotlinLibraries.getValue((moduleInfo as CommonKlibModuleInfo).kotlinLibrary)
}

override fun registerDependencyForAllModules(
moduleInfo: ModuleInfo,
descriptorForModule: ModuleDescriptorImpl
) {
mutableDependenciesForAllModules.add(moduleInfo)
mutableDependenciesForAllModuleDescriptors.add(descriptorForModule)
}

override fun packageFragmentProviderForModuleInfo(
moduleInfo: ModuleInfo
): PackageFragmentProvider? {
if (moduleInfo !in moduleInfos)
return null
return packageFragmentProviderForKotlinLibrary((moduleInfo as CommonKlibModuleInfo).kotlinLibrary)
}

private val klibMetadataModuleDescriptorFactory by lazy {
KlibMetadataModuleDescriptorFactoryImpl(
MetadataFactories.DefaultDescriptorFactory,
MetadataFactories.DefaultPackageFragmentsFactory,
MetadataFactories.flexibleTypeDeserializer,
MetadataFactories.platformDependentTypeTransformer
)
}

private fun packageFragmentProviderForKotlinLibrary(
library: KotlinLibrary
): PackageFragmentProvider {
val languageVersionSettings = configuration.languageVersionSettings

val libraryModuleDescriptor = moduleDescriptorsForKotlinLibraries.getValue(library)
val packageFragmentNames = parseModuleHeader(library.moduleHeaderData).packageFragmentNameList

return klibMetadataModuleDescriptorFactory.createPackageFragmentProvider(
library,
packageAccessHandler = null,
packageFragmentNames = packageFragmentNames,
storageManager = LockBasedStorageManager("KlibMetadataPackageFragmentProvider"),
moduleDescriptor = libraryModuleDescriptor,
configuration = CompilerDeserializationConfiguration(languageVersionSettings),
compositePackageFragmentAddend = null,
lookupTracker = LookupTracker.DO_NOTHING
).also {
libraryModuleDescriptor.initialize(it)
}
}
}

private val MetadataFactories =
KlibMetadataFactories(
{ DefaultBuiltIns.Instance },
NullFlexibleTypeDeserializer,
NativeTypeTransformer()
)