Skip to content

Commit b855a0b

Browse files
authored
Support for providing sourceRoots as relative paths (#3362)
* Use `Path::toRealPath` to resolve symlinks and `..` in descriptors * Use `analysisContext.modulesWithFiles` in symbols
1 parent bb036a3 commit b855a0b

File tree

4 files changed

+72
-25
lines changed

4 files changed

+72
-25
lines changed

dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,4 +374,42 @@ class CliIntegrationTest : AbstractCliIntegrationTest() {
374374

375375
assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory")
376376
}
377+
378+
@Test
379+
fun `relative paths in configuraiton should work`() {
380+
val resourcePath =
381+
javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!")
382+
val jsonPath = File(resourcePath)
383+
384+
val dokkaOutputDir = File(projectDir, "output-relative")
385+
assertTrue(dokkaOutputDir.mkdirs())
386+
jsonPath.writeText(
387+
jsonBuilder(
388+
outputPath = dokkaOutputDir.invariantSeparatorsPath,
389+
pluginsClasspath = basePluginJarFile.absoluteFile.invariantSeparatorsPath,
390+
projectPath = "src", // relative path
391+
)
392+
)
393+
394+
ProcessBuilder(
395+
"java", "-jar", cliJarFile.absolutePath, jsonPath.absolutePath
396+
).directory(projectDir).redirectErrorStream(true).start().also { process ->
397+
val result = process.awaitProcessResult()
398+
assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)")
399+
}
400+
401+
assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory")
402+
403+
val htmlFiles = dokkaOutputDir.allHtmlFiles().map { it.relativeTo(dokkaOutputDir).path }.toList()
404+
405+
// check that both Kotlin and Java sources are processed
406+
407+
// kotlin:
408+
assertContains(htmlFiles, "-dokka -example/it.basic/index.html")
409+
assertContains(htmlFiles, "-dokka -example/it.basic/-public-class/public-documented-function.html")
410+
411+
// java:
412+
assertContains(htmlFiles, "-dokka -example/it.basic.java/index.html")
413+
assertContains(htmlFiles, "-dokka -example/it.basic.java/-sample-java-class/public-documented-function.html")
414+
}
377415
}

dokka-subprojects/analysis-kotlin-descriptors-compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,14 @@ private class DokkaDescriptorVisitor(
168168
private val syntheticDocProvider = SyntheticDescriptorDocumentationProvider(kDocFinder, sourceSet)
169169

170170
private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter {
171-
it.toSourceElement.containingFile.toString().let { path ->
172-
path.isNotBlank() && sourceSet.sourceRoots.any { root ->
173-
Paths.get(path).startsWith(root.toPath())
171+
val pathString = it.toSourceElement.containingFile.toString()
172+
when {
173+
pathString.isBlank() -> false
174+
else -> {
175+
val absolutePath = Paths.get(pathString).toRealPath()
176+
sourceSet.sourceRoots.any { root ->
177+
absolutePath.startsWith(root.toPath().toRealPath())
178+
}
174179
}
175180
}
176181
}

dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import org.jetbrains.kotlin.name.FqName
4646
import org.jetbrains.kotlin.psi.*
4747
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
4848
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
49-
import java.nio.file.Paths
5049

5150
internal class DefaultSymbolToDocumentableTranslator(context: DokkaContext) : AsyncSourceToDocumentableTranslator {
5251
private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
@@ -111,46 +110,46 @@ internal class DokkaSymbolVisitor(
111110
private val KtDeclarationSymbol.isExpect
112111
get() = (psi as? KtModifierListOwner)?.hasExpectModifier() == true
113112

114-
private fun <T : KtSymbol> Collection<T>.filterSymbolsInSourceSet() = filter {
115-
it.psi?.containingFile?.virtualFile?.path?.let { path ->
116-
path.isNotBlank() && sourceSet.sourceRoots.any { root ->
117-
Paths.get(path).startsWith(root.toPath())
118-
}
119-
} == true
113+
private fun <T : KtSymbol> List<T>.filterSymbolsInSourceSet(moduleFiles: Set<KtFile>): List<T> = filter {
114+
when (val file = it.psi?.containingFile) {
115+
is KtFile -> moduleFiles.contains(file)
116+
else -> false
117+
}
120118
}
121119

122120
fun visitModule(): DModule {
123121
val sourceModule = analysisContext.getModule(sourceSet)
124-
val ktFiles = analysisContext.modulesWithFiles[sourceModule]?.filterIsInstance<KtFile>() ?: throw IllegalStateException("No source files for a source module ${sourceModule.moduleName} of source set ${sourceSet.sourceSetID}")
122+
val ktFiles = analysisContext.modulesWithFiles[sourceModule]?.filterIsInstance<KtFile>()?.toSet()
123+
?: throw IllegalStateException("No source files for a source module ${sourceModule.moduleName} of source set ${sourceSet.sourceSetID}")
125124
val processedPackages: MutableSet<FqName> = mutableSetOf()
126125
return analyze(sourceModule) {
127-
val packageSymbols: List<DPackage> = ktFiles
128-
.mapNotNull {
129-
if (processedPackages.contains(it.packageFqName))
130-
return@mapNotNull null
131-
processedPackages.add(it.packageFqName)
132-
getPackageSymbolIfPackageExists(it.packageFqName)?.let { it1 ->
133-
visitPackageSymbol(
134-
it1
135-
)
136-
}
126+
val packages = ktFiles.mapNotNull { file ->
127+
if (processedPackages.contains(file.packageFqName))
128+
return@mapNotNull null
129+
processedPackages.add(file.packageFqName)
130+
getPackageSymbolIfPackageExists(file.packageFqName)?.let { packageSymbol ->
131+
visitPackageSymbol(packageSymbol, ktFiles)
137132
}
133+
}
138134

139135
DModule(
140136
name = moduleName,
141-
packages = packageSymbols,
137+
packages = packages,
142138
documentation = emptyMap(),
143139
expectPresentInSet = null,
144140
sourceSets = setOf(sourceSet)
145141
)
146142
}
147143
}
148144

149-
private fun KtAnalysisSession.visitPackageSymbol(packageSymbol: KtPackageSymbol): DPackage {
145+
private fun KtAnalysisSession.visitPackageSymbol(
146+
packageSymbol: KtPackageSymbol,
147+
moduleFiles: Set<KtFile>
148+
): DPackage {
150149
val dri = getDRIFromPackage(packageSymbol)
151150
val scope = packageSymbol.getPackageScope()
152-
val callables = scope.getCallableSymbols().toList().filterSymbolsInSourceSet()
153-
val classifiers = scope.getClassifierSymbols().toList().filterSymbolsInSourceSet()
151+
val callables = scope.getCallableSymbols().toList().filterSymbolsInSourceSet(moduleFiles)
152+
val classifiers = scope.getClassifierSymbols().toList().filterSymbolsInSourceSet(moduleFiles)
154153

155154
val functions = callables.filterIsInstance<KtFunctionSymbol>().map { visitFunctionSymbol(it, dri) }
156155
val properties = callables.filterIsInstance<KtPropertySymbol>().map { visitPropertySymbol(it, dri) }

dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ class ContentForInheritorsTest : BaseAbstractTest() {
258258
|
259259
|class Child : Parent()
260260
|
261+
|/src/linuxX64Main/kotlin/pageMerger/Test.kt
262+
|package pageMerger
263+
|
264+
|class Child : Parent()
265+
|
261266
""".trimMargin(),
262267
mppTestConfiguration
263268
) {

0 commit comments

Comments
 (0)